ConusAI vendor v0.1.0-beta.114
ci / gate (push) Waiting to run

This commit is contained in:
ConusAI vendor v0.1.0-beta.114
2026-11-14 09:56:45 +00:00
commit 1ce8d23c35
867 changed files with 122104 additions and 0 deletions
+94
View File
@@ -0,0 +1,94 @@
"use client"
// Adds a contact point (an e-mail address, a phone number, a web address) to a party. The service
// checks the value against its kind and answers a malformed one in its own words.
import type { ProjectSlugs } from "@conusai-kit/ui"
import { describeError, useWrite } from "@conusai-kit/ui/data"
import {
CheckField,
FormLayout,
type FormValues,
PageSection,
SelectField,
serviceNotice,
TextField,
usePageNavigation,
} from "@conusai-kit/ui/patterns"
import { FieldGroup, FieldLegend, FieldSet } from "@conusai-kit/ui/primitives"
import { type ReactElement, useState } from "react"
import { CONTACT_KINDS, CONTACT_PURPOSES } from "../contract/constants.js"
export interface ContactFormLabels {
readonly heading: string
readonly kind: string
readonly value: string
readonly purposes: string
readonly primary: string
readonly submit: string
readonly busy: string
readonly kinds: Readonly<Record<(typeof CONTACT_KINDS)[number], string>>
readonly purposeNames: Readonly<Record<(typeof CONTACT_PURPOSES)[number], string>>
/** The text of each business refusal by problem code (`businessRefusalNotices` of `@conusai-kit/business/refusals`). */
readonly refusals: Readonly<Record<string, string>>
}
const INPUT_TYPE = { email: "email", phone: "tel", web: "url" } as const
export function ContactForm({
slugs,
partyId,
labels,
}: {
slugs: ProjectSlugs
partyId: string
labels: ContactFormLabels
}): ReactElement {
const navigation = usePageNavigation()
const add = useWrite<Record<string, unknown>, unknown>("crm.contact.add")
const [requestId, setRequestId] = useState(() => crypto.randomUUID())
const [kind, setKind] = useState<(typeof CONTACT_KINDS)[number]>("email")
const submit = async (values: FormValues): Promise<void> => {
await add.mutate({
...slugs,
partyId,
kind,
value: values.value,
purposes: CONTACT_PURPOSES.filter((purpose) => values[`purpose-${purpose}`] === "on"),
isPrimary: values.isPrimary === "on",
requestId,
})
setRequestId(crypto.randomUUID())
// The page is rendered on the server: a reload lists the new contact point.
await navigation.refresh()
}
return (
<PageSection title={labels.heading} variant="card">
<FormLayout
submitLabel={labels.submit}
busyLabel={labels.busy}
onSubmit={submit}
describeError={(error) => serviceNotice(error, labels.refusals)?.text ?? describeError(error)}
>
<SelectField
label={labels.kind}
value={kind}
onValueChange={(value) => setKind(CONTACT_KINDS.find((option) => option === value) ?? "email")}
options={CONTACT_KINDS.map((option) => ({ value: option, label: labels.kinds[option] }))}
/>
<TextField name="value" label={labels.value} type={INPUT_TYPE[kind]} required maxLength={256} />
<FieldSet>
<FieldLegend variant="label">{labels.purposes}</FieldLegend>
<FieldGroup>
{CONTACT_PURPOSES.map((purpose) => (
<CheckField key={purpose} name={`purpose-${purpose}`} label={labels.purposeNames[purpose]} />
))}
</FieldGroup>
</FieldSet>
<CheckField name="isPrimary" label={labels.primary} />
</FormLayout>
</PageSection>
)
}