introduce EffectiveDefault<T>
This commit is contained in:
parent
eb41c6237b
commit
610fc48a91
@ -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 = () => {
|
||||
<main className="container mx-auto max-w-3xl space-y-6 px-4 py-6 pb-12">
|
||||
<Accordion type="multiple" className="w-full space-y-4">
|
||||
<ModelSettingsSection
|
||||
effectiveModel={model.effective}
|
||||
effectiveModelScorer={model.scorer}
|
||||
effectiveModelTailoring={model.tailoring}
|
||||
effectiveModelProjectSelection={model.projectSelection}
|
||||
defaultModel={model.default}
|
||||
values={model}
|
||||
isLoading={isLoading}
|
||||
isSaving={isSaving}
|
||||
/>
|
||||
<PipelineWebhookSection
|
||||
defaultPipelineWebhookUrl={pipelineWebhook.default}
|
||||
effectivePipelineWebhookUrl={pipelineWebhook.effective}
|
||||
values={pipelineWebhook}
|
||||
isLoading={isLoading}
|
||||
isSaving={isSaving}
|
||||
/>
|
||||
<JobCompleteWebhookSection
|
||||
defaultJobCompleteWebhookUrl={jobCompleteWebhook.default}
|
||||
effectiveJobCompleteWebhookUrl={jobCompleteWebhook.effective}
|
||||
values={jobCompleteWebhook}
|
||||
isLoading={isLoading}
|
||||
isSaving={isSaving}
|
||||
/>
|
||||
<UkvisajobsSection
|
||||
defaultUkvisajobsMaxJobs={ukvisajobs.defaultMaxJobs}
|
||||
effectiveUkvisajobsMaxJobs={ukvisajobs.effectiveMaxJobs}
|
||||
values={ukvisajobs}
|
||||
isLoading={isLoading}
|
||||
isSaving={isSaving}
|
||||
/>
|
||||
<GradcrackerSection
|
||||
defaultGradcrackerMaxJobsPerTerm={gradcracker.defaultMaxJobsPerTerm}
|
||||
effectiveGradcrackerMaxJobsPerTerm={gradcracker.effectiveMaxJobsPerTerm}
|
||||
values={gradcracker}
|
||||
isLoading={isLoading}
|
||||
isSaving={isSaving}
|
||||
/>
|
||||
<SearchTermsSection
|
||||
defaultSearchTerms={searchTerms.default}
|
||||
effectiveSearchTerms={searchTerms.effective}
|
||||
values={searchTerms}
|
||||
isLoading={isLoading}
|
||||
isSaving={isSaving}
|
||||
/>
|
||||
<JobspySection
|
||||
defaultJobspySites={jobspy.sites.default}
|
||||
effectiveJobspySites={jobspy.sites.effective}
|
||||
defaultJobspyLocation={jobspy.location.default}
|
||||
effectiveJobspyLocation={jobspy.location.effective}
|
||||
defaultJobspyResultsWanted={jobspy.resultsWanted.default}
|
||||
effectiveJobspyResultsWanted={jobspy.resultsWanted.effective}
|
||||
defaultJobspyHoursOld={jobspy.hoursOld.default}
|
||||
effectiveJobspyHoursOld={jobspy.hoursOld.effective}
|
||||
defaultJobspyCountryIndeed={jobspy.countryIndeed.default}
|
||||
effectiveJobspyCountryIndeed={jobspy.countryIndeed.effective}
|
||||
defaultJobspyLinkedinFetchDescription={jobspy.linkedinFetchDescription.default}
|
||||
effectiveJobspyLinkedinFetchDescription={jobspy.linkedinFetchDescription.effective}
|
||||
values={jobspy}
|
||||
isLoading={isLoading}
|
||||
isSaving={isSaving}
|
||||
/>
|
||||
@ -423,8 +403,7 @@ export const SettingsPage: React.FC = () => {
|
||||
isSaving={isSaving}
|
||||
/>
|
||||
<DisplaySettingsSection
|
||||
defaultShowSponsorInfo={display.defaultShowSponsorInfo}
|
||||
effectiveShowSponsorInfo={display.effectiveShowSponsorInfo}
|
||||
values={display}
|
||||
isLoading={isLoading}
|
||||
isSaving={isSaving}
|
||||
/>
|
||||
|
||||
@ -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<DisplaySettingsSectionProps> = ({
|
||||
defaultShowSponsorInfo,
|
||||
effectiveShowSponsorInfo,
|
||||
values,
|
||||
isLoading,
|
||||
isSaving,
|
||||
}) => {
|
||||
const { default: defaultShowSponsorInfo, effective: effectiveShowSponsorInfo } = values
|
||||
const { control } = useFormContext<UpdateSettingsInput>()
|
||||
|
||||
return (
|
||||
@ -79,4 +79,3 @@ export const DisplaySettingsSection: React.FC<DisplaySettingsSectionProps> = ({
|
||||
</AccordionItem>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -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<GradcrackerSectionProps> = ({
|
||||
defaultGradcrackerMaxJobsPerTerm,
|
||||
effectiveGradcrackerMaxJobsPerTerm,
|
||||
values,
|
||||
isLoading,
|
||||
isSaving,
|
||||
}) => {
|
||||
const { effective: effectiveGradcrackerMaxJobsPerTerm, default: defaultGradcrackerMaxJobsPerTerm } = values
|
||||
const { control, formState: { errors } } = useFormContext<UpdateSettingsInput>()
|
||||
|
||||
return (
|
||||
@ -75,4 +75,3 @@ export const GradcrackerSection: React.FC<GradcrackerSectionProps> = ({
|
||||
</AccordionItem>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -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<JobCompleteWebhookSectionProps> = ({
|
||||
defaultJobCompleteWebhookUrl,
|
||||
effectiveJobCompleteWebhookUrl,
|
||||
values,
|
||||
isLoading,
|
||||
isSaving,
|
||||
}) => {
|
||||
const { default: defaultJobCompleteWebhookUrl, effective: effectiveJobCompleteWebhookUrl } = values
|
||||
const { register, formState: { errors } } = useFormContext<UpdateSettingsInput>()
|
||||
|
||||
return (
|
||||
@ -58,4 +58,3 @@ export const JobCompleteWebhookSection: React.FC<JobCompleteWebhookSectionProps>
|
||||
</AccordionItem>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -22,18 +22,14 @@ const JobspyHarness = () => {
|
||||
<FormProvider {...methods}>
|
||||
<Accordion type="multiple" defaultValue={["jobspy"]}>
|
||||
<JobspySection
|
||||
defaultJobspySites={["indeed", "linkedin"]}
|
||||
effectiveJobspySites={["indeed", "linkedin"]}
|
||||
defaultJobspyLocation="UK"
|
||||
effectiveJobspyLocation="UK"
|
||||
defaultJobspyResultsWanted={200}
|
||||
effectiveJobspyResultsWanted={200}
|
||||
defaultJobspyHoursOld={72}
|
||||
effectiveJobspyHoursOld={72}
|
||||
defaultJobspyCountryIndeed="UK"
|
||||
effectiveJobspyCountryIndeed="UK"
|
||||
defaultJobspyLinkedinFetchDescription={true}
|
||||
effectiveJobspyLinkedinFetchDescription={true}
|
||||
values={{
|
||||
sites: { default: ["indeed", "linkedin"], effective: ["indeed", "linkedin"] },
|
||||
location: { default: "UK", effective: "UK" },
|
||||
resultsWanted: { default: 200, effective: 200 },
|
||||
hoursOld: { default: 72, effective: 72 },
|
||||
countryIndeed: { default: "UK", effective: "UK" },
|
||||
linkedinFetchDescription: { default: true, effective: true },
|
||||
}}
|
||||
isLoading={false}
|
||||
isSaving={false}
|
||||
/>
|
||||
|
||||
@ -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<JobspySectionProps> = ({
|
||||
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<UpdateSettingsInput>()
|
||||
|
||||
return (
|
||||
@ -59,9 +46,9 @@ export const JobspySection: React.FC<JobspySectionProps> = ({
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
id="site-indeed"
|
||||
checked={field.value?.includes('indeed') ?? defaultJobspySites.includes('indeed')}
|
||||
checked={field.value?.includes('indeed') ?? sites.default.includes('indeed')}
|
||||
onCheckedChange={(checked) => {
|
||||
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<JobspySectionProps> = ({
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
id="site-linkedin"
|
||||
checked={field.value?.includes('linkedin') ?? defaultJobspySites.includes('linkedin')}
|
||||
checked={field.value?.includes('linkedin') ?? sites.default.includes('linkedin')}
|
||||
onCheckedChange={(checked) => {
|
||||
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<JobspySectionProps> = ({
|
||||
Select which sites JobSpy should scrape.
|
||||
</div>
|
||||
<div className="flex gap-2 text-xs text-muted-foreground">
|
||||
<span>Effective: {(effectiveJobspySites || []).join(', ') || "None"}</span>
|
||||
<span>Default: {(defaultJobspySites || []).join(', ')}</span>
|
||||
<span>Effective: {(sites.effective || []).join(', ') || "None"}</span>
|
||||
<span>Default: {(sites.default || []).join(', ')}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -116,7 +103,7 @@ export const JobspySection: React.FC<JobspySectionProps> = ({
|
||||
<div className="text-sm font-medium">Location</div>
|
||||
<Input
|
||||
{...register("jobspyLocation")}
|
||||
placeholder={defaultJobspyLocation || "UK"}
|
||||
placeholder={location.default || "UK"}
|
||||
disabled={isLoading || isSaving}
|
||||
/>
|
||||
{errors.jobspyLocation && <p className="text-xs text-destructive">{errors.jobspyLocation.message}</p>}
|
||||
@ -124,8 +111,8 @@ export const JobspySection: React.FC<JobspySectionProps> = ({
|
||||
Location to search for jobs (e.g. "UK", "London", "Remote").
|
||||
</div>
|
||||
<div className="flex gap-2 text-xs text-muted-foreground">
|
||||
<span>Effective: {effectiveJobspyLocation || "—"}</span>
|
||||
<span>Default: {defaultJobspyLocation || "—"}</span>
|
||||
<span>Effective: {location.effective || "—"}</span>
|
||||
<span>Default: {location.default || "—"}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -158,8 +145,8 @@ export const JobspySection: React.FC<JobspySectionProps> = ({
|
||||
Number of results to fetch per term per site. Max 1000.
|
||||
</div>
|
||||
<div className="flex gap-2 text-xs text-muted-foreground">
|
||||
<span>Effective: {effectiveJobspyResultsWanted}</span>
|
||||
<span>Default: {defaultJobspyResultsWanted}</span>
|
||||
<span>Effective: {resultsWanted.effective}</span>
|
||||
<span>Default: {resultsWanted.default}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -192,8 +179,8 @@ export const JobspySection: React.FC<JobspySectionProps> = ({
|
||||
Max age of jobs in hours (e.g. 72 for 3 days). Max 720 (30 days).
|
||||
</div>
|
||||
<div className="flex gap-2 text-xs text-muted-foreground">
|
||||
<span>Effective: {effectiveJobspyHoursOld}h</span>
|
||||
<span>Default: {defaultJobspyHoursOld}h</span>
|
||||
<span>Effective: {hoursOld.effective}h</span>
|
||||
<span>Default: {hoursOld.default}h</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -201,7 +188,7 @@ export const JobspySection: React.FC<JobspySectionProps> = ({
|
||||
<div className="text-sm font-medium">Indeed Country</div>
|
||||
<Input
|
||||
{...register("jobspyCountryIndeed")}
|
||||
placeholder={defaultJobspyCountryIndeed || "UK"}
|
||||
placeholder={countryIndeed.default || "UK"}
|
||||
disabled={isLoading || isSaving}
|
||||
/>
|
||||
{errors.jobspyCountryIndeed && <p className="text-xs text-destructive">{errors.jobspyCountryIndeed.message}</p>}
|
||||
@ -209,8 +196,8 @@ export const JobspySection: React.FC<JobspySectionProps> = ({
|
||||
Country domain for Indeed (e.g. "UK" for indeed.co.uk).
|
||||
</div>
|
||||
<div className="flex gap-2 text-xs text-muted-foreground">
|
||||
<span>Effective: {effectiveJobspyCountryIndeed || "—"}</span>
|
||||
<span>Default: {defaultJobspyCountryIndeed || "—"}</span>
|
||||
<span>Effective: {countryIndeed.effective || "—"}</span>
|
||||
<span>Default: {countryIndeed.default || "—"}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -224,7 +211,7 @@ export const JobspySection: React.FC<JobspySectionProps> = ({
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
id="linkedin-desc"
|
||||
checked={field.value ?? defaultJobspyLinkedinFetchDescription}
|
||||
checked={field.value ?? linkedinFetchDescription.default}
|
||||
onCheckedChange={(checked) => field.onChange(!!checked)}
|
||||
disabled={isLoading || isSaving}
|
||||
/>
|
||||
@ -241,8 +228,8 @@ export const JobspySection: React.FC<JobspySectionProps> = ({
|
||||
If enabled, JobSpy will make extra requests to fetch full descriptions. Slower but better data.
|
||||
</p>
|
||||
<div className="flex gap-2 text-xs text-muted-foreground">
|
||||
<span>Effective: {effectiveJobspyLinkedinFetchDescription ? "Yes" : "No"}</span>
|
||||
<span>Default: {defaultJobspyLinkedinFetchDescription ? "Yes" : "No"}</span>
|
||||
<span>Effective: {linkedinFetchDescription.effective ? "Yes" : "No"}</span>
|
||||
<span>Default: {linkedinFetchDescription.default ? "Yes" : "No"}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -251,4 +238,3 @@ export const JobspySection: React.FC<JobspySectionProps> = ({
|
||||
</AccordionItem>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -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<ModelSettingsSectionProps> = ({
|
||||
effectiveModel,
|
||||
effectiveModelScorer,
|
||||
effectiveModelTailoring,
|
||||
effectiveModelProjectSelection,
|
||||
defaultModel,
|
||||
values,
|
||||
isLoading,
|
||||
isSaving,
|
||||
}) => {
|
||||
const { effective, default: defaultModel, scorer, tailoring, projectSelection } = values
|
||||
const { register, formState: { errors } } = useFormContext<UpdateSettingsInput>()
|
||||
|
||||
return (
|
||||
@ -57,12 +51,12 @@ export const ModelSettingsSection: React.FC<ModelSettingsSectionProps> = ({
|
||||
<div className="text-sm">Scoring Model</div>
|
||||
<Input
|
||||
{...register("modelScorer")}
|
||||
placeholder={effectiveModel || "inherit"}
|
||||
placeholder={effective || "inherit"}
|
||||
disabled={isLoading || isSaving}
|
||||
/>
|
||||
{errors.modelScorer && <p className="text-xs text-destructive">{errors.modelScorer.message}</p>}
|
||||
<div className="text-xs text-muted-foreground">
|
||||
Effective: <span className="font-mono">{effectiveModelScorer || effectiveModel}</span>
|
||||
Effective: <span className="font-mono">{scorer || effective}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -70,12 +64,12 @@ export const ModelSettingsSection: React.FC<ModelSettingsSectionProps> = ({
|
||||
<div className="text-sm">Tailoring Model</div>
|
||||
<Input
|
||||
{...register("modelTailoring")}
|
||||
placeholder={effectiveModel || "inherit"}
|
||||
placeholder={effective || "inherit"}
|
||||
disabled={isLoading || isSaving}
|
||||
/>
|
||||
{errors.modelTailoring && <p className="text-xs text-destructive">{errors.modelTailoring.message}</p>}
|
||||
<div className="text-xs text-muted-foreground">
|
||||
Effective: <span className="font-mono">{effectiveModelTailoring || effectiveModel}</span>
|
||||
Effective: <span className="font-mono">{tailoring || effective}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -83,12 +77,12 @@ export const ModelSettingsSection: React.FC<ModelSettingsSectionProps> = ({
|
||||
<div className="text-sm">Project Selection Model</div>
|
||||
<Input
|
||||
{...register("modelProjectSelection")}
|
||||
placeholder={effectiveModel || "inherit"}
|
||||
placeholder={effective || "inherit"}
|
||||
disabled={isLoading || isSaving}
|
||||
/>
|
||||
{errors.modelProjectSelection && <p className="text-xs text-destructive">{errors.modelProjectSelection.message}</p>}
|
||||
<div className="text-xs text-muted-foreground">
|
||||
Effective: <span className="font-mono">{effectiveModelProjectSelection || effectiveModel}</span>
|
||||
Effective: <span className="font-mono">{projectSelection || effective}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -99,7 +93,7 @@ export const ModelSettingsSection: React.FC<ModelSettingsSectionProps> = ({
|
||||
<div className="grid gap-2 text-sm sm:grid-cols-2">
|
||||
<div>
|
||||
<div className="text-xs text-muted-foreground">Global Effective</div>
|
||||
<div className="break-words font-mono text-xs">{effectiveModel || "—"}</div>
|
||||
<div className="break-words font-mono text-xs">{effective || "—"}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xs text-muted-foreground">Default (env)</div>
|
||||
@ -111,4 +105,3 @@ export const ModelSettingsSection: React.FC<ModelSettingsSectionProps> = ({
|
||||
</AccordionItem>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -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<PipelineWebhookSectionProps> = ({
|
||||
defaultPipelineWebhookUrl,
|
||||
effectivePipelineWebhookUrl,
|
||||
values,
|
||||
isLoading,
|
||||
isSaving,
|
||||
}) => {
|
||||
const { default: defaultPipelineWebhookUrl, effective: effectivePipelineWebhookUrl } = values
|
||||
const { register, formState: { errors } } = useFormContext<UpdateSettingsInput>()
|
||||
|
||||
return (
|
||||
@ -58,4 +58,3 @@ export const PipelineWebhookSection: React.FC<PipelineWebhookSectionProps> = ({
|
||||
</AccordionItem>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -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<SearchTermsSectionProps> = ({
|
||||
defaultSearchTerms,
|
||||
effectiveSearchTerms,
|
||||
values,
|
||||
isLoading,
|
||||
isSaving,
|
||||
}) => {
|
||||
const { default: defaultSearchTerms, effective: effectiveSearchTerms } = values
|
||||
const { control, formState: { errors } } = useFormContext<UpdateSettingsInput>()
|
||||
|
||||
return (
|
||||
@ -75,4 +75,3 @@ export const SearchTermsSection: React.FC<SearchTermsSectionProps> = ({
|
||||
</AccordionItem>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -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<UkvisajobsSectionProps> = ({
|
||||
defaultUkvisajobsMaxJobs,
|
||||
effectiveUkvisajobsMaxJobs,
|
||||
values,
|
||||
isLoading,
|
||||
isSaving,
|
||||
}) => {
|
||||
const { effective: effectiveUkvisajobsMaxJobs, default: defaultUkvisajobsMaxJobs } = values
|
||||
const { control, formState: { errors } } = useFormContext<UpdateSettingsInput>()
|
||||
|
||||
return (
|
||||
@ -75,4 +75,3 @@ export const UkvisajobsSection: React.FC<UkvisajobsSectionProps> = ({
|
||||
</AccordionItem>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
24
orchestrator/src/client/pages/settings/types.ts
Normal file
24
orchestrator/src/client/pages/settings/types.ts
Normal file
@ -0,0 +1,24 @@
|
||||
export type EffectiveDefault<T> = {
|
||||
effective: T
|
||||
default: T
|
||||
}
|
||||
|
||||
export type ModelValues = EffectiveDefault<string> & {
|
||||
scorer: string
|
||||
tailoring: string
|
||||
projectSelection: string
|
||||
}
|
||||
|
||||
export type WebhookValues = EffectiveDefault<string>
|
||||
export type NumericSettingValues = EffectiveDefault<number>
|
||||
export type SearchTermsValues = EffectiveDefault<string[]>
|
||||
export type DisplayValues = EffectiveDefault<boolean>
|
||||
|
||||
export type JobspyValues = {
|
||||
sites: EffectiveDefault<string[]>
|
||||
location: EffectiveDefault<string>
|
||||
resultsWanted: EffectiveDefault<number>
|
||||
hoursOld: EffectiveDefault<number>
|
||||
countryIndeed: EffectiveDefault<string>
|
||||
linkedinFetchDescription: EffectiveDefault<boolean>
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user