diff --git a/orchestrator/src/client/pages/SettingsPage.tsx b/orchestrator/src/client/pages/SettingsPage.tsx
index 49a1506..37b4c90 100644
--- a/orchestrator/src/client/pages/SettingsPage.tsx
+++ b/orchestrator/src/client/pages/SettingsPage.tsx
@@ -129,12 +129,12 @@ const getDerivedSettings = (settings: AppSettings | null) => {
default: settings?.defaultJobCompleteWebhookUrl ?? "",
},
ukvisajobs: {
- effectiveMaxJobs: settings?.ukvisajobsMaxJobs ?? 50,
- defaultMaxJobs: settings?.defaultUkvisajobsMaxJobs ?? 50,
+ effective: settings?.ukvisajobsMaxJobs ?? 50,
+ default: settings?.defaultUkvisajobsMaxJobs ?? 50,
},
gradcracker: {
- effectiveMaxJobsPerTerm: settings?.gradcrackerMaxJobsPerTerm ?? 50,
- defaultMaxJobsPerTerm: settings?.defaultGradcrackerMaxJobsPerTerm ?? 50,
+ effective: settings?.gradcrackerMaxJobsPerTerm ?? 50,
+ default: settings?.defaultGradcrackerMaxJobsPerTerm ?? 50,
},
searchTerms: {
effective: settings?.searchTerms ?? [],
@@ -167,8 +167,8 @@ const getDerivedSettings = (settings: AppSettings | null) => {
},
},
display: {
- effectiveShowSponsorInfo: settings?.showSponsorInfo ?? true,
- defaultShowSponsorInfo: settings?.defaultShowSponsorInfo ?? true,
+ effective: settings?.showSponsorInfo ?? true,
+ default: settings?.defaultShowSponsorInfo ?? true,
},
defaultResumeProjects: settings?.defaultResumeProjects ?? null,
profileProjects,
@@ -253,8 +253,8 @@ export const SettingsPage: React.FC = () => {
pipelineWebhookUrl: normalizeString(data.pipelineWebhookUrl),
jobCompleteWebhookUrl: normalizeString(data.jobCompleteWebhookUrl),
resumeProjects: resumeProjectsOverride,
- ukvisajobsMaxJobs: nullIfSame(data.ukvisajobsMaxJobs, ukvisajobs.defaultMaxJobs),
- gradcrackerMaxJobsPerTerm: nullIfSame(data.gradcrackerMaxJobsPerTerm, gradcracker.defaultMaxJobsPerTerm),
+ ukvisajobsMaxJobs: nullIfSame(data.ukvisajobsMaxJobs, ukvisajobs.default),
+ gradcrackerMaxJobsPerTerm: nullIfSame(data.gradcrackerMaxJobsPerTerm, gradcracker.default),
searchTerms: nullIfSameList(data.searchTerms, searchTerms.default),
jobspyLocation: nullIfSame(data.jobspyLocation, jobspy.location.default),
jobspyResultsWanted: nullIfSame(data.jobspyResultsWanted, jobspy.resultsWanted.default),
@@ -265,7 +265,7 @@ export const SettingsPage: React.FC = () => {
data.jobspyLinkedinFetchDescription,
jobspy.linkedinFetchDescription.default
),
- showSponsorInfo: nullIfSame(data.showSponsorInfo, display.defaultShowSponsorInfo),
+ showSponsorInfo: nullIfSame(data.showSponsorInfo, display.default),
}
const updated = await api.updateSettings(payload)
@@ -361,57 +361,37 @@ export const SettingsPage: React.FC = () => {
@@ -423,8 +403,7 @@ export const SettingsPage: React.FC = () => {
isSaving={isSaving}
/>
diff --git a/orchestrator/src/client/pages/settings/components/DisplaySettingsSection.tsx b/orchestrator/src/client/pages/settings/components/DisplaySettingsSection.tsx
index 60c1e6d..00ee4ab 100644
--- a/orchestrator/src/client/pages/settings/components/DisplaySettingsSection.tsx
+++ b/orchestrator/src/client/pages/settings/components/DisplaySettingsSection.tsx
@@ -5,20 +5,20 @@ import { AccordionContent, AccordionItem, AccordionTrigger } from "@/components/
import { Checkbox } from "@/components/ui/checkbox"
import { Separator } from "@/components/ui/separator"
import { UpdateSettingsInput } from "@shared/settings-schema"
+import type { DisplayValues } from "@client/pages/settings/types"
type DisplaySettingsSectionProps = {
- defaultShowSponsorInfo: boolean
- effectiveShowSponsorInfo: boolean
+ values: DisplayValues
isLoading: boolean
isSaving: boolean
}
export const DisplaySettingsSection: React.FC = ({
- defaultShowSponsorInfo,
- effectiveShowSponsorInfo,
+ values,
isLoading,
isSaving,
}) => {
+ const { default: defaultShowSponsorInfo, effective: effectiveShowSponsorInfo } = values
const { control } = useFormContext()
return (
@@ -79,4 +79,3 @@ export const DisplaySettingsSection: React.FC = ({
)
}
-
diff --git a/orchestrator/src/client/pages/settings/components/GradcrackerSection.tsx b/orchestrator/src/client/pages/settings/components/GradcrackerSection.tsx
index 8085491..1c34166 100644
--- a/orchestrator/src/client/pages/settings/components/GradcrackerSection.tsx
+++ b/orchestrator/src/client/pages/settings/components/GradcrackerSection.tsx
@@ -5,20 +5,20 @@ import { AccordionContent, AccordionItem, AccordionTrigger } from "@/components/
import { Input } from "@/components/ui/input"
import { Separator } from "@/components/ui/separator"
import { UpdateSettingsInput } from "@shared/settings-schema"
+import type { NumericSettingValues } from "@client/pages/settings/types"
type GradcrackerSectionProps = {
- defaultGradcrackerMaxJobsPerTerm: number
- effectiveGradcrackerMaxJobsPerTerm: number
+ values: NumericSettingValues
isLoading: boolean
isSaving: boolean
}
export const GradcrackerSection: React.FC = ({
- defaultGradcrackerMaxJobsPerTerm,
- effectiveGradcrackerMaxJobsPerTerm,
+ values,
isLoading,
isSaving,
}) => {
+ const { effective: effectiveGradcrackerMaxJobsPerTerm, default: defaultGradcrackerMaxJobsPerTerm } = values
const { control, formState: { errors } } = useFormContext()
return (
@@ -75,4 +75,3 @@ export const GradcrackerSection: React.FC = ({
)
}
-
diff --git a/orchestrator/src/client/pages/settings/components/JobCompleteWebhookSection.tsx b/orchestrator/src/client/pages/settings/components/JobCompleteWebhookSection.tsx
index 3b8b5f9..1d4b775 100644
--- a/orchestrator/src/client/pages/settings/components/JobCompleteWebhookSection.tsx
+++ b/orchestrator/src/client/pages/settings/components/JobCompleteWebhookSection.tsx
@@ -5,20 +5,20 @@ import { AccordionContent, AccordionItem, AccordionTrigger } from "@/components/
import { Input } from "@/components/ui/input"
import { Separator } from "@/components/ui/separator"
import { UpdateSettingsInput } from "@shared/settings-schema"
+import type { WebhookValues } from "@client/pages/settings/types"
type JobCompleteWebhookSectionProps = {
- defaultJobCompleteWebhookUrl: string
- effectiveJobCompleteWebhookUrl: string
+ values: WebhookValues
isLoading: boolean
isSaving: boolean
}
export const JobCompleteWebhookSection: React.FC = ({
- defaultJobCompleteWebhookUrl,
- effectiveJobCompleteWebhookUrl,
+ values,
isLoading,
isSaving,
}) => {
+ const { default: defaultJobCompleteWebhookUrl, effective: effectiveJobCompleteWebhookUrl } = values
const { register, formState: { errors } } = useFormContext()
return (
@@ -58,4 +58,3 @@ export const JobCompleteWebhookSection: React.FC
)
}
-
diff --git a/orchestrator/src/client/pages/settings/components/JobspySection.test.tsx b/orchestrator/src/client/pages/settings/components/JobspySection.test.tsx
index a4d2960..bba0bdf 100644
--- a/orchestrator/src/client/pages/settings/components/JobspySection.test.tsx
+++ b/orchestrator/src/client/pages/settings/components/JobspySection.test.tsx
@@ -22,18 +22,14 @@ const JobspyHarness = () => {
diff --git a/orchestrator/src/client/pages/settings/components/JobspySection.tsx b/orchestrator/src/client/pages/settings/components/JobspySection.tsx
index 7a41c68..9af1b53 100644
--- a/orchestrator/src/client/pages/settings/components/JobspySection.tsx
+++ b/orchestrator/src/client/pages/settings/components/JobspySection.tsx
@@ -6,40 +6,27 @@ import { Checkbox } from "@/components/ui/checkbox"
import { Input } from "@/components/ui/input"
import { Separator } from "@/components/ui/separator"
import { UpdateSettingsInput } from "@shared/settings-schema"
+import type { JobspyValues } from "@client/pages/settings/types"
type JobspySectionProps = {
- defaultJobspySites: string[]
- effectiveJobspySites: string[]
- defaultJobspyLocation: string
- effectiveJobspyLocation: string
- defaultJobspyResultsWanted: number
- effectiveJobspyResultsWanted: number
- defaultJobspyHoursOld: number
- effectiveJobspyHoursOld: number
- defaultJobspyCountryIndeed: string
- effectiveJobspyCountryIndeed: string
- defaultJobspyLinkedinFetchDescription: boolean
- effectiveJobspyLinkedinFetchDescription: boolean
+ values: JobspyValues
isLoading: boolean
isSaving: boolean
}
export const JobspySection: React.FC = ({
- defaultJobspySites,
- effectiveJobspySites,
- defaultJobspyLocation,
- effectiveJobspyLocation,
- defaultJobspyResultsWanted,
- effectiveJobspyResultsWanted,
- defaultJobspyHoursOld,
- effectiveJobspyHoursOld,
- defaultJobspyCountryIndeed,
- effectiveJobspyCountryIndeed,
- defaultJobspyLinkedinFetchDescription,
- effectiveJobspyLinkedinFetchDescription,
+ values,
isLoading,
isSaving,
}) => {
+ const {
+ sites,
+ location,
+ resultsWanted,
+ hoursOld,
+ countryIndeed,
+ linkedinFetchDescription,
+ } = values
const { control, register, formState: { errors } } = useFormContext()
return (
@@ -59,9 +46,9 @@ export const JobspySection: React.FC = ({
render={({ field }) => (
{
- const current = field.value ?? defaultJobspySites
+ const current = field.value ?? sites.default
let next = [...current]
if (checked) {
if (!next.includes('indeed')) next.push('indeed')
@@ -83,9 +70,9 @@ export const JobspySection: React.FC = ({
render={({ field }) => (
{
- const current = field.value ?? defaultJobspySites
+ const current = field.value ?? sites.default
let next = [...current]
if (checked) {
if (!next.includes('linkedin')) next.push('linkedin')
@@ -106,8 +93,8 @@ export const JobspySection: React.FC = ({
Select which sites JobSpy should scrape.
- Effective: {(effectiveJobspySites || []).join(', ') || "None"}
- Default: {(defaultJobspySites || []).join(', ')}
+ Effective: {(sites.effective || []).join(', ') || "None"}
+ Default: {(sites.default || []).join(', ')}
@@ -116,7 +103,7 @@ export const JobspySection: React.FC = ({
Location
{errors.jobspyLocation && {errors.jobspyLocation.message}
}
@@ -124,8 +111,8 @@ export const JobspySection: React.FC = ({
Location to search for jobs (e.g. "UK", "London", "Remote").
- Effective: {effectiveJobspyLocation || "—"}
- Default: {defaultJobspyLocation || "—"}
+ Effective: {location.effective || "—"}
+ Default: {location.default || "—"}
@@ -158,8 +145,8 @@ export const JobspySection: React.FC = ({
Number of results to fetch per term per site. Max 1000.
- Effective: {effectiveJobspyResultsWanted}
- Default: {defaultJobspyResultsWanted}
+ Effective: {resultsWanted.effective}
+ Default: {resultsWanted.default}
@@ -192,8 +179,8 @@ export const JobspySection: React.FC = ({
Max age of jobs in hours (e.g. 72 for 3 days). Max 720 (30 days).
- Effective: {effectiveJobspyHoursOld}h
- Default: {defaultJobspyHoursOld}h
+ Effective: {hoursOld.effective}h
+ Default: {hoursOld.default}h
@@ -201,7 +188,7 @@ export const JobspySection: React.FC = ({
Indeed Country
{errors.jobspyCountryIndeed && {errors.jobspyCountryIndeed.message}
}
@@ -209,8 +196,8 @@ export const JobspySection: React.FC = ({
Country domain for Indeed (e.g. "UK" for indeed.co.uk).
- Effective: {effectiveJobspyCountryIndeed || "—"}
- Default: {defaultJobspyCountryIndeed || "—"}
+ Effective: {countryIndeed.effective || "—"}
+ Default: {countryIndeed.default || "—"}
@@ -224,7 +211,7 @@ export const JobspySection: React.FC = ({
render={({ field }) => (
field.onChange(!!checked)}
disabled={isLoading || isSaving}
/>
@@ -241,8 +228,8 @@ export const JobspySection: React.FC = ({
If enabled, JobSpy will make extra requests to fetch full descriptions. Slower but better data.
- Effective: {effectiveJobspyLinkedinFetchDescription ? "Yes" : "No"}
- Default: {defaultJobspyLinkedinFetchDescription ? "Yes" : "No"}
+ Effective: {linkedinFetchDescription.effective ? "Yes" : "No"}
+ Default: {linkedinFetchDescription.default ? "Yes" : "No"}
@@ -251,4 +238,3 @@ export const JobspySection: React.FC = ({
)
}
-
diff --git a/orchestrator/src/client/pages/settings/components/ModelSettingsSection.tsx b/orchestrator/src/client/pages/settings/components/ModelSettingsSection.tsx
index f37ca99..b08e414 100644
--- a/orchestrator/src/client/pages/settings/components/ModelSettingsSection.tsx
+++ b/orchestrator/src/client/pages/settings/components/ModelSettingsSection.tsx
@@ -5,26 +5,20 @@ import { AccordionContent, AccordionItem, AccordionTrigger } from "@/components/
import { Input } from "@/components/ui/input"
import { Separator } from "@/components/ui/separator"
import { UpdateSettingsInput } from "@shared/settings-schema"
+import type { ModelValues } from "@client/pages/settings/types"
type ModelSettingsSectionProps = {
- effectiveModel: string
- effectiveModelScorer: string
- effectiveModelTailoring: string
- effectiveModelProjectSelection: string
- defaultModel: string
+ values: ModelValues
isLoading: boolean
isSaving: boolean
}
export const ModelSettingsSection: React.FC = ({
- effectiveModel,
- effectiveModelScorer,
- effectiveModelTailoring,
- effectiveModelProjectSelection,
- defaultModel,
+ values,
isLoading,
isSaving,
}) => {
+ const { effective, default: defaultModel, scorer, tailoring, projectSelection } = values
const { register, formState: { errors } } = useFormContext()
return (
@@ -57,12 +51,12 @@ export const ModelSettingsSection: React.FC = ({
Scoring Model
{errors.modelScorer && {errors.modelScorer.message}
}
- Effective: {effectiveModelScorer || effectiveModel}
+ Effective: {scorer || effective}
@@ -70,12 +64,12 @@ export const ModelSettingsSection: React.FC = ({
Tailoring Model
{errors.modelTailoring && {errors.modelTailoring.message}
}
- Effective: {effectiveModelTailoring || effectiveModel}
+ Effective: {tailoring || effective}
@@ -83,12 +77,12 @@ export const ModelSettingsSection: React.FC = ({
Project Selection Model
{errors.modelProjectSelection && {errors.modelProjectSelection.message}
}
- Effective: {effectiveModelProjectSelection || effectiveModel}
+ Effective: {projectSelection || effective}
@@ -99,7 +93,7 @@ export const ModelSettingsSection: React.FC = ({
Global Effective
-
{effectiveModel || "—"}
+
{effective || "—"}
Default (env)
@@ -111,4 +105,3 @@ export const ModelSettingsSection: React.FC
= ({
)
}
-
diff --git a/orchestrator/src/client/pages/settings/components/PipelineWebhookSection.tsx b/orchestrator/src/client/pages/settings/components/PipelineWebhookSection.tsx
index 61e0ff4..8d92275 100644
--- a/orchestrator/src/client/pages/settings/components/PipelineWebhookSection.tsx
+++ b/orchestrator/src/client/pages/settings/components/PipelineWebhookSection.tsx
@@ -5,20 +5,20 @@ import { AccordionContent, AccordionItem, AccordionTrigger } from "@/components/
import { Input } from "@/components/ui/input"
import { Separator } from "@/components/ui/separator"
import { UpdateSettingsInput } from "@shared/settings-schema"
+import type { WebhookValues } from "@client/pages/settings/types"
type PipelineWebhookSectionProps = {
- defaultPipelineWebhookUrl: string
- effectivePipelineWebhookUrl: string
+ values: WebhookValues
isLoading: boolean
isSaving: boolean
}
export const PipelineWebhookSection: React.FC = ({
- defaultPipelineWebhookUrl,
- effectivePipelineWebhookUrl,
+ values,
isLoading,
isSaving,
}) => {
+ const { default: defaultPipelineWebhookUrl, effective: effectivePipelineWebhookUrl } = values
const { register, formState: { errors } } = useFormContext()
return (
@@ -58,4 +58,3 @@ export const PipelineWebhookSection: React.FC = ({
)
}
-
diff --git a/orchestrator/src/client/pages/settings/components/SearchTermsSection.tsx b/orchestrator/src/client/pages/settings/components/SearchTermsSection.tsx
index 48dc91a..595f517 100644
--- a/orchestrator/src/client/pages/settings/components/SearchTermsSection.tsx
+++ b/orchestrator/src/client/pages/settings/components/SearchTermsSection.tsx
@@ -4,20 +4,20 @@ import { useFormContext, Controller } from "react-hook-form"
import { AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion"
import { Separator } from "@/components/ui/separator"
import { UpdateSettingsInput } from "@shared/settings-schema"
+import type { SearchTermsValues } from "@client/pages/settings/types"
type SearchTermsSectionProps = {
- defaultSearchTerms: string[]
- effectiveSearchTerms: string[]
+ values: SearchTermsValues
isLoading: boolean
isSaving: boolean
}
export const SearchTermsSection: React.FC = ({
- defaultSearchTerms,
- effectiveSearchTerms,
+ values,
isLoading,
isSaving,
}) => {
+ const { default: defaultSearchTerms, effective: effectiveSearchTerms } = values
const { control, formState: { errors } } = useFormContext()
return (
@@ -75,4 +75,3 @@ export const SearchTermsSection: React.FC = ({
)
}
-
diff --git a/orchestrator/src/client/pages/settings/components/UkvisajobsSection.tsx b/orchestrator/src/client/pages/settings/components/UkvisajobsSection.tsx
index b980c88..3b7f0b0 100644
--- a/orchestrator/src/client/pages/settings/components/UkvisajobsSection.tsx
+++ b/orchestrator/src/client/pages/settings/components/UkvisajobsSection.tsx
@@ -5,20 +5,20 @@ import { AccordionContent, AccordionItem, AccordionTrigger } from "@/components/
import { Input } from "@/components/ui/input"
import { Separator } from "@/components/ui/separator"
import { UpdateSettingsInput } from "@shared/settings-schema"
+import type { NumericSettingValues } from "@client/pages/settings/types"
type UkvisajobsSectionProps = {
- defaultUkvisajobsMaxJobs: number
- effectiveUkvisajobsMaxJobs: number
+ values: NumericSettingValues
isLoading: boolean
isSaving: boolean
}
export const UkvisajobsSection: React.FC = ({
- defaultUkvisajobsMaxJobs,
- effectiveUkvisajobsMaxJobs,
+ values,
isLoading,
isSaving,
}) => {
+ const { effective: effectiveUkvisajobsMaxJobs, default: defaultUkvisajobsMaxJobs } = values
const { control, formState: { errors } } = useFormContext()
return (
@@ -75,4 +75,3 @@ export const UkvisajobsSection: React.FC = ({
)
}
-
diff --git a/orchestrator/src/client/pages/settings/types.ts b/orchestrator/src/client/pages/settings/types.ts
new file mode 100644
index 0000000..227be46
--- /dev/null
+++ b/orchestrator/src/client/pages/settings/types.ts
@@ -0,0 +1,24 @@
+export type EffectiveDefault = {
+ effective: T
+ default: T
+}
+
+export type ModelValues = EffectiveDefault & {
+ scorer: string
+ tailoring: string
+ projectSelection: string
+}
+
+export type WebhookValues = EffectiveDefault
+export type NumericSettingValues = EffectiveDefault
+export type SearchTermsValues = EffectiveDefault
+export type DisplayValues = EffectiveDefault
+
+export type JobspyValues = {
+ sites: EffectiveDefault
+ location: EffectiveDefault
+ resultsWanted: EffectiveDefault
+ hoursOld: EffectiveDefault
+ countryIndeed: EffectiveDefault
+ linkedinFetchDescription: EffectiveDefault
+}