{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "certificate-upload-form", "type": "registry:component", "title": "Certificate Upload Form", "description": "PFX/P12 upload form that parses an A1 certificate in the browser and returns profile metadata.", "registryDependencies": [ "badge", "button", "card", "input", "label" ], "dependencies": [ "@signature-kit/react", "@signature-kit/a1", "@tanstack/react-form@1.33.2", "lucide-react" ], "files": [ { "path": "registry/default/certificate-upload-form/certificate-upload-form.tsx", "type": "registry:component", "target": "@components/signature-kit/certificate-upload-form.tsx", "content": "\"use client\";\n\nimport type { A1CertificateProfile } from \"@signature-kit/a1/config\";\nimport { useA1Certificate } from \"@signature-kit/react/a1\";\nimport { Loader2, UploadCloud } from \"lucide-react\";\nimport * as React from \"react\";\nimport { useForm, useSelector } from \"@tanstack/react-form\";\n\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport { Card } from \"@/components/ui/card\";\nimport { Input } from \"@/components/ui/input\";\nimport { Label } from \"@/components/ui/label\";\nimport { cn } from \"@/lib/utils\";\n\nexport type CertificateUploadFormProps = {\n readonly onUpload: (profile: A1CertificateProfile, file: File) => void | Promise;\n readonly submitLabel?: string;\n readonly fileLabel?: string;\n readonly passwordLabel?: string;\n readonly className?: string;\n};\n\ntype CertificateUploadFormValues = {\n readonly file: File | null;\n readonly password: string;\n};\n\nconst certificateUploadFormDefaults: CertificateUploadFormValues = {\n file: null,\n password: \"\",\n};\n\nconst formatDate = (date: Date): string =>\n new Intl.DateTimeFormat(undefined, { dateStyle: \"medium\" }).format(date);\n\nexport function CertificateUploadForm({\n onUpload,\n submitLabel = \"Validate certificate\",\n fileLabel = \"A1 certificate (.pfx or .p12)\",\n passwordLabel = \"Certificate password\",\n className,\n}: CertificateUploadFormProps) {\n const certificate = useA1Certificate();\n const form = useForm({ defaultValues: certificateUploadFormDefaults });\n const file = useSelector(form.store, (state) => state.values.file);\n const password = useSelector(form.store, (state) => state.values.password);\n\n const submit = async (event: React.FormEvent) => {\n event.preventDefault();\n if (file === null || password.length === 0) return;\n const bytes = new Uint8Array(await file.arrayBuffer());\n const result = await certificate.load(bytes, password);\n if (result.ok) void onUpload(result.profile, file);\n };\n\n return (\n \n \n {(field) => (\n
\n \n \n \n \n field.handleChange(event.currentTarget.files?.item(0) ?? null)}\n />\n
\n )}\n
\n \n {(field) => (\n
\n \n field.handleChange(event.currentTarget.value)}\n />\n
\n )}\n
\n {certificate.error !== null ? (\n

\n {certificate.error.message}\n

\n ) : null}\n {certificate.profile !== null ? (\n \n
\n
\n

\n {certificate.profile.subject}\n

\n

{certificate.profile.issuer}

\n
\n \n Ready\n \n
\n
\n
\n
CPF/CNPJ
\n
{certificate.profile.document}
\n
\n
\n
Valid until
\n
{formatDate(certificate.profile.validTo)}
\n
\n
\n
\n ) : null}\n \n {certificate.status === \"loading\" ? (\n \n ) : null}\n {submitLabel}\n \n \n );\n}\n" } ] }