mirror of
https://github.com/wavetermdev/docusaurus-og.git
synced 2026-08-05 13:46:35 -07:00
fetch job and challenges lists and update JobsPerDepartment data source
This commit is contained in:
@@ -0,0 +1 @@
|
||||
GITHUB_ACCESS_TOKEN=
|
||||
@@ -45,6 +45,18 @@ const config = {
|
||||
routeBasePath: '/',
|
||||
path: 'docs',
|
||||
},
|
||||
generated: {
|
||||
challenges: {
|
||||
repoArray: [
|
||||
{ owner: 'logos-co', repo: 'nomos-node' },
|
||||
{ owner: 'logos-co', repo: 'nomos-specs' },
|
||||
],
|
||||
githubAccessToken: process.env.GITHUB_ACCESS_TOKEN,
|
||||
},
|
||||
jobList: {
|
||||
jobBoard: 'waku',
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
],
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
ShowcaseCard,
|
||||
JobsPerDepartment,
|
||||
} from '../components/mdx'
|
||||
import * as jobData from '/generated/jobs.json'
|
||||
|
||||
<Hero size="large">
|
||||
<HeroInfo>
|
||||
@@ -353,4 +354,4 @@ import {
|
||||
Build on Waku
|
||||
</CallToActionButton>
|
||||
</Box>
|
||||
<JobsPerDepartment jobBoard="codex" useDummyData />
|
||||
<JobsPerDepartment jobData={jobData} />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,289 @@
|
||||
{
|
||||
"departments": [
|
||||
{
|
||||
"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": []
|
||||
},
|
||||
{
|
||||
"id": 84549,
|
||||
"name": "Communications",
|
||||
"parent_id": null,
|
||||
"child_ids": [],
|
||||
"jobs": []
|
||||
},
|
||||
{
|
||||
"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": []
|
||||
},
|
||||
{
|
||||
"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": []
|
||||
},
|
||||
{
|
||||
"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": []
|
||||
},
|
||||
{
|
||||
"id": 144866,
|
||||
"name": "Nomos",
|
||||
"parent_id": 43806,
|
||||
"child_ids": [],
|
||||
"jobs": []
|
||||
},
|
||||
{
|
||||
"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": []
|
||||
},
|
||||
{
|
||||
"id": 87981,
|
||||
"name": "Waku",
|
||||
"parent_id": 43806,
|
||||
"child_ids": [],
|
||||
"jobs": [
|
||||
{
|
||||
"absolute_url": "https://boards.greenhouse.io/waku/jobs/5499678",
|
||||
"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": 5499678,
|
||||
"updated_at": "2023-11-10T10:02:59-05:00",
|
||||
"requisition_id": "WAK-GL-1",
|
||||
"title": "Growth Lead"
|
||||
},
|
||||
{
|
||||
"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/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 "
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 87849,
|
||||
"name": "Web",
|
||||
"parent_id": 87842,
|
||||
"child_ids": [],
|
||||
"jobs": []
|
||||
},
|
||||
{
|
||||
"id": 0,
|
||||
"name": "No Department",
|
||||
"parent_id": null,
|
||||
"child_ids": [],
|
||||
"jobs": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -29,7 +29,8 @@
|
||||
"@docusaurus/utils-validation": "^2.4.1",
|
||||
"docusaurus-plugin-sass": "^0.2.3",
|
||||
"fs-extra": "^10.1.0",
|
||||
"lodash": "^4.17.21"
|
||||
"lodash": "^4.17.21",
|
||||
"node-fetch": "2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.14"
|
||||
|
||||
@@ -121,7 +121,7 @@ export default function logosPreset(
|
||||
}
|
||||
}
|
||||
|
||||
if (options.generated) plugins.push([generatedDataPlugin, {}])
|
||||
if (options.generated) plugins.push([generatedDataPlugin, options.generated])
|
||||
|
||||
return {
|
||||
plugins,
|
||||
|
||||
@@ -2,14 +2,18 @@ import { PluginModule } from '@docusaurus/types'
|
||||
import * as fsp from 'fs/promises'
|
||||
import * as fs from 'fs'
|
||||
import path from 'path'
|
||||
import fetch from 'node-fetch'
|
||||
import { fetchGithubIssues } from '../utils/github.utils'
|
||||
|
||||
type JobListConfig = {
|
||||
url: string
|
||||
jobBoard: string
|
||||
}
|
||||
|
||||
type ChallengeConfig = {
|
||||
repo: string
|
||||
owner: string
|
||||
repoArray: {
|
||||
repo: string
|
||||
owner: string
|
||||
}[]
|
||||
githubAccessToken: string
|
||||
}
|
||||
|
||||
@@ -18,23 +22,35 @@ export type GeneratedDataPluginConfig = {
|
||||
challenges?: ChallengeConfig
|
||||
}
|
||||
|
||||
const fetchJobList = async ({ url, out }: { out: string } & JobListConfig) => {
|
||||
const list = []
|
||||
export type FetchJobListParams = {
|
||||
jobBoard: string
|
||||
out: string
|
||||
}
|
||||
|
||||
await fsp.writeFile(out, JSON.stringify(list))
|
||||
const fetchJobList = async ({ jobBoard, out }: FetchJobListParams) => {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://boards-api.greenhouse.io/v1/boards/${jobBoard}/departments`,
|
||||
)
|
||||
const jobDepartmentData = await response.json()
|
||||
|
||||
await fsp.writeFile(out, JSON.stringify(jobDepartmentData))
|
||||
} catch (err) {
|
||||
console.error('\nfetchJobList error:')
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
const fetchChallenges = async ({
|
||||
owner,
|
||||
repo,
|
||||
repoArray,
|
||||
out,
|
||||
githubAccessToken,
|
||||
}: {
|
||||
out: string
|
||||
} & ChallengeConfig) => {
|
||||
const list = []
|
||||
const issues = await fetchGithubIssues(repoArray, githubAccessToken)
|
||||
|
||||
await fsp.writeFile(out, JSON.stringify(list))
|
||||
await fsp.writeFile(out, JSON.stringify(issues))
|
||||
}
|
||||
|
||||
export const generatedDataPlugin: PluginModule = (context, options) => {
|
||||
@@ -51,14 +67,13 @@ export const generatedDataPlugin: PluginModule = (context, options) => {
|
||||
loadContent: async () => {
|
||||
if (jobList)
|
||||
await fetchJobList({
|
||||
url: jobList.url,
|
||||
jobBoard: jobList.jobBoard,
|
||||
out: path.join(outDir, 'jobs.json'),
|
||||
})
|
||||
|
||||
if (challenges)
|
||||
await fetchChallenges({
|
||||
repo: challenges.repo,
|
||||
owner: challenges.owner,
|
||||
repoArray: challenges.repoArray,
|
||||
githubAccessToken: challenges.githubAccessToken,
|
||||
out: path.join(outDir, 'challenges.json'),
|
||||
})
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
type GithubProject = {
|
||||
name: string
|
||||
}
|
||||
|
||||
type GithubUser = {
|
||||
login: string
|
||||
avatarUrl: string
|
||||
}
|
||||
|
||||
type GithubComment = {
|
||||
id: string
|
||||
author: string
|
||||
body: string
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
type GithubAssignee = {
|
||||
login: string
|
||||
avatarUrl: string
|
||||
}
|
||||
|
||||
export type GithubIssue = {
|
||||
id: string
|
||||
title: string
|
||||
body?: string
|
||||
url: string
|
||||
user: GithubUser
|
||||
labels: string[]
|
||||
commentCount: number
|
||||
comments: GithubComment[]
|
||||
assignees: GithubAssignee[]
|
||||
milestone?: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
projects: GithubProject[]
|
||||
}
|
||||
|
||||
export type GithubIssuesPerRepo = {
|
||||
[key: string]: GithubIssue[]
|
||||
}
|
||||
|
||||
export type RepoOwnerArray = {
|
||||
repo: string
|
||||
owner: string
|
||||
}[]
|
||||
@@ -0,0 +1,134 @@
|
||||
import {
|
||||
GithubIssue,
|
||||
GithubIssuesPerRepo,
|
||||
RepoOwnerArray,
|
||||
} from '../types/github'
|
||||
import fetch from 'node-fetch'
|
||||
|
||||
const https = require('https')
|
||||
|
||||
// Inspired by:
|
||||
// https://github.com/acid-info/rnd-dynamic-list/blob/master/challenges/fetch-challenges-multiple-private-graphql.js
|
||||
|
||||
const sanitizeAlias = (str) => str.replace(/-/g, '_')
|
||||
|
||||
const createQueryPart = (owner, repo) => {
|
||||
const alias = sanitizeAlias(`${owner}_${repo}`)
|
||||
return `
|
||||
${alias}: repository(owner: "${owner}", name: "${repo}") {
|
||||
issues(first: 100, states: OPEN) {
|
||||
nodes {
|
||||
id
|
||||
title
|
||||
url
|
||||
author {
|
||||
login
|
||||
avatarUrl
|
||||
}
|
||||
labels(first: 10) {
|
||||
nodes {
|
||||
name
|
||||
}
|
||||
}
|
||||
commentCount: comments {
|
||||
totalCount
|
||||
}
|
||||
commentsDetailed: comments(first: 10) {
|
||||
nodes {
|
||||
id
|
||||
author {
|
||||
login
|
||||
}
|
||||
body
|
||||
createdAt
|
||||
}
|
||||
}
|
||||
assignees(first: 10) {
|
||||
nodes {
|
||||
login
|
||||
avatarUrl
|
||||
}
|
||||
}
|
||||
milestone {
|
||||
title
|
||||
}
|
||||
createdAt
|
||||
updatedAt
|
||||
projectCards(first: 10) {
|
||||
nodes {
|
||||
project {
|
||||
name
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
export const fetchGithubIssues = async (
|
||||
repoOwnerArray: RepoOwnerArray,
|
||||
githubAccessToken: string,
|
||||
): Promise<GithubIssuesPerRepo[]> => {
|
||||
let query = '{'
|
||||
repoOwnerArray.forEach(({ owner, repo }) => {
|
||||
query += createQueryPart(owner, repo)
|
||||
})
|
||||
query += '}'
|
||||
|
||||
const response = await fetch('https://api.github.com/graphql', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'User-Agent': 'NodeJS-Script',
|
||||
Authorization: `Bearer ${githubAccessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ query }),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`GitHub API responded with status ${response.status}`)
|
||||
}
|
||||
|
||||
const responseData = await response.json()
|
||||
|
||||
const parsedIssues: GithubIssuesPerRepo[] = repoOwnerArray.map(
|
||||
({ owner, repo }) => {
|
||||
const alias = sanitizeAlias(`${owner}_${repo}`)
|
||||
const rawIssues = responseData.data[alias]?.issues.nodes || []
|
||||
return {
|
||||
[`${owner}/${repo}`]: rawIssues.map(
|
||||
(issue): GithubIssue => ({
|
||||
id: issue.id,
|
||||
title: issue.title,
|
||||
url: issue.url,
|
||||
user: {
|
||||
login: issue.author.login,
|
||||
avatarUrl: issue.author.avatarUrl,
|
||||
},
|
||||
labels: issue.labels.nodes.map((label) => label.name),
|
||||
commentCount: issue.commentCount.totalCount,
|
||||
comments: issue.commentsDetailed.nodes.map((comment) => ({
|
||||
id: comment.id,
|
||||
author: comment.author.login,
|
||||
body: comment.body,
|
||||
createdAt: comment.createdAt,
|
||||
})),
|
||||
assignees: issue.assignees.nodes,
|
||||
milestone: issue.milestone ? issue.milestone.title : null,
|
||||
created_at: issue.createdAt,
|
||||
updated_at: issue.updatedAt,
|
||||
projects: issue.projectCards.nodes.map((card) => ({
|
||||
name: card.project.name,
|
||||
url: card.project.url,
|
||||
})),
|
||||
}),
|
||||
),
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return parsedIssues
|
||||
}
|
||||
@@ -21,11 +21,17 @@ const schema = Joi.object<PluginOptions>({
|
||||
generated: Joi.object<GeneratedDataPluginConfig>({
|
||||
challenges: Joi.object<GeneratedDataPluginConfig['challenges']>({
|
||||
githubAccessToken: Joi.string().required(),
|
||||
owner: Joi.string().required(),
|
||||
repo: Joi.string().required(),
|
||||
repoArray: Joi.array()
|
||||
.items(
|
||||
Joi.object({
|
||||
repo: Joi.string().required(),
|
||||
owner: Joi.string().required(),
|
||||
}),
|
||||
)
|
||||
.required(),
|
||||
}).optional(),
|
||||
jobList: Joi.object<GeneratedDataPluginConfig['jobList']>({
|
||||
url: Joi.string().uri().required(),
|
||||
jobBoard: Joi.string().uri().required(),
|
||||
}).optional(),
|
||||
}).optional(),
|
||||
})
|
||||
|
||||
+45
-19
@@ -1,9 +1,38 @@
|
||||
import { Typography } from '@acid-info/lsd-react'
|
||||
import { SingleDepartmentJobs } from './SingleDepartmentJobs'
|
||||
import useFetchJobs, { JobDepartmentArray } from './useFetchJobs'
|
||||
import React from 'react'
|
||||
import { jobsPerDepartmentDummyData } from './jobsPerDepartmentDummyData'
|
||||
|
||||
export type JobDepartmentData = {
|
||||
id: number
|
||||
name: string
|
||||
parent_id: number | null
|
||||
child_ids: number[]
|
||||
jobs: Job[]
|
||||
}
|
||||
|
||||
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 JobsPerDepartmentHeaderProps = {
|
||||
message?: string
|
||||
}
|
||||
@@ -34,42 +63,39 @@ const hasJobs = (jobsPerDepartment: JobDepartmentArray): boolean => {
|
||||
}
|
||||
|
||||
type JobsPerDepartmentProps = React.HTMLAttributes<HTMLDivElement> & {
|
||||
jobBoard: string
|
||||
jobData: { departments?: JobDepartmentArray }
|
||||
titleFilter?: string
|
||||
fetchAll?: boolean
|
||||
useDummyData?: boolean
|
||||
}
|
||||
|
||||
export const JobsPerDepartment: React.FC<JobsPerDepartmentProps> = ({
|
||||
jobBoard,
|
||||
jobData,
|
||||
titleFilter = '',
|
||||
fetchAll = false,
|
||||
useDummyData = false,
|
||||
...props
|
||||
}) => {
|
||||
const { data, error, isLoading } = useFetchJobs(
|
||||
jobBoard,
|
||||
titleFilter,
|
||||
useDummyData ? jobsPerDepartmentDummyData : null,
|
||||
)
|
||||
let jobArray: JobDepartmentData[] | undefined | null = useDummyData
|
||||
? jobsPerDepartmentDummyData
|
||||
: jobData?.departments
|
||||
|
||||
if (isLoading) {
|
||||
// Skipping loading state for now, as per the designer's request.
|
||||
return <JobsPerDepartmentHeader />
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <JobsPerDepartmentHeader message="Error fetching data" />
|
||||
}
|
||||
|
||||
if (!data || !hasJobs(data)) {
|
||||
if (!jobArray || !hasJobs(jobArray)) {
|
||||
return <JobsPerDepartmentHeader message="No job openings to show" />
|
||||
}
|
||||
|
||||
// Filter jobs in each department based on titleFilter
|
||||
if (jobArray && titleFilter) {
|
||||
jobArray = jobArray.map((department) => ({
|
||||
...department,
|
||||
jobs: department.jobs.filter((job) => job.title.includes(titleFilter)),
|
||||
}))
|
||||
}
|
||||
|
||||
return (
|
||||
<div {...props}>
|
||||
<JobsPerDepartmentHeader />
|
||||
{data.map((department) => {
|
||||
{jobArray.map((department) => {
|
||||
return (
|
||||
<SingleDepartmentJobs key={department.name} department={department} />
|
||||
)
|
||||
|
||||
+1
-1
@@ -1,9 +1,9 @@
|
||||
import { Typography } from '@acid-info/lsd-react'
|
||||
import { JobDepartmentData } from './useFetchJobs'
|
||||
import React from 'react'
|
||||
import { IconExternalLink } from '@logos-theme/components/Icon'
|
||||
import './JobsPerDepartment.scss'
|
||||
import Link from '@docusaurus/Link'
|
||||
import { JobDepartmentData } from '.'
|
||||
|
||||
type SingleDepartmentJobsProps = {
|
||||
department: JobDepartmentData
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { JobDepartmentArray } from './useFetchJobs'
|
||||
import { JobDepartmentArray } from '.'
|
||||
|
||||
// Only for dev purposes - prevents making multiple requests to the API while developing.
|
||||
export const jobsPerDepartmentDummyData: JobDepartmentArray = [
|
||||
|
||||
-161
@@ -1,161 +0,0 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
/*
|
||||
// Here's an array with some 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<JobDepartmentArray>(null)
|
||||
const [error, setError] = useState<Error | null>(null)
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false)
|
||||
const isMounted = useRef(true)
|
||||
|
||||
const fetchJobs = async (): Promise<{
|
||||
jobsPerDepartment: JobDepartmentArray
|
||||
errorData: Error | null
|
||||
}> => {
|
||||
try {
|
||||
if (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
|
||||
@@ -12461,6 +12461,13 @@ node-emoji@^1.10.0:
|
||||
dependencies:
|
||||
lodash "^4.17.21"
|
||||
|
||||
node-fetch@2:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
|
||||
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
|
||||
dependencies:
|
||||
whatwg-url "^5.0.0"
|
||||
|
||||
node-fetch@2.6.7:
|
||||
version "2.6.7"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
|
||||
|
||||
Reference in New Issue
Block a user