11'use client' ;
22
3- import dynamic from 'next/dynamic' ;
43import axios from 'axios' ;
54import { useEffect , useState , useCallback , useRef , useMemo } from 'react' ;
6- import Hero from '../../../src/ui/Hero' ;
7- import Loading from '../../components/Loading' ;
8- import {
9- forceSimulation ,
10- forceCollide ,
11- forceManyBody ,
12- forceCenter ,
13- forceLink ,
14- } from 'd3-force' ;
5+ import { forceCollide , forceManyBody } from 'd3-force' ;
156import ForceGraph2D from 'react-force-graph-2d' ;
167
17- // const ForceGraph2D = dynamic(() => import('react-force-graph-2d'), {
18- // ssr: false,
19- // });
20-
21- const DEFAULT_WIDTH = 800 ;
22- const DEFAULT_HEIGHT = 600 ;
23-
248// Format skills array into a readable string
259const formatSkills = ( skills ) => {
2610 if ( ! skills ) return '' ;
@@ -98,7 +82,6 @@ export default function Jobs({ params }) {
9882
9983 const [ dimensions , setDimensions ] = useState ( { width : 0 , height : 0 } ) ;
10084 const graphRef = useRef ( ) ;
101- const canvasRef = useRef ( null ) ;
10285 const [ activeNode , setActiveNode ] = useState ( null ) ;
10386 const [ hoveredNode , setHoveredNode ] = useState ( null ) ;
10487 const [ pinnedNode , setPinnedNode ] = useState ( null ) ;
@@ -115,48 +98,51 @@ export default function Jobs({ params }) {
11598 const [ filteredNodes , setFilteredNodes ] = useState ( new Set ( ) ) ;
11699
117100 const [ showSalaryGradient , setShowSalaryGradient ] = useState ( false ) ;
118- const [ salaryRange , setSalaryRange ] = useState ( { min : Infinity , max : - Infinity } ) ;
101+ const [ salaryRange , setSalaryRange ] = useState ( {
102+ min : Infinity ,
103+ max : - Infinity ,
104+ } ) ;
119105
120106 // Parse salary from various string formats
121107 const parseSalary = useCallback ( ( salary ) => {
122108 if ( ! salary ) return null ;
123109 if ( typeof salary === 'number' ) return salary ;
124-
110+
125111 const str = salary . toString ( ) . toLowerCase ( ) ;
126112 // Extract all numbers from the string
127113 const numbers = str . match ( / \d + (?: \. \d + ) ? / g) ;
128114 if ( ! numbers ) return null ;
129-
115+
130116 // Convert numbers considering k/K multiplier
131- const values = numbers . map ( num => {
117+ const values = numbers . map ( ( num ) => {
132118 const multiplier = str . includes ( 'k' ) ? 1000 : 1 ;
133119 return parseFloat ( num ) * multiplier ;
134120 } ) ;
135-
121+
136122 // If range, return average
137123 if ( values . length > 1 ) {
138124 values . sort ( ( a , b ) => a - b ) ;
139125 return ( values [ 0 ] + values [ values . length - 1 ] ) / 2 ;
140126 }
141-
127+
142128 return values [ 0 ] ;
143129 } , [ ] ) ;
144130
145131 // Calculate salary range when jobs data changes
146132 useEffect ( ( ) => {
147133 if ( ! jobInfo ) return ;
148-
134+
149135 let min = Infinity ;
150136 let max = - Infinity ;
151-
152- Object . values ( jobInfo ) . forEach ( job => {
137+
138+ Object . values ( jobInfo ) . forEach ( ( job ) => {
153139 const salary = parseSalary ( job . salary ) ;
154140 if ( salary ) {
155141 min = Math . min ( min , salary ) ;
156142 max = Math . max ( max , salary ) ;
157143 }
158144 } ) ;
159-
145+
160146 if ( min !== Infinity && max !== - Infinity ) {
161147 setSalaryRange ( { min, max } ) ;
162148 }
@@ -220,19 +206,26 @@ export default function Jobs({ params }) {
220206 // Memoize node colors for salary view
221207 const nodeSalaryColors = useMemo ( ( ) => {
222208 if ( ! showSalaryGradient || ! jobInfo ) return new Map ( ) ;
223-
209+
224210 const colors = new Map ( ) ;
225211 Object . entries ( jobInfo ) . forEach ( ( [ id , job ] ) => {
226212 const salary = parseSalary ( job . salary ) ;
227213 if ( salary ) {
228- const percentage = ( salary - salaryRange . min ) / ( salaryRange . max - salaryRange . min ) ;
214+ const percentage =
215+ ( salary - salaryRange . min ) / ( salaryRange . max - salaryRange . min ) ;
229216 const lightBlue = [ 219 , 234 , 254 ] ; // bg-blue-100
230- const darkBlue = [ 30 , 64 , 175 ] ; // bg-blue-800
231-
232- const r = Math . round ( lightBlue [ 0 ] + ( darkBlue [ 0 ] - lightBlue [ 0 ] ) * percentage ) ;
233- const g = Math . round ( lightBlue [ 1 ] + ( darkBlue [ 1 ] - lightBlue [ 1 ] ) * percentage ) ;
234- const b = Math . round ( lightBlue [ 2 ] + ( darkBlue [ 2 ] - lightBlue [ 2 ] ) * percentage ) ;
235-
217+ const darkBlue = [ 30 , 64 , 175 ] ; // bg-blue-800
218+
219+ const r = Math . round (
220+ lightBlue [ 0 ] + ( darkBlue [ 0 ] - lightBlue [ 0 ] ) * percentage ,
221+ ) ;
222+ const g = Math . round (
223+ lightBlue [ 1 ] + ( darkBlue [ 1 ] - lightBlue [ 1 ] ) * percentage ,
224+ ) ;
225+ const b = Math . round (
226+ lightBlue [ 2 ] + ( darkBlue [ 2 ] - lightBlue [ 2 ] ) * percentage ,
227+ ) ;
228+
236229 colors . set ( id , `rgb(${ r } , ${ g } , ${ b } )` ) ;
237230 } else {
238231 colors . set ( id , '#e2e8f0' ) ; // Light gray for no salary
@@ -245,11 +238,11 @@ export default function Jobs({ params }) {
245238 ( node ) => {
246239 if ( node . group === - 1 ) return '#fff' ;
247240 if ( filterText && ! filteredNodes . has ( node . id ) ) return '#f8fafc' ;
248-
241+
249242 if ( showSalaryGradient ) {
250243 return nodeSalaryColors . get ( node . id ) || '#e2e8f0' ;
251244 }
252-
245+
253246 return readJobs . has ( node . id ) ? '#f1f5f9' : '#fef9c3' ;
254247 } ,
255248 [ readJobs , filterText , filteredNodes , showSalaryGradient , nodeSalaryColors ] ,
@@ -259,26 +252,41 @@ export default function Jobs({ params }) {
259252 ( node ) => {
260253 if ( node . group === - 1 ) return '#fff' ;
261254 if ( filterText && ! filteredNodes . has ( node . id ) ) return '#f8fafc' ;
262-
255+
263256 if ( showSalaryGradient && jobInfo [ node . id ] ) {
264257 const salary = parseSalary ( jobInfo [ node . id ] . salary ) ;
265258 if ( salary ) {
266- const percentage = ( salary - salaryRange . min ) / ( salaryRange . max - salaryRange . min ) ;
259+ const percentage =
260+ ( salary - salaryRange . min ) / ( salaryRange . max - salaryRange . min ) ;
267261 const lightBlue = [ 219 , 234 , 254 ] ; // bg-blue-100
268- const darkBlue = [ 30 , 64 , 175 ] ; // bg-blue-800
269-
270- const r = Math . round ( lightBlue [ 0 ] + ( darkBlue [ 0 ] - lightBlue [ 0 ] ) * percentage ) ;
271- const g = Math . round ( lightBlue [ 1 ] + ( darkBlue [ 1 ] - lightBlue [ 1 ] ) * percentage ) ;
272- const b = Math . round ( lightBlue [ 2 ] + ( darkBlue [ 2 ] - lightBlue [ 2 ] ) * percentage ) ;
273-
262+ const darkBlue = [ 30 , 64 , 175 ] ; // bg-blue-800
263+
264+ const r = Math . round (
265+ lightBlue [ 0 ] + ( darkBlue [ 0 ] - lightBlue [ 0 ] ) * percentage ,
266+ ) ;
267+ const g = Math . round (
268+ lightBlue [ 1 ] + ( darkBlue [ 1 ] - lightBlue [ 1 ] ) * percentage ,
269+ ) ;
270+ const b = Math . round (
271+ lightBlue [ 2 ] + ( darkBlue [ 2 ] - lightBlue [ 2 ] ) * percentage ,
272+ ) ;
273+
274274 return `rgb(${ r } , ${ g } , ${ b } )` ;
275275 }
276276 return '#e2e8f0' ; // Light gray for no salary
277277 }
278-
278+
279279 return readJobs . has ( node . id ) ? '#f1f5f9' : '#fef9c3' ;
280280 } ,
281- [ readJobs , filterText , filteredNodes , showSalaryGradient , jobInfo , parseSalary , salaryRange ] ,
281+ [
282+ readJobs ,
283+ filterText ,
284+ filteredNodes ,
285+ showSalaryGradient ,
286+ jobInfo ,
287+ parseSalary ,
288+ salaryRange ,
289+ ] ,
282290 ) ;
283291
284292 // Function to preload and cache image
@@ -453,7 +461,9 @@ export default function Jobs({ params }) {
453461 onChange = { ( e ) => setShowSalaryGradient ( e . target . checked ) }
454462 />
455463 < div className = "w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600" > </ div >
456- < span className = "ml-2 text-sm font-medium text-gray-900" > Salary View</ span >
464+ < span className = "ml-2 text-sm font-medium text-gray-900" >
465+ Salary View
466+ </ span >
457467 </ label >
458468 </ div >
459469 </ div >
0 commit comments