webhook sections combined
This commit is contained in:
parent
a81b1f0e58
commit
31dfc7afd6
@ -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}
|
||||
/>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user