webhook sections combined

This commit is contained in:
DaKheera47 2026-01-22 12:36:53 +00:00
parent a81b1f0e58
commit 31dfc7afd6
7 changed files with 135 additions and 106 deletions

View File

@ -16,10 +16,9 @@ import { DangerZoneSection } from "@client/pages/settings/components/DangerZoneS
import { DisplaySettingsSection } from "@client/pages/settings/components/DisplaySettingsSection"
import { EnvironmentSettingsSection } from "@client/pages/settings/components/EnvironmentSettingsSection"
import { GradcrackerSection } from "@client/pages/settings/components/GradcrackerSection"
import { JobCompleteWebhookSection } from "@client/pages/settings/components/JobCompleteWebhookSection"
import { JobspySection } from "@client/pages/settings/components/JobspySection"
import { ModelSettingsSection } from "@client/pages/settings/components/ModelSettingsSection"
import { PipelineWebhookSection } from "@client/pages/settings/components/PipelineWebhookSection"
import { WebhooksSection } from "@client/pages/settings/components/WebhooksSection"
import { ResumeProjectsSection } from "@client/pages/settings/components/ResumeProjectsSection"
import { SearchTermsSection } from "@client/pages/settings/components/SearchTermsSection"
import { UkvisajobsSection } from "@client/pages/settings/components/UkvisajobsSection"
@ -452,13 +451,10 @@ export const SettingsPage: React.FC = () => {
isLoading={isLoading}
isSaving={isSaving}
/>
<PipelineWebhookSection
values={pipelineWebhook}
isLoading={isLoading}
isSaving={isSaving}
/>
<JobCompleteWebhookSection
values={jobCompleteWebhook}
<WebhooksSection
pipelineWebhook={pipelineWebhook}
jobCompleteWebhook={jobCompleteWebhook}
webhookSecretHint={envSettings.private.webhookSecretHint}
isLoading={isLoading}
isSaving={isSaving}
/>

View File

@ -56,7 +56,6 @@ describe("EnvironmentSettingsSection", () => {
expect(screen.getByText("sk-1********")).toBeInTheDocument()
expect(screen.getByText("pass********")).toBeInTheDocument()
expect(screen.getByText("abcd********")).toBeInTheDocument()
expect(screen.getByText("sec-********")).toBeInTheDocument()
expect(screen.getByText("Not set")).toBeInTheDocument()
// Basic Auth

View File

@ -50,16 +50,6 @@ export const EnvironmentSettingsSection: React.FC<EnvironmentSettingsSectionProp
error={errors.openrouterApiKey?.message as string | undefined}
current={formatSecretHint(privateValues.openrouterApiKeyHint)}
/>
<SettingsInput
label="Webhook secret"
inputProps={register("webhookSecret")}
type="password"
placeholder="Enter new secret"
disabled={isLoading || isSaving}
error={errors.webhookSecret?.message as string | undefined}
current={formatSecretHint(privateValues.webhookSecretHint)}
/>
</div>
</div>

View File

@ -1,43 +0,0 @@
import React from "react"
import { useFormContext } from "react-hook-form"
import { AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion"
import { UpdateSettingsInput } from "@shared/settings-schema"
import type { WebhookValues } from "@client/pages/settings/types"
import { SettingsInput } from "@client/pages/settings/components/SettingsInput"
type JobCompleteWebhookSectionProps = {
values: WebhookValues
isLoading: boolean
isSaving: boolean
}
export const JobCompleteWebhookSection: React.FC<JobCompleteWebhookSectionProps> = ({
values,
isLoading,
isSaving,
}) => {
const { default: defaultJobCompleteWebhookUrl, effective: effectiveJobCompleteWebhookUrl } = values
const { register, formState: { errors } } = useFormContext<UpdateSettingsInput>()
return (
<AccordionItem value="job-complete-webhook" className="border rounded-lg px-4">
<AccordionTrigger className="hover:no-underline py-4">
<span className="text-base font-semibold">Job Complete Webhook</span>
</AccordionTrigger>
<AccordionContent className="pb-4">
<div className="space-y-4">
<SettingsInput
label="Job completion webhook URL"
inputProps={register("jobCompleteWebhookUrl")}
placeholder={defaultJobCompleteWebhookUrl || "https://..."}
disabled={isLoading || isSaving}
error={errors.jobCompleteWebhookUrl?.message as string | undefined}
helper={`When set, the server sends a POST when you mark a job as applied (includes the job description). Default: ${defaultJobCompleteWebhookUrl || "—"}.`}
current={effectiveJobCompleteWebhookUrl || "—"}
/>
</div>
</AccordionContent>
</AccordionItem>
)
}

View File

@ -1,43 +0,0 @@
import React from "react"
import { useFormContext } from "react-hook-form"
import { AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion"
import { UpdateSettingsInput } from "@shared/settings-schema"
import type { WebhookValues } from "@client/pages/settings/types"
import { SettingsInput } from "@client/pages/settings/components/SettingsInput"
type PipelineWebhookSectionProps = {
values: WebhookValues
isLoading: boolean
isSaving: boolean
}
export const PipelineWebhookSection: React.FC<PipelineWebhookSectionProps> = ({
values,
isLoading,
isSaving,
}) => {
const { default: defaultPipelineWebhookUrl, effective: effectivePipelineWebhookUrl } = values
const { register, formState: { errors } } = useFormContext<UpdateSettingsInput>()
return (
<AccordionItem value="pipeline-webhook" className="border rounded-lg px-4">
<AccordionTrigger className="hover:no-underline py-4">
<span className="text-base font-semibold">Pipeline Webhook</span>
</AccordionTrigger>
<AccordionContent className="pb-4">
<div className="space-y-4">
<SettingsInput
label="Pipeline status webhook URL"
inputProps={register("pipelineWebhookUrl")}
placeholder={defaultPipelineWebhookUrl || "https://..."}
disabled={isLoading || isSaving}
error={errors.pipelineWebhookUrl?.message as string | undefined}
helper={`When set, the server sends a POST on pipeline completion/failure. Default: ${defaultPipelineWebhookUrl || "—"}.`}
current={effectivePipelineWebhookUrl || "—"}
/>
</div>
</AccordionContent>
</AccordionItem>
)
}

View File

@ -0,0 +1,50 @@
import { render, screen } from "@testing-library/react"
import { useForm, FormProvider } from "react-hook-form"
import { Accordion } from "@/components/ui/accordion"
import { WebhooksSection } from "./WebhooksSection"
import { UpdateSettingsInput } from "@shared/settings-schema"
const WebhooksHarness = () => {
const methods = useForm<UpdateSettingsInput>({
defaultValues: {
pipelineWebhookUrl: "https://pipeline.com",
jobCompleteWebhookUrl: "https://job.com",
webhookSecret: "",
}
})
return (
<FormProvider {...methods}>
<Accordion type="multiple" defaultValue={["webhooks"]}>
<WebhooksSection
pipelineWebhook={{
default: "https://default-p.com",
effective: "https://pipeline.com",
}}
jobCompleteWebhook={{
default: "https://default-j.com",
effective: "https://job.com",
}}
webhookSecretHint="sec-"
isLoading={false}
isSaving={false}
/>
</Accordion>
</FormProvider>
)
}
describe("WebhooksSection", () => {
it("renders both webhook sections and the secret", () => {
render(<WebhooksHarness />)
expect(screen.getByText("Pipeline Status")).toBeInTheDocument()
expect(screen.getByText("Job Completion")).toBeInTheDocument()
expect(screen.getByDisplayValue("https://pipeline.com")).toBeInTheDocument()
expect(screen.getByDisplayValue("https://job.com")).toBeInTheDocument()
expect(screen.getByText("sec-********")).toBeInTheDocument()
})
})

View File

@ -0,0 +1,80 @@
import React from "react"
import { useFormContext } 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 { WebhookValues } from "@client/pages/settings/types"
import { SettingsInput } from "@client/pages/settings/components/SettingsInput"
type WebhooksSectionProps = {
pipelineWebhook: WebhookValues
jobCompleteWebhook: WebhookValues
webhookSecretHint: string | null
isLoading: boolean
isSaving: boolean
}
export const WebhooksSection: React.FC<WebhooksSectionProps> = ({
pipelineWebhook,
jobCompleteWebhook,
webhookSecretHint,
isLoading,
isSaving,
}) => {
const { register, formState: { errors } } = useFormContext<UpdateSettingsInput>()
const formatSecretHint = (hint: string | null) => (hint ? `${hint}********` : "Not set")
return (
<AccordionItem value="webhooks" className="border rounded-lg px-4">
<AccordionTrigger className="hover:no-underline py-4">
<span className="text-base font-semibold">Webhooks</span>
</AccordionTrigger>
<AccordionContent className="pb-4">
<div className="space-y-6">
<div className="space-y-4">
<div className="text-sm font-medium">Pipeline Status</div>
<SettingsInput
label="Webhook URL"
inputProps={register("pipelineWebhookUrl")}
placeholder={pipelineWebhook.default || "https://..."}
disabled={isLoading || isSaving}
error={errors.pipelineWebhookUrl?.message as string | undefined}
helper={`When set, the server sends a POST on pipeline completion/failure. Default: ${pipelineWebhook.default || "—"}.`}
current={pipelineWebhook.effective || "—"}
/>
</div>
<Separator />
<div className="space-y-4">
<div className="text-sm font-medium">Job Completion</div>
<div className="space-y-4">
<SettingsInput
label="Webhook URL"
inputProps={register("jobCompleteWebhookUrl")}
placeholder={jobCompleteWebhook.default || "https://..."}
disabled={isLoading || isSaving}
error={errors.jobCompleteWebhookUrl?.message as string | undefined}
helper={`When set, the server sends a POST when you mark a job as applied (includes the job description). Default: ${jobCompleteWebhook.default || "—"}.`}
current={jobCompleteWebhook.effective || "—"}
/>
<SettingsInput
label="Webhook Secret"
inputProps={register("webhookSecret")}
type="password"
placeholder="Enter new secret"
disabled={isLoading || isSaving}
error={errors.webhookSecret?.message as string | undefined}
helper="Secret sent to webhook"
current={formatSecretHint(webhookSecretHint)}
/>
</div>
</div>
</div>
</AccordionContent>
</AccordionItem>
)
}