From 3da1faef69f74314d92288047796f74930871686 Mon Sep 17 00:00:00 2001 From: jongomez Date: Tue, 24 Oct 2023 12:49:54 +0100 Subject: [PATCH 1/7] feat: implement job list --- .../docusaurus-playground/src/pages/index.mdx | 2 + .../components/mdx/JobList/JobList.scss | 0 .../client/components/mdx/JobList/JobList.tsx | 53 +++++++++ .../client/components/mdx/JobList/index.ts | 1 + .../components/mdx/JobList/useFetchJobs.ts | 108 ++++++++++++++++++ .../src/client/components/mdx/index.ts | 1 + 6 files changed, 165 insertions(+) create mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobList/JobList.scss create mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobList/JobList.tsx create mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobList/index.ts create mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobList/useFetchJobs.ts diff --git a/packages/docusaurus-playground/src/pages/index.mdx b/packages/docusaurus-playground/src/pages/index.mdx index a3b3b249..0a107ce3 100644 --- a/packages/docusaurus-playground/src/pages/index.mdx +++ b/packages/docusaurus-playground/src/pages/index.mdx @@ -18,6 +18,7 @@ import { SectionHeader, TimelineItem, ShowcaseCard, + JobList, } from '../components/mdx' @@ -352,3 +353,4 @@ import { Build on Waku + diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/JobList.scss b/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/JobList.scss new file mode 100644 index 00000000..e69de29b diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/JobList.tsx b/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/JobList.tsx new file mode 100644 index 00000000..2f759b8f --- /dev/null +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/JobList.tsx @@ -0,0 +1,53 @@ +import React, { useState } from 'react' +import useFetchJobs, { JobBoard, Job } from './useFetchJobs' + +type Props = { + jobBoard: JobBoard + titleFilter?: string +} + +export const JobList: React.FC = ({ jobBoard, titleFilter = '' }) => { + const { data, error, fetchJobs } = useFetchJobs() + const [isLoading, setIsLoading] = useState(false) + + const handleFetchJobs = async () => { + setIsLoading(true) + await fetchJobs([jobBoard], titleFilter) + setIsLoading(false) + } + + React.useEffect(() => { + handleFetchJobs() + }, [jobBoard, titleFilter]) + + if (isLoading) { + return
Loading...
+ } + + if (error) { + return
Error: {error.message}
+ } + + if (!data) { + return
No data
+ } + + return ( +
+

Job List

+ +
+ ) +} diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/index.ts b/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/index.ts new file mode 100644 index 00000000..22f40c24 --- /dev/null +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/index.ts @@ -0,0 +1 @@ +export * from './JobList' diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/useFetchJobs.ts b/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/useFetchJobs.ts new file mode 100644 index 00000000..083aed0d --- /dev/null +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/useFetchJobs.ts @@ -0,0 +1,108 @@ +import { useState } from 'react' + +// Inspired by: +// https://github.com/acid-info/rnd-dynamic-list/blob/master/react/useFetchJobs.js + +const jobBoards = [ + 'acidtest', + 'logos', + 'status', + 'nimbus', + 'codex', + 'nomos', + 'statusnetwork', + 'ift', + 'vac', + 'waku', +] as const + +export type JobBoard = (typeof jobBoards)[number] + +const jobBoardMappings: Record = { + acidtest: 'testacidinfo', + logos: 'logos', + status: 'status72', + nimbus: 'nimbus', + codex: 'codex', + nomos: 'nomos', + statusnetwork: 'thestatusnetwork', + ift: 'instituteoffreetechnologies', + vac: 'vac', + waku: 'waku', +} + +export type Job = { + absolute_url: string + data_compliance?: { + type: string + requires_consent: boolean + requires_processing_consent: boolean + requires_retention_consent: boolean + retention_period: null | string // Assuming retention_period can be a string or null. + }[] + internal_job_id: number + location?: { + name: string + } + metadata?: any + id: number + updated_at: string + requisition_id: string + title: string +} + +type JobData = { + jobs: Job[] + meta: { + total: number + } +} | null + +type UseFetchJobs = { + data: JobData + error: any + fetchJobs: (jobBoardsToFetch: JobBoard[], titleFilter: string) => void +} + +export const useFetchJobs = (): UseFetchJobs => { + const [data, setData] = useState(null) + const [error, setError] = useState(null) + + const fetchJobs = async (jobBoardsToFetch: JobBoard[], titleFilter) => { + try { + const resultsPerBoard: Partial> = {} + + for (let board of jobBoardsToFetch) { + const response = await fetch( + `https://boards-api.greenhouse.io/v1/boards/${jobBoardMappings[board]}/jobs`, + ) + const jobData = await response.json() + resultsPerBoard[board] = jobData.jobs + } + + let jobs = Object.values(resultsPerBoard).flat() as unknown as Job[] + + if (titleFilter) { + jobs = jobs.filter( + (job) => job.title && job.title.includes(titleFilter), + ) + } + + setData({ jobs, meta: { total: jobs.length } }) + } catch (err) { + if (err instanceof Error) { + setError(err) + } else if (typeof err === 'string') { + setError(new Error(err)) + } else { + setError(new Error('Unknown error')) + } + + console.error(err) + } + } + + return { data, error, fetchJobs } +} + +export default useFetchJobs diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/index.ts b/packages/logos-docusaurus-theme/src/client/components/mdx/index.ts index 24e703e5..a09c9868 100644 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/index.ts +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/index.ts @@ -24,3 +24,4 @@ export * from './Showcase' export * from './ShowcaseCard' export * from './SocialCard' export * from './TimelineItem' +export * from './JobList' From 330abd47adc3e9ae72cd6adcbfd1d196f9c119c0 Mon Sep 17 00:00:00 2001 From: jongomez Date: Wed, 25 Oct 2023 14:25:02 +0100 Subject: [PATCH 2/7] feat: fetches jobs per board --- .../docusaurus-playground/src/pages/index.mdx | 4 +- .../components/mdx/JobList/JobList.scss | 0 .../client/components/mdx/JobList/JobList.tsx | 53 ------ .../client/components/mdx/JobList/index.ts | 1 - .../components/mdx/JobList/useFetchJobs.ts | 108 ------------- .../components/mdx/JobLists/JobLists.scss | 52 ++++++ .../components/mdx/JobLists/JobLists.tsx | 77 +++++++++ .../mdx/JobLists/SingleJobBoardList.tsx | 47 ++++++ .../client/components/mdx/JobLists/index.ts | 1 + .../mdx/JobLists/jobListsDummyData.ts | 145 +++++++++++++++++ .../components/mdx/JobLists/useFetchJobs.ts | 152 ++++++++++++++++++ .../src/client/components/mdx/index.ts | 2 +- 12 files changed, 477 insertions(+), 165 deletions(-) delete mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobList/JobList.scss delete mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobList/JobList.tsx delete mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobList/index.ts delete mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobList/useFetchJobs.ts create mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/JobLists.scss create mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/JobLists.tsx create mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/SingleJobBoardList.tsx create mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/index.ts create mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/jobListsDummyData.ts create mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/useFetchJobs.ts diff --git a/packages/docusaurus-playground/src/pages/index.mdx b/packages/docusaurus-playground/src/pages/index.mdx index 0a107ce3..20c25d32 100644 --- a/packages/docusaurus-playground/src/pages/index.mdx +++ b/packages/docusaurus-playground/src/pages/index.mdx @@ -18,7 +18,7 @@ import { SectionHeader, TimelineItem, ShowcaseCard, - JobList, + JobLists, } from '../components/mdx' @@ -353,4 +353,4 @@ import { Build on Waku - + diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/JobList.scss b/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/JobList.scss deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/JobList.tsx b/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/JobList.tsx deleted file mode 100644 index 2f759b8f..00000000 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/JobList.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React, { useState } from 'react' -import useFetchJobs, { JobBoard, Job } from './useFetchJobs' - -type Props = { - jobBoard: JobBoard - titleFilter?: string -} - -export const JobList: React.FC = ({ jobBoard, titleFilter = '' }) => { - const { data, error, fetchJobs } = useFetchJobs() - const [isLoading, setIsLoading] = useState(false) - - const handleFetchJobs = async () => { - setIsLoading(true) - await fetchJobs([jobBoard], titleFilter) - setIsLoading(false) - } - - React.useEffect(() => { - handleFetchJobs() - }, [jobBoard, titleFilter]) - - if (isLoading) { - return
Loading...
- } - - if (error) { - return
Error: {error.message}
- } - - if (!data) { - return
No data
- } - - return ( -
-

Job List

- -
- ) -} diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/index.ts b/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/index.ts deleted file mode 100644 index 22f40c24..00000000 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './JobList' diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/useFetchJobs.ts b/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/useFetchJobs.ts deleted file mode 100644 index 083aed0d..00000000 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/JobList/useFetchJobs.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { useState } from 'react' - -// Inspired by: -// https://github.com/acid-info/rnd-dynamic-list/blob/master/react/useFetchJobs.js - -const jobBoards = [ - 'acidtest', - 'logos', - 'status', - 'nimbus', - 'codex', - 'nomos', - 'statusnetwork', - 'ift', - 'vac', - 'waku', -] as const - -export type JobBoard = (typeof jobBoards)[number] - -const jobBoardMappings: Record = { - acidtest: 'testacidinfo', - logos: 'logos', - status: 'status72', - nimbus: 'nimbus', - codex: 'codex', - nomos: 'nomos', - statusnetwork: 'thestatusnetwork', - ift: 'instituteoffreetechnologies', - vac: 'vac', - waku: 'waku', -} - -export type Job = { - absolute_url: string - data_compliance?: { - type: string - requires_consent: boolean - requires_processing_consent: boolean - requires_retention_consent: boolean - retention_period: null | string // Assuming retention_period can be a string or null. - }[] - internal_job_id: number - location?: { - name: string - } - metadata?: any - id: number - updated_at: string - requisition_id: string - title: string -} - -type JobData = { - jobs: Job[] - meta: { - total: number - } -} | null - -type UseFetchJobs = { - data: JobData - error: any - fetchJobs: (jobBoardsToFetch: JobBoard[], titleFilter: string) => void -} - -export const useFetchJobs = (): UseFetchJobs => { - const [data, setData] = useState(null) - const [error, setError] = useState(null) - - const fetchJobs = async (jobBoardsToFetch: JobBoard[], titleFilter) => { - try { - const resultsPerBoard: Partial> = {} - - for (let board of jobBoardsToFetch) { - const response = await fetch( - `https://boards-api.greenhouse.io/v1/boards/${jobBoardMappings[board]}/jobs`, - ) - const jobData = await response.json() - resultsPerBoard[board] = jobData.jobs - } - - let jobs = Object.values(resultsPerBoard).flat() as unknown as Job[] - - if (titleFilter) { - jobs = jobs.filter( - (job) => job.title && job.title.includes(titleFilter), - ) - } - - setData({ jobs, meta: { total: jobs.length } }) - } catch (err) { - if (err instanceof Error) { - setError(err) - } else if (typeof err === 'string') { - setError(new Error(err)) - } else { - setError(new Error('Unknown error')) - } - - console.error(err) - } - } - - return { data, error, fetchJobs } -} - -export default useFetchJobs diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/JobLists.scss b/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/JobLists.scss new file mode 100644 index 00000000..215677df --- /dev/null +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/JobLists.scss @@ -0,0 +1,52 @@ +@use '../../../css/utils'; +@use '../../../css/lsd'; + +.mdx-jl__header { + margin-bottom: 40px; + margin-top: 16px; +} + +.mdx-jl__single-job-board-container { + border-top: 1px solid rgb(var(--lsd-border-primary)); + padding-top: 8px; +} + +.mdx-jl__board-title { + font-size: 12px; + line-height: 16px; +} + +.mdx-jl__job-title-container { + display: flex; + align-items: center; + + padding-bottom: 8px; +} + +.mdx-jl__external-link-icon { + margin-left: 8px; +} + +.mdx-jl__job-list { + list-style-type: none; + padding: 8px 0 24px 0; + margin: 0; +} + +.mdx-jl__job-list-item { + padding: 14px 0; +} + +.mdx-jl__job-link { + display: block; + width: fit-content; + text-decoration: none; + + &:hover { + text-decoration: none; + + .mdx-jl__job-title { + text-decoration: underline; + } + } +} diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/JobLists.tsx b/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/JobLists.tsx new file mode 100644 index 00000000..ac340294 --- /dev/null +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/JobLists.tsx @@ -0,0 +1,77 @@ +import { Typography } from '@acid-info/lsd-react' +import { SingleJobBoardList } from './SingleJobBoardList' +import useFetchJobs, { JobBoard, JobsPerBoard } from './useFetchJobs' +import React from 'react' +import { jobListsDummyData } from './jobListsDummyData' + +const hasJobs = (jobsPerBoard: JobsPerBoard): boolean => { + if (!jobsPerBoard) return false + let jobFound = false + + Object.keys(jobsPerBoard).forEach((board) => { + if (jobsPerBoard[board]!.jobs.length > 0) { + jobFound = true + return + } + }) + + return jobFound +} + +type JobListsProps = React.HTMLAttributes & { + jobBoard: JobBoard + titleFilter?: string + fetchAll?: boolean + useDummyData?: boolean +} + +export const JobLists: React.FC = ({ + jobBoard, + titleFilter = '', + fetchAll = false, + useDummyData = false, + ...props +}) => { + const { data, error, isLoading } = useFetchJobs( + [jobBoard], + fetchAll, + titleFilter, + useDummyData ? jobListsDummyData : null, + ) + + console.log('data', data) + console.log('error', error) + console.log('isLoading', isLoading) + + if (isLoading) { + return
Loading...
+ } + + if (error) { + return
Error fetching data
+ } + + if (!data || !hasJobs(data)) { + return
No job openings to show
+ } + + const returnedJobBoards = Object.keys(data) + + return ( +
+ + Current job openings + + {returnedJobBoards.map((board) => { + const jobDataForBoard = data[board] + return ( + + ) + })} +
+ ) +} diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/SingleJobBoardList.tsx b/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/SingleJobBoardList.tsx new file mode 100644 index 00000000..416291e2 --- /dev/null +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/SingleJobBoardList.tsx @@ -0,0 +1,47 @@ +import { Typography } from '@acid-info/lsd-react' +import { Job, JobBoard } from './useFetchJobs' +import React from 'react' +import { IconExternalLink } from '@logos-theme/components/Icon' +import './JobLists.scss' + +type SingleJobBoardListProps = { + jobBoard: JobBoard + jobs: Job[] +} + +export const SingleJobBoardList: React.FC = ({ + jobBoard, + jobs, +}) => { + return ( +
+ + {jobBoard} + + +
+ ) +} diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/index.ts b/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/index.ts new file mode 100644 index 00000000..8dd06380 --- /dev/null +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/index.ts @@ -0,0 +1 @@ +export * from './JobLists' diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/jobListsDummyData.ts b/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/jobListsDummyData.ts new file mode 100644 index 00000000..a3be8069 --- /dev/null +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/jobListsDummyData.ts @@ -0,0 +1,145 @@ +import { JobsPerBoard } from './useFetchJobs' + +// Only for dev purposes - prevents making multiple requests to the API while developing. +export const jobListsDummyData: JobsPerBoard = { + nomos: { + jobs: [ + { + absolute_url: 'https://boards.greenhouse.io/nomos/jobs/5453089', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 2315608, + location: { + name: 'Remote (Worldwide)', + }, + metadata: null, + id: 5453089, + updated_at: '2023-10-20T07:06:31-04:00', + requisition_id: 'PROV-Nom-5', + title: 'Applied Network Researcher', + }, + ], + meta: { + total: 1, + }, + }, + waku: { + jobs: [ + { + absolute_url: 'https://boards.greenhouse.io/waku/jobs/5453096', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 2055187, + location: { + name: 'Remote (Worldwide)', + }, + metadata: null, + id: 5453096, + updated_at: '2023-10-20T07:00:46-04:00', + requisition_id: 'PROV-Sec-2', + title: 'Protocol Engineer', + }, + { + absolute_url: 'https://boards.greenhouse.io/waku/jobs/5453098', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 1830496, + location: { + name: 'Remote, Worldwide', + }, + metadata: null, + id: 5453098, + updated_at: '2023-10-20T07:01:13-04:00', + requisition_id: 'PROV-Sec-3', + title: 'Protocol Researcher (Distributed Systems)', + }, + { + absolute_url: 'https://boards.greenhouse.io/waku/jobs/5453099', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 2584916, + location: { + name: 'Remote (Worldwide)', + }, + metadata: null, + id: 5453099, + updated_at: '2023-10-20T07:01:37-04:00', + requisition_id: 'SDK-2', + title: 'Software Engineer (Chat SDK)', + }, + { + absolute_url: 'https://boards.greenhouse.io/waku/jobs/5453102', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 2656108, + location: { + name: 'Remote (Worldwide)', + }, + metadata: null, + id: 5453102, + updated_at: '2023-10-20T07:02:20-04:00', + requisition_id: 'PROV-Wak-13', + title: 'Software Engineer Distributed Systems Testing', + }, + { + absolute_url: 'https://boards.greenhouse.io/waku/jobs/5453103', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 2694724, + location: { + name: 'Remote (Worldwide)', + }, + metadata: null, + id: 5453103, + updated_at: '2023-10-20T13:12:19-04:00', + requisition_id: 'WAK-BD-1', + title: 'Technical Business Development Lead ', + }, + ], + meta: { + total: 5, + }, + }, +} diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/useFetchJobs.ts b/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/useFetchJobs.ts new file mode 100644 index 00000000..7993f0e4 --- /dev/null +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/useFetchJobs.ts @@ -0,0 +1,152 @@ +import { useEffect, useRef, useState } from 'react' + +/* +const allJobBoards = [ + 'testacidinfo', // Acid Test job board + 'logos', + 'nimbus', + 'codex', + 'nomos', + 'thestatusnetwork', + 'instituteoffreetechnologies', // aka IFT + 'vac', + 'waku', +] as const + +*/ + +const allJobBoards = ['codex', 'nomos', 'vac', 'waku'] as const + +export type JobBoard = string + +export type Job = { + absolute_url: string + data_compliance?: { + type: string + requires_consent: boolean + requires_processing_consent: boolean + requires_retention_consent: boolean + retention_period: null | string + }[] + internal_job_id: number + location: { + name: string + } + metadata?: any + id: number + updated_at: string + requisition_id: string + title: string +} + +type JobData = { + jobs: Job[] + meta: { + total: number + } +} | null + +export type JobsPerBoard = Record | null + +type UseFetchJobsReturn = { + data: JobsPerBoard + error: Error | null + isLoading: boolean +} + +const parseJobs = (rawJobs: Job[], titleFilter?: string): JobData => { + const jobs = titleFilter + ? rawJobs.filter((job) => job.title?.includes(titleFilter)) + : rawJobs + + return { jobs, meta: { total: jobs.length } } +} + +export const useFetchJobs = ( + jobBoardsToFetch: JobBoard[] = [], + fetchAllJobBoards: boolean = false, + titleFilter: string = '', + dummyResponse: JobsPerBoard = null, +): UseFetchJobsReturn => { + const [data, setData] = useState(null) + const [error, setError] = useState(null) + const [isLoading, setIsLoading] = useState(false) + const isMounted = useRef(true) + + const fetchJobs = async (): Promise<{ + jobsPerBoard: JobsPerBoard + errorData: Error | null + }> => { + try { + if (dummyResponse) { + return { jobsPerBoard: dummyResponse, errorData: null } + } + + const jobsPerBoard: JobsPerBoard = {} + + if (fetchAllJobBoards) { + const promises = allJobBoards.map((boardName) => + fetch( + `https://boards-api.greenhouse.io/v1/boards/${boardName}/jobs`, + ).then((response) => response.json()), + ) + + const allResults = await Promise.all(promises) + allResults.forEach((result, index) => { + const currentBoard = allJobBoards[index] + + if (!currentBoard) { + throw new Error('No current board found for ' + result) + } + + jobsPerBoard[currentBoard] = parseJobs(result.jobs, titleFilter) + }) + } else { + for (let board of jobBoardsToFetch) { + const response = await fetch( + `https://boards-api.greenhouse.io/v1/boards/${board}/jobs`, + ) + const jobData = await response.json() + jobsPerBoard[board] = parseJobs(jobData.jobs, titleFilter) + } + } + + return { + jobsPerBoard, + errorData: null, + } + } catch (err) { + const errorData = + err instanceof Error + ? err + : typeof err === 'string' + ? new Error(err) + : new Error('Unknown error') + + console.error(err) + return { jobsPerBoard: null, errorData } + } + } + + useEffect(() => { + if (isLoading) return + + setIsLoading(true) + fetchJobs().then(({ jobsPerBoard, errorData }) => { + // We should not update state if the component is unmounted. + if (!isMounted.current) return + + if (jobsPerBoard) setData(jobsPerBoard) + if (errorData) setError(errorData) + setIsLoading(false) + }) + + return () => { + isMounted.current = false + } + }, []) + + return { data, error, isLoading } +} + +export default useFetchJobs diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/index.ts b/packages/logos-docusaurus-theme/src/client/components/mdx/index.ts index a09c9868..ae3212b7 100644 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/index.ts +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/index.ts @@ -24,4 +24,4 @@ export * from './Showcase' export * from './ShowcaseCard' export * from './SocialCard' export * from './TimelineItem' -export * from './JobList' +export * from './JobLists' From ebeb0079318cfb3753929d2fe2670f3ce32d481c Mon Sep 17 00:00:00 2001 From: jongomez Date: Wed, 25 Oct 2023 15:24:02 +0100 Subject: [PATCH 3/7] fix: use the department API endpoint instead - working version --- .../docusaurus-playground/src/pages/index.mdx | 4 +- .../components/mdx/JobLists/JobLists.tsx | 77 --- .../mdx/JobLists/SingleJobBoardList.tsx | 47 -- .../client/components/mdx/JobLists/index.ts | 1 - .../mdx/JobLists/jobListsDummyData.ts | 145 ----- .../components/mdx/JobLists/useFetchJobs.ts | 152 ----- .../JobsPerDepartment.scss} | 22 +- .../JobsPerDepartment/JobsPerDepartment.tsx | 66 +++ .../SingleDepartmentJobs.tsx | 49 ++ .../components/mdx/JobsPerDepartment/index.ts | 1 + .../jobsPerDepartmentDummyData.ts | 555 ++++++++++++++++++ .../mdx/JobsPerDepartment/useFetchJobs.ts | 162 +++++ .../src/client/components/mdx/index.ts | 2 +- 13 files changed, 847 insertions(+), 436 deletions(-) delete mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/JobLists.tsx delete mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/SingleJobBoardList.tsx delete mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/index.ts delete mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/jobListsDummyData.ts delete mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/useFetchJobs.ts rename packages/logos-docusaurus-theme/src/client/components/mdx/{JobLists/JobLists.scss => JobsPerDepartment/JobsPerDepartment.scss} (62%) create mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/JobsPerDepartment.tsx create mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/SingleDepartmentJobs.tsx create mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/index.ts create mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/jobsPerDepartmentDummyData.ts create mode 100644 packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/useFetchJobs.ts diff --git a/packages/docusaurus-playground/src/pages/index.mdx b/packages/docusaurus-playground/src/pages/index.mdx index 20c25d32..7d4abbb8 100644 --- a/packages/docusaurus-playground/src/pages/index.mdx +++ b/packages/docusaurus-playground/src/pages/index.mdx @@ -18,7 +18,7 @@ import { SectionHeader, TimelineItem, ShowcaseCard, - JobLists, + JobsPerDepartment, } from '../components/mdx' @@ -353,4 +353,4 @@ import { Build on Waku - + diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/JobLists.tsx b/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/JobLists.tsx deleted file mode 100644 index ac340294..00000000 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/JobLists.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { Typography } from '@acid-info/lsd-react' -import { SingleJobBoardList } from './SingleJobBoardList' -import useFetchJobs, { JobBoard, JobsPerBoard } from './useFetchJobs' -import React from 'react' -import { jobListsDummyData } from './jobListsDummyData' - -const hasJobs = (jobsPerBoard: JobsPerBoard): boolean => { - if (!jobsPerBoard) return false - let jobFound = false - - Object.keys(jobsPerBoard).forEach((board) => { - if (jobsPerBoard[board]!.jobs.length > 0) { - jobFound = true - return - } - }) - - return jobFound -} - -type JobListsProps = React.HTMLAttributes & { - jobBoard: JobBoard - titleFilter?: string - fetchAll?: boolean - useDummyData?: boolean -} - -export const JobLists: React.FC = ({ - jobBoard, - titleFilter = '', - fetchAll = false, - useDummyData = false, - ...props -}) => { - const { data, error, isLoading } = useFetchJobs( - [jobBoard], - fetchAll, - titleFilter, - useDummyData ? jobListsDummyData : null, - ) - - console.log('data', data) - console.log('error', error) - console.log('isLoading', isLoading) - - if (isLoading) { - return
Loading...
- } - - if (error) { - return
Error fetching data
- } - - if (!data || !hasJobs(data)) { - return
No job openings to show
- } - - const returnedJobBoards = Object.keys(data) - - return ( -
- - Current job openings - - {returnedJobBoards.map((board) => { - const jobDataForBoard = data[board] - return ( - - ) - })} -
- ) -} diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/SingleJobBoardList.tsx b/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/SingleJobBoardList.tsx deleted file mode 100644 index 416291e2..00000000 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/SingleJobBoardList.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { Typography } from '@acid-info/lsd-react' -import { Job, JobBoard } from './useFetchJobs' -import React from 'react' -import { IconExternalLink } from '@logos-theme/components/Icon' -import './JobLists.scss' - -type SingleJobBoardListProps = { - jobBoard: JobBoard - jobs: Job[] -} - -export const SingleJobBoardList: React.FC = ({ - jobBoard, - jobs, -}) => { - return ( -
- - {jobBoard} - - -
- ) -} diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/index.ts b/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/index.ts deleted file mode 100644 index 8dd06380..00000000 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './JobLists' diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/jobListsDummyData.ts b/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/jobListsDummyData.ts deleted file mode 100644 index a3be8069..00000000 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/jobListsDummyData.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { JobsPerBoard } from './useFetchJobs' - -// Only for dev purposes - prevents making multiple requests to the API while developing. -export const jobListsDummyData: JobsPerBoard = { - nomos: { - jobs: [ - { - absolute_url: 'https://boards.greenhouse.io/nomos/jobs/5453089', - data_compliance: [ - { - type: 'gdpr', - requires_consent: false, - requires_processing_consent: false, - requires_retention_consent: false, - retention_period: null, - }, - ], - internal_job_id: 2315608, - location: { - name: 'Remote (Worldwide)', - }, - metadata: null, - id: 5453089, - updated_at: '2023-10-20T07:06:31-04:00', - requisition_id: 'PROV-Nom-5', - title: 'Applied Network Researcher', - }, - ], - meta: { - total: 1, - }, - }, - waku: { - jobs: [ - { - absolute_url: 'https://boards.greenhouse.io/waku/jobs/5453096', - data_compliance: [ - { - type: 'gdpr', - requires_consent: false, - requires_processing_consent: false, - requires_retention_consent: false, - retention_period: null, - }, - ], - internal_job_id: 2055187, - location: { - name: 'Remote (Worldwide)', - }, - metadata: null, - id: 5453096, - updated_at: '2023-10-20T07:00:46-04:00', - requisition_id: 'PROV-Sec-2', - title: 'Protocol Engineer', - }, - { - absolute_url: 'https://boards.greenhouse.io/waku/jobs/5453098', - data_compliance: [ - { - type: 'gdpr', - requires_consent: false, - requires_processing_consent: false, - requires_retention_consent: false, - retention_period: null, - }, - ], - internal_job_id: 1830496, - location: { - name: 'Remote, Worldwide', - }, - metadata: null, - id: 5453098, - updated_at: '2023-10-20T07:01:13-04:00', - requisition_id: 'PROV-Sec-3', - title: 'Protocol Researcher (Distributed Systems)', - }, - { - absolute_url: 'https://boards.greenhouse.io/waku/jobs/5453099', - data_compliance: [ - { - type: 'gdpr', - requires_consent: false, - requires_processing_consent: false, - requires_retention_consent: false, - retention_period: null, - }, - ], - internal_job_id: 2584916, - location: { - name: 'Remote (Worldwide)', - }, - metadata: null, - id: 5453099, - updated_at: '2023-10-20T07:01:37-04:00', - requisition_id: 'SDK-2', - title: 'Software Engineer (Chat SDK)', - }, - { - absolute_url: 'https://boards.greenhouse.io/waku/jobs/5453102', - data_compliance: [ - { - type: 'gdpr', - requires_consent: false, - requires_processing_consent: false, - requires_retention_consent: false, - retention_period: null, - }, - ], - internal_job_id: 2656108, - location: { - name: 'Remote (Worldwide)', - }, - metadata: null, - id: 5453102, - updated_at: '2023-10-20T07:02:20-04:00', - requisition_id: 'PROV-Wak-13', - title: 'Software Engineer Distributed Systems Testing', - }, - { - absolute_url: 'https://boards.greenhouse.io/waku/jobs/5453103', - data_compliance: [ - { - type: 'gdpr', - requires_consent: false, - requires_processing_consent: false, - requires_retention_consent: false, - retention_period: null, - }, - ], - internal_job_id: 2694724, - location: { - name: 'Remote (Worldwide)', - }, - metadata: null, - id: 5453103, - updated_at: '2023-10-20T13:12:19-04:00', - requisition_id: 'WAK-BD-1', - title: 'Technical Business Development Lead ', - }, - ], - meta: { - total: 5, - }, - }, -} diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/useFetchJobs.ts b/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/useFetchJobs.ts deleted file mode 100644 index 7993f0e4..00000000 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/useFetchJobs.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { useEffect, useRef, useState } from 'react' - -/* -const allJobBoards = [ - 'testacidinfo', // Acid Test job board - 'logos', - 'nimbus', - 'codex', - 'nomos', - 'thestatusnetwork', - 'instituteoffreetechnologies', // aka IFT - 'vac', - 'waku', -] as const - -*/ - -const allJobBoards = ['codex', 'nomos', 'vac', 'waku'] as const - -export type JobBoard = string - -export type Job = { - absolute_url: string - data_compliance?: { - type: string - requires_consent: boolean - requires_processing_consent: boolean - requires_retention_consent: boolean - retention_period: null | string - }[] - internal_job_id: number - location: { - name: string - } - metadata?: any - id: number - updated_at: string - requisition_id: string - title: string -} - -type JobData = { - jobs: Job[] - meta: { - total: number - } -} | null - -export type JobsPerBoard = Record | null - -type UseFetchJobsReturn = { - data: JobsPerBoard - error: Error | null - isLoading: boolean -} - -const parseJobs = (rawJobs: Job[], titleFilter?: string): JobData => { - const jobs = titleFilter - ? rawJobs.filter((job) => job.title?.includes(titleFilter)) - : rawJobs - - return { jobs, meta: { total: jobs.length } } -} - -export const useFetchJobs = ( - jobBoardsToFetch: JobBoard[] = [], - fetchAllJobBoards: boolean = false, - titleFilter: string = '', - dummyResponse: JobsPerBoard = null, -): UseFetchJobsReturn => { - const [data, setData] = useState(null) - const [error, setError] = useState(null) - const [isLoading, setIsLoading] = useState(false) - const isMounted = useRef(true) - - const fetchJobs = async (): Promise<{ - jobsPerBoard: JobsPerBoard - errorData: Error | null - }> => { - try { - if (dummyResponse) { - return { jobsPerBoard: dummyResponse, errorData: null } - } - - const jobsPerBoard: JobsPerBoard = {} - - if (fetchAllJobBoards) { - const promises = allJobBoards.map((boardName) => - fetch( - `https://boards-api.greenhouse.io/v1/boards/${boardName}/jobs`, - ).then((response) => response.json()), - ) - - const allResults = await Promise.all(promises) - allResults.forEach((result, index) => { - const currentBoard = allJobBoards[index] - - if (!currentBoard) { - throw new Error('No current board found for ' + result) - } - - jobsPerBoard[currentBoard] = parseJobs(result.jobs, titleFilter) - }) - } else { - for (let board of jobBoardsToFetch) { - const response = await fetch( - `https://boards-api.greenhouse.io/v1/boards/${board}/jobs`, - ) - const jobData = await response.json() - jobsPerBoard[board] = parseJobs(jobData.jobs, titleFilter) - } - } - - return { - jobsPerBoard, - errorData: null, - } - } catch (err) { - const errorData = - err instanceof Error - ? err - : typeof err === 'string' - ? new Error(err) - : new Error('Unknown error') - - console.error(err) - return { jobsPerBoard: null, errorData } - } - } - - useEffect(() => { - if (isLoading) return - - setIsLoading(true) - fetchJobs().then(({ jobsPerBoard, errorData }) => { - // We should not update state if the component is unmounted. - if (!isMounted.current) return - - if (jobsPerBoard) setData(jobsPerBoard) - if (errorData) setError(errorData) - setIsLoading(false) - }) - - return () => { - isMounted.current = false - } - }, []) - - return { data, error, isLoading } -} - -export default useFetchJobs diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/JobLists.scss b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/JobsPerDepartment.scss similarity index 62% rename from packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/JobLists.scss rename to packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/JobsPerDepartment.scss index 215677df..c2e7d88a 100644 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/JobLists/JobLists.scss +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/JobsPerDepartment.scss @@ -1,43 +1,43 @@ @use '../../../css/utils'; @use '../../../css/lsd'; -.mdx-jl__header { +.mdx-jpd__header { margin-bottom: 40px; margin-top: 16px; } -.mdx-jl__single-job-board-container { +.mdx-jpd__single-job-department-container { border-top: 1px solid rgb(var(--lsd-border-primary)); padding-top: 8px; } -.mdx-jl__board-title { - font-size: 12px; - line-height: 16px; +.mdx-jpd__department-title { + font-size: 12px !important; + line-height: 16px !important; } -.mdx-jl__job-title-container { +.mdx-jpd__job-title-container { display: flex; align-items: center; padding-bottom: 8px; } -.mdx-jl__external-link-icon { +.mdx-jpd__external-link-icon { margin-left: 8px; } -.mdx-jl__job-list { +.mdx-jpd__job-list { list-style-type: none; padding: 8px 0 24px 0; margin: 0; } -.mdx-jl__job-list-item { +.mdx-jpd__job-list-item { padding: 14px 0; } -.mdx-jl__job-link { +.mdx-jpd__job-link { display: block; width: fit-content; text-decoration: none; @@ -45,7 +45,7 @@ &:hover { text-decoration: none; - .mdx-jl__job-title { + .mdx-jpd__job-title { text-decoration: underline; } } diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/JobsPerDepartment.tsx b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/JobsPerDepartment.tsx new file mode 100644 index 00000000..43395f27 --- /dev/null +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/JobsPerDepartment.tsx @@ -0,0 +1,66 @@ +import { Typography } from '@acid-info/lsd-react' +import { SingleDepartmentJobs } from './SingleDepartmentJobs' +import useFetchJobs, { JobDepartmentArray } from './useFetchJobs' +import React from 'react' +import { jobsPerDepartmentDummyData } from './jobsPerDepartmentDummyData' + +const hasJobs = (jobsPerDepartment: JobDepartmentArray): boolean => { + if (!jobsPerDepartment) return false + + // Check if there's any department that has at least one job + let jobFound = jobsPerDepartment.some((department) => { + return department.jobs && department.jobs.length > 0 + }) + + return jobFound +} + +type JobsPerDepartmentProps = React.HTMLAttributes & { + jobBoard: string + titleFilter?: string + fetchAll?: boolean + useDummyData?: boolean +} + +export const JobsPerDepartment: React.FC = ({ + jobBoard, + titleFilter = '', + fetchAll = false, + useDummyData = false, + ...props +}) => { + const { data, error, isLoading } = useFetchJobs( + jobBoard, + titleFilter, + useDummyData ? jobsPerDepartmentDummyData : null, + ) + + console.log('data', data) + console.log('error', error) + console.log('isLoading', isLoading) + + if (isLoading) { + return
Loading...
+ } + + if (error) { + return
Error fetching data
+ } + + if (!data || !hasJobs(data)) { + return
No job openings to show
+ } + + return ( +
+ + Current job openings + + {data.map((department) => { + return ( + + ) + })} +
+ ) +} diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/SingleDepartmentJobs.tsx b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/SingleDepartmentJobs.tsx new file mode 100644 index 00000000..e4c504bd --- /dev/null +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/SingleDepartmentJobs.tsx @@ -0,0 +1,49 @@ +import { Typography } from '@acid-info/lsd-react' +import { Job, JobDepartmentData } from './useFetchJobs' +import React from 'react' +import { IconExternalLink } from '@logos-theme/components/Icon' +import './JobsPerDepartment.scss' + +type SingleDepartmentJobsProps = { + department: JobDepartmentData +} + +export const SingleDepartmentJobs: React.FC = ({ + department, +}) => { + if (!department.jobs || department.jobs.length === 0) { + return null + } + + return ( +
+ + {department.name} + + +
+ ) +} diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/index.ts b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/index.ts new file mode 100644 index 00000000..68907d2f --- /dev/null +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/index.ts @@ -0,0 +1 @@ +export * from './JobsPerDepartment' diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/jobsPerDepartmentDummyData.ts b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/jobsPerDepartmentDummyData.ts new file mode 100644 index 00000000..ee967da2 --- /dev/null +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/jobsPerDepartmentDummyData.ts @@ -0,0 +1,555 @@ +import { JobDepartmentArray } from './useFetchJobs' + +// Only for dev purposes - prevents making multiple requests to the API while developing. +export const jobsPerDepartmentDummyData: JobDepartmentArray = [ + { + id: 87842, + name: 'App', + parent_id: 43806, + child_ids: [87847, 87852, 87850, 87848, 45530, 87849], + jobs: [], + }, + { + id: 54504, + name: 'Brand Design Studio', + parent_id: null, + child_ids: [], + jobs: [], + }, + { + id: 45532, + name: 'Business Development', + parent_id: null, + child_ids: [], + jobs: [], + }, + { + id: 87841, + name: 'Codex', + parent_id: 43806, + child_ids: [], + jobs: [ + { + absolute_url: 'https://jobs.status.im/?gh_jid=5329400', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 2662332, + location: { + name: 'Remote (Worldwide)', + }, + metadata: null, + id: 5329400, + updated_at: '2023-10-13T09:40:03-04:00', + requisition_id: 'Cod-6', + title: 'Technical Business Development Lead [Codex]', + }, + ], + }, + { + id: 84549, + name: 'Communications', + parent_id: null, + child_ids: [], + jobs: [ + { + absolute_url: 'https://jobs.status.im/?gh_jid=5276254', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 2645076, + location: { + name: 'Remote (Worldwide)', + }, + metadata: null, + id: 5276254, + updated_at: '2023-10-19T03:08:59-04:00', + requisition_id: 'PROV-Com-16', + title: 'Motion Designer', + }, + ], + }, + { + id: 45531, + name: 'Design', + parent_id: null, + child_ids: [], + jobs: [], + }, + { + id: 87847, + name: 'Desktop', + parent_id: 87842, + child_ids: [], + jobs: [], + }, + { + id: 87852, + name: 'Documentation', + parent_id: 87842, + child_ids: [], + jobs: [], + }, + { + id: 45547, + name: 'Engineering ', + parent_id: null, + child_ids: [], + jobs: [ + { + absolute_url: 'https://jobs.status.im/?gh_jid=5419957', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 2693761, + location: { + name: 'Remote (Worldwide)', + }, + metadata: null, + id: 5419957, + updated_at: '2023-10-24T07:30:00-04:00', + requisition_id: 'APP-QA-2', + title: 'Desktop QA Engineer ', + }, + { + absolute_url: 'https://jobs.status.im/?gh_jid=3694379', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 2055210, + location: { + name: 'Remote (Worldwide)', + }, + metadata: null, + id: 3694379, + updated_at: '2023-10-24T16:23:12-04:00', + requisition_id: 'BACK-1050', + title: 'Senior C++ Qt/QML developer for blockchain app', + }, + { + absolute_url: 'https://jobs.status.im/?gh_jid=3702173', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 2058858, + location: { + name: 'Remote (Worldwide)', + }, + metadata: null, + id: 3702173, + updated_at: '2023-10-24T16:21:54-04:00', + requisition_id: '93', + title: 'Senior Mobile ClojureScript UI Developer ', + }, + ], + }, + { + id: 49925, + name: 'Finance', + parent_id: 87845, + child_ids: [], + jobs: [], + }, + { + id: 87854, + name: 'Infrastructure', + parent_id: 43806, + child_ids: [], + jobs: [], + }, + { + id: 87853, + name: 'Insights', + parent_id: 87845, + child_ids: [], + jobs: [], + }, + { + id: 87850, + name: 'Keycard', + parent_id: 87842, + child_ids: [], + jobs: [], + }, + { + id: 145838, + name: 'Leadership', + parent_id: null, + child_ids: [], + jobs: [ + { + absolute_url: 'https://jobs.status.im/?gh_jid=5447463', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 2704948, + location: { + name: 'Remote (Worldwide)', + }, + metadata: null, + id: 5447463, + updated_at: '2023-10-24T09:07:22-04:00', + requisition_id: 'LEAD-1', + title: 'Chief of Staff [whole ecosystem]', + }, + ], + }, + { + id: 74156, + name: 'Legal', + parent_id: 87845, + child_ids: [], + jobs: [], + }, + { + id: 91698, + name: 'Logos', + parent_id: null, + child_ids: [], + jobs: [], + }, + { + id: 43807, + name: 'Marketing', + parent_id: null, + child_ids: [], + jobs: [], + }, + { + id: 87848, + name: 'Mobile', + parent_id: 87842, + child_ids: [], + jobs: [], + }, + { + id: 87843, + name: 'Nimbus', + parent_id: 43806, + child_ids: [], + jobs: [ + { + absolute_url: 'https://jobs.status.im/?gh_jid=5370820', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 2679527, + location: { + name: 'Remote (Worldwide)', + }, + metadata: null, + id: 5370820, + updated_at: '2023-10-19T03:05:07-04:00', + requisition_id: 'LIDO-1', + title: 'Senior DevOps Engineer (Blockchain)', + }, + ], + }, + { + id: 144866, + name: 'Nomos', + parent_id: 43806, + child_ids: [], + jobs: [ + { + absolute_url: 'https://jobs.status.im/?gh_jid=5433423', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 2315608, + location: { + name: 'Remote (Worldwide)', + }, + metadata: null, + id: 5433423, + updated_at: '2023-10-12T11:20:52-04:00', + requisition_id: 'PROV-Nom-5', + title: 'Applied Network Researcher', + }, + ], + }, + { + id: 45548, + name: 'People Operations', + parent_id: 87845, + child_ids: [], + jobs: [], + }, + { + id: 45530, + name: 'Product Design', + parent_id: 87842, + child_ids: [], + jobs: [], + }, + { + id: 90941, + name: 'Program Management', + parent_id: null, + child_ids: [], + jobs: [], + }, + { + id: 43806, + name: 'Research & Development', + parent_id: null, + child_ids: [ + 87842, 87841, 87854, 87843, 144866, 87846, 87981, 87847, 87852, 87850, + 87848, 45530, 87849, + ], + jobs: [], + }, + { + id: 87851, + name: 'Security', + parent_id: 87845, + child_ids: [], + jobs: [], + }, + { + id: 87845, + name: 'Services', + parent_id: null, + child_ids: [49925, 87853, 74156, 45548, 87851], + jobs: [], + }, + { + id: 91697, + name: 'Status App ', + parent_id: null, + child_ids: [], + jobs: [], + }, + { + id: 54783, + name: 'Technical Writing ', + parent_id: null, + child_ids: [], + jobs: [], + }, + { + id: 87846, + name: 'Vac', + parent_id: 43806, + child_ids: [], + jobs: [ + { + absolute_url: 'https://jobs.status.im/?gh_jid=4460860', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 2331302, + location: { + name: 'Remote (Worldwide)', + }, + metadata: null, + id: 4460860, + updated_at: '2023-10-04T05:13:53-04:00', + requisition_id: 'PROV-zkV-1', + title: 'Zero Knowledge Research Engineer ', + }, + ], + }, + { + id: 87981, + name: 'Waku', + parent_id: 43806, + child_ids: [], + jobs: [ + { + absolute_url: 'https://jobs.status.im/?gh_jid=5456032', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 2707470, + location: { + name: 'Remote (Worldwide)', + }, + metadata: null, + id: 5456032, + updated_at: '2023-10-23T11:40:19-04:00', + requisition_id: 'WAK-GL-1', + title: 'Growth Lead', + }, + { + absolute_url: 'https://jobs.status.im/?gh_jid=3693623', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 2055187, + location: { + name: 'Remote (Worldwide)', + }, + metadata: null, + id: 3693623, + updated_at: '2023-10-04T05:13:53-04:00', + requisition_id: 'PROV-Sec-2', + title: 'Protocol Engineer', + }, + { + absolute_url: 'https://jobs.status.im/?gh_jid=3157908', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 1830496, + location: { + name: 'Remote, Worldwide', + }, + metadata: null, + id: 3157908, + updated_at: '2023-10-04T05:13:53-04:00', + requisition_id: 'PROV-Sec-3', + title: 'Protocol Researcher (Distributed Systems)', + }, + { + absolute_url: 'https://jobs.status.im/?gh_jid=5175038', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 2584916, + location: { + name: 'Remote (Worldwide)', + }, + metadata: null, + id: 5175038, + updated_at: '2023-10-09T05:53:53-04:00', + requisition_id: 'SDK-2', + title: 'Software Engineer (Chat SDK)', + }, + { + absolute_url: 'https://jobs.status.im/?gh_jid=5310503', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 2656108, + location: { + name: 'Remote (Worldwide)', + }, + metadata: null, + id: 5310503, + updated_at: '2023-10-19T03:12:53-04:00', + requisition_id: 'PROV-Wak-13', + title: 'Software Engineer Distributed Systems Testing', + }, + { + absolute_url: 'https://jobs.status.im/?gh_jid=5423094', + data_compliance: [ + { + type: 'gdpr', + requires_consent: false, + requires_processing_consent: false, + requires_retention_consent: false, + retention_period: null, + }, + ], + internal_job_id: 2694724, + location: { + name: 'Remote (Worldwide)', + }, + metadata: null, + id: 5423094, + updated_at: '2023-10-24T12:39:05-04:00', + requisition_id: 'WAK-BD-1', + title: 'Technical Business Development Lead ', + }, + ], + }, + { + id: 87849, + name: 'Web', + parent_id: 87842, + child_ids: [], + jobs: [], + }, + { + id: 0, + name: 'No Department', + parent_id: null, + child_ids: [], + jobs: [], + }, +] diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/useFetchJobs.ts b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/useFetchJobs.ts new file mode 100644 index 00000000..d65cc826 --- /dev/null +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/useFetchJobs.ts @@ -0,0 +1,162 @@ +import { useEffect, useRef, useState } from 'react' + +/* +// Array of possible job boards: +const allJobBoards = [ + 'testacidinfo', // Acid Test job board + 'logos', + 'nimbus', + 'codex', + 'status72', // Note: this job board has all the jobs from the other job boards + 'nomos', + 'thestatusnetwork', + 'instituteoffreetechnologies', // aka IFT + 'vac', + 'waku', +] as const +*/ + +export type JobDepartmentName = string + +export type JobDepartmentData = { + id: number + name: string + parent_id: number | null + child_ids: number[] + jobs: Job[] +} + +export type Job = { + absolute_url: string + data_compliance?: { + type: string + requires_consent: boolean + requires_processing_consent: boolean + requires_retention_consent: boolean + retention_period: null | string + }[] + internal_job_id: number + location: { + name: string + } + metadata?: any + id: number + updated_at: string + requisition_id: string + title: string +} + +export type JobDepartmentArray = JobDepartmentData[] | null + +type UseFetchJobsReturn = { + data: JobDepartmentArray + error: Error | null + isLoading: boolean +} + +const parseJobDepartments = ( + rawJobDepartmentData: any, + titleFilter?: string, +): JobDepartmentArray => { + const jobDepartmentArray = rawJobDepartmentData?.departments + + if (!jobDepartmentArray) { + throw Error('No departments found in rawJobDepartmentData') + } + + if (!Array.isArray(jobDepartmentArray)) { + throw Error('The received jobDepartmentArray is not an array') + } + + // Map through each department and filter its jobs based on titleFilter + const processedJobDepartmentArray: JobDepartmentData[] = + jobDepartmentArray.map((department: JobDepartmentData) => { + let filteredJobs = department.jobs + + if (titleFilter) { + filteredJobs = department.jobs.filter((job) => + job.title.includes(titleFilter), + ) + } + + return { + ...department, + jobs: filteredJobs, + } + }) + + return processedJobDepartmentArray +} + +// Fetches jobs from greenhouse via their department endpoint: +// https://developers.greenhouse.io/job-board.html#list-departments + +export const useFetchJobs = ( + jobBoardToFetch: string, + titleFilter: string = '', + dummyResponse: JobDepartmentArray = null, +): UseFetchJobsReturn => { + const [data, setData] = useState(null) + const [error, setError] = useState(null) + const [isLoading, setIsLoading] = useState(false) + const isMounted = useRef(true) + + const fetchJobs = async (): Promise<{ + jobsPerDepartment: JobDepartmentArray + errorData: Error | null + }> => { + try { + if (dummyResponse) { + console.log('dummy data bruh:', dummyResponse) + return { jobsPerDepartment: dummyResponse, errorData: null } + } + + const response = await fetch( + `https://boards-api.greenhouse.io/v1/boards/${jobBoardToFetch}/departments`, + ) + const jobDepartmentData = await response.json() + + const jobsPerDepartment: JobDepartmentArray = parseJobDepartments( + jobDepartmentData, + titleFilter, + ) + + return { + jobsPerDepartment, + errorData: null, + } + } catch (err) { + const errorData = + err instanceof Error + ? err + : typeof err === 'string' + ? new Error(err) + : new Error('Unknown error') + + console.error(err) + return { jobsPerDepartment: null, errorData } + } + } + + useEffect(() => { + if (isLoading) return + + setIsLoading(true) + fetchJobs().then(({ jobsPerDepartment, errorData }) => { + // We should not update state if the component is unmounted. + if (!isMounted.current) return + + if (jobsPerDepartment) setData(jobsPerDepartment) + if (errorData) setError(errorData) + setIsLoading(false) + }) + + return () => { + isMounted.current = false + } + }, []) + + return { data, error, isLoading } +} + +export default useFetchJobs diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/index.ts b/packages/logos-docusaurus-theme/src/client/components/mdx/index.ts index ae3212b7..04e421d2 100644 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/index.ts +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/index.ts @@ -24,4 +24,4 @@ export * from './Showcase' export * from './ShowcaseCard' export * from './SocialCard' export * from './TimelineItem' -export * from './JobLists' +export * from './JobsPerDepartment' From bb9bccfd0792d4cf30f92afb636b1d3bdfaee9e9 Mon Sep 17 00:00:00 2001 From: jongomez Date: Wed, 25 Oct 2023 15:55:13 +0100 Subject: [PATCH 4/7] fix: final cleanup --- .../docusaurus-playground/src/pages/index.mdx | 2 +- .../JobsPerDepartment/JobsPerDepartment.tsx | 32 +++++++++++++------ .../mdx/JobsPerDepartment/useFetchJobs.ts | 3 +- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/packages/docusaurus-playground/src/pages/index.mdx b/packages/docusaurus-playground/src/pages/index.mdx index 7d4abbb8..6b22eb5a 100644 --- a/packages/docusaurus-playground/src/pages/index.mdx +++ b/packages/docusaurus-playground/src/pages/index.mdx @@ -353,4 +353,4 @@ import { Build on Waku - + diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/JobsPerDepartment.tsx b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/JobsPerDepartment.tsx index 43395f27..8a1918ff 100644 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/JobsPerDepartment.tsx +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/JobsPerDepartment.tsx @@ -4,6 +4,24 @@ import useFetchJobs, { JobDepartmentArray } from './useFetchJobs' import React from 'react' import { jobsPerDepartmentDummyData } from './jobsPerDepartmentDummyData' +type JobsPerDepartmentHeaderProps = { + message?: string +} + +const JobsPerDepartmentHeader: React.FC = ({ + message, +}) => { + return ( + <> + + Current job openings + + + {!!message && {message}} + + ) +} + const hasJobs = (jobsPerDepartment: JobDepartmentArray): boolean => { if (!jobsPerDepartment) return false @@ -35,27 +53,21 @@ export const JobsPerDepartment: React.FC = ({ useDummyData ? jobsPerDepartmentDummyData : null, ) - console.log('data', data) - console.log('error', error) - console.log('isLoading', isLoading) - if (isLoading) { - return
Loading...
+ return } if (error) { - return
Error fetching data
+ return } if (!data || !hasJobs(data)) { - return
No job openings to show
+ return } return (
- - Current job openings - + {data.map((department) => { return ( diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/useFetchJobs.ts b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/useFetchJobs.ts index d65cc826..570ef794 100644 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/useFetchJobs.ts +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/useFetchJobs.ts @@ -1,7 +1,7 @@ import { useEffect, useRef, useState } from 'react' /* -// Array of possible job boards: +// Here's an array with some possible job boards: const allJobBoards = [ 'testacidinfo', // Acid Test job board 'logos', @@ -107,7 +107,6 @@ export const useFetchJobs = ( }> => { try { if (dummyResponse) { - console.log('dummy data bruh:', dummyResponse) return { jobsPerDepartment: dummyResponse, errorData: null } } From 81f385d12183ef96428b4cf43db0ddc92d632f9d Mon Sep 17 00:00:00 2001 From: jongomez Date: Wed, 25 Oct 2023 16:01:55 +0100 Subject: [PATCH 5/7] fix: replace anchor element with link element and remove unused import --- .../mdx/JobsPerDepartment/SingleDepartmentJobs.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/SingleDepartmentJobs.tsx b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/SingleDepartmentJobs.tsx index e4c504bd..2a26e619 100644 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/SingleDepartmentJobs.tsx +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/SingleDepartmentJobs.tsx @@ -1,8 +1,9 @@ import { Typography } from '@acid-info/lsd-react' -import { Job, JobDepartmentData } from './useFetchJobs' +import { JobDepartmentData } from './useFetchJobs' import React from 'react' import { IconExternalLink } from '@logos-theme/components/Icon' import './JobsPerDepartment.scss' +import Link from '@docusaurus/Link' type SingleDepartmentJobsProps = { department: JobDepartmentData @@ -23,7 +24,7 @@ export const SingleDepartmentJobs: React.FC = ({ From e14a158ab90ad8a875017b5aacf9c2ac31c4c750 Mon Sep 17 00:00:00 2001 From: jongomez Date: Wed, 25 Oct 2023 16:05:42 +0100 Subject: [PATCH 6/7] fix: skip loading state for now --- .../components/mdx/JobsPerDepartment/JobsPerDepartment.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/JobsPerDepartment.tsx b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/JobsPerDepartment.tsx index 8a1918ff..bc313460 100644 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/JobsPerDepartment.tsx +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/JobsPerDepartment.tsx @@ -54,7 +54,8 @@ export const JobsPerDepartment: React.FC = ({ ) if (isLoading) { - return + // Skipping loading state for now, as per the designer's request. + return null } if (error) { From a2a6569efe8f061b99ae52176147b3ed2f1b8def Mon Sep 17 00:00:00 2001 From: jongomez Date: Wed, 25 Oct 2023 20:28:16 +0100 Subject: [PATCH 7/7] fix: just show the header when loading --- .../components/mdx/JobsPerDepartment/JobsPerDepartment.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/JobsPerDepartment.tsx b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/JobsPerDepartment.tsx index bc313460..e503998e 100644 --- a/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/JobsPerDepartment.tsx +++ b/packages/logos-docusaurus-theme/src/client/components/mdx/JobsPerDepartment/JobsPerDepartment.tsx @@ -55,7 +55,7 @@ export const JobsPerDepartment: React.FC = ({ if (isLoading) { // Skipping loading state for now, as per the designer's request. - return null + return } if (error) {