uhc-portal-components-react 0.2.5 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.2.6 - 2026-10-09
6
+
7
+ - Add the opt-in `AnimalIdentityOnboardingV2` with responsive animal imagery,
8
+ collapsible identity/controller/evidence blocks and an explicit single legal
9
+ representative or controller confirmation while preserving the V1 payload.
10
+
5
11
  ## 0.2.5 - 2026-10-09
6
12
 
7
13
  - Render the protected organization target on a neutral light/dark panel with
@@ -0,0 +1,20 @@
1
+ import type { AnimalIdentityOnboardingCopy, AnimalIdentityOnboardingInput } from './AnimalIdentityOnboarding.js';
2
+ export type AnimalIdentityOnboardingV2Copy = AnimalIdentityOnboardingCopy & Readonly<{
3
+ controllerIsLegalRepresentative: string;
4
+ animalPreview: string;
5
+ }>;
6
+ /**
7
+ * Progressive animal onboarding presentation. It preserves the V1 payload so
8
+ * hosts can adopt it without changing evidence, authorization or persistence.
9
+ */
10
+ export declare function AnimalIdentityOnboardingV2({ copy, onSubmit, submitAction, speciesOptions, busy }: Readonly<{
11
+ copy: AnimalIdentityOnboardingV2Copy;
12
+ onSubmit: (input: AnimalIdentityOnboardingInput) => Promise<void> | void;
13
+ submitAction?: string;
14
+ speciesOptions?: readonly Readonly<{
15
+ taxonomyId: string;
16
+ label: string;
17
+ }>[];
18
+ busy?: boolean;
19
+ }>): import("react").JSX.Element;
20
+ //# sourceMappingURL=AnimalIdentityOnboardingV2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnimalIdentityOnboardingV2.d.ts","sourceRoot":"","sources":["../src/AnimalIdentityOnboardingV2.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,4BAA4B,EAC5B,6BAA6B,EAC9B,MAAM,+BAA+B,CAAA;AAEtC,MAAM,MAAM,8BAA8B,GAAG,4BAA4B,GAAG,QAAQ,CAAC;IACnF,+BAA+B,EAAE,MAAM,CAAA;IACvC,aAAa,EAAE,MAAM,CAAA;CACtB,CAAC,CAAA;AAEF;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAmB,EAAE,IAAY,EAAE,EAAE,QAAQ,CAAC;IACvH,IAAI,EAAE,8BAA8B,CAAA;IACpC,QAAQ,EAAE,CAAC,KAAK,EAAE,6BAA6B,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IACxE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,SAAS,QAAQ,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,CAAA;IAC3E,IAAI,CAAC,EAAE,OAAO,CAAA;CACf,CAAC,+BAgGD"}
@@ -0,0 +1,80 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ /**
4
+ * Progressive animal onboarding presentation. It preserves the V1 payload so
5
+ * hosts can adopt it without changing evidence, authorization or persistence.
6
+ */
7
+ export function AnimalIdentityOnboardingV2({ copy, onSubmit, submitAction, speciesOptions = [], busy = false }) {
8
+ const [submitting, setSubmitting] = useState(false);
9
+ const [animalName, setAnimalName] = useState('');
10
+ const [previewUrl, setPreviewUrl] = useState('');
11
+ useEffect(() => () => {
12
+ if (previewUrl.startsWith('blob:'))
13
+ URL.revokeObjectURL(previewUrl);
14
+ }, [previewUrl]);
15
+ function previewAnimal(file) {
16
+ if (previewUrl.startsWith('blob:'))
17
+ URL.revokeObjectURL(previewUrl);
18
+ if (!file?.type.startsWith('image/'))
19
+ return setPreviewUrl('');
20
+ if (typeof URL.createObjectURL === 'function')
21
+ return setPreviewUrl(URL.createObjectURL(file));
22
+ const reader = new FileReader();
23
+ reader.onload = () => setPreviewUrl(typeof reader.result === 'string' ? reader.result : '');
24
+ reader.readAsDataURL(file);
25
+ }
26
+ async function submit(event) {
27
+ event.preventDefault();
28
+ const formElement = event.currentTarget;
29
+ const form = new FormData(formElement);
30
+ setSubmitting(true);
31
+ try {
32
+ await onSubmit({
33
+ animalName: requiredText(form, 'animalName'),
34
+ taxonomyId: requiredText(form, 'taxonomyId'),
35
+ birthDate: optionalText(form, 'birthDate'),
36
+ sex: requiredText(form, 'sex'),
37
+ identifierSystem: requiredText(form, 'identifierSystem'),
38
+ identifierValue: requiredText(form, 'identifierValue'),
39
+ controllerLegalName: requiredText(form, 'controllerLegalName'),
40
+ controllerIdentifierType: requiredText(form, 'controllerIdentifierType'),
41
+ controllerIdentifierValue: requiredText(form, 'controllerIdentifierValue'),
42
+ controllerCountry: requiredText(form, 'controllerCountry').toUpperCase(),
43
+ relationship: requiredText(form, 'relationship'),
44
+ ownerDocument: requiredFile(formElement, 'ownerDocument'),
45
+ animalDocument: requiredFile(formElement, 'animalDocument'),
46
+ relationshipDocument: requiredFile(formElement, 'relationshipDocument'),
47
+ controllerPin: requiredText(form, 'controllerPin'),
48
+ });
49
+ }
50
+ finally {
51
+ setSubmitting(false);
52
+ }
53
+ }
54
+ const disabled = busy || submitting;
55
+ return _jsxs("section", { className: "uhcp-animal-onboarding uhcp-animal-onboarding-v2", "aria-labelledby": "animal-identity-onboarding-v2-title", children: [_jsxs("header", { children: [_jsx("h2", { id: "animal-identity-onboarding-v2-title", children: copy.title }), _jsx("p", { children: copy.intro })] }), _jsxs("form", { onSubmit: submit, children: [_jsxs("details", { open: true, children: [_jsxs("summary", { children: [_jsx(StepNumber, { value: 1 }), " ", copy.animalStep] }), _jsxs("div", { className: "uhcp-animal-onboarding-primary", children: [_jsxs("div", { className: "uhcp-animal-photo", children: [previewUrl
56
+ ? _jsx("img", { src: previewUrl, alt: copy.animalPreview.replace('{name}', animalName || copy.animalName) })
57
+ : _jsx("span", { "aria-hidden": "true", children: animalName.trim().slice(0, 1).toLocaleUpperCase() || '+' }), _jsxs("label", { children: [copy.animalDocument, _jsx("input", { name: "animalDocument", type: "file", accept: "application/pdf,image/jpeg,image/png", required: true, onChange: event => previewAnimal(event.currentTarget.files?.[0]) })] })] }), _jsxs("div", { className: "uhcp-animal-onboarding-fields", children: [_jsxs("label", { children: [copy.animalName, _jsx("input", { name: "animalName", required: true, maxLength: 80, value: animalName, onChange: event => setAnimalName(event.currentTarget.value) })] }), _jsxs("label", { children: [copy.taxonomyId, speciesOptions.length > 0
58
+ ? _jsx("select", { name: "taxonomyId", required: true, defaultValue: speciesOptions[0]?.taxonomyId, children: speciesOptions.map(option => _jsx("option", { value: option.taxonomyId, children: option.label }, option.taxonomyId)) })
59
+ : _jsx("input", { name: "taxonomyId", required: true, inputMode: "numeric", pattern: "[1-9][0-9]{0,6}", defaultValue: "9615" })] }), _jsxs("label", { children: [copy.birthDate, _jsx("input", { name: "birthDate", type: "date" })] }), _jsxs("label", { children: [copy.sex, _jsxs("select", { name: "sex", defaultValue: "unknown", children: [_jsx("option", { value: "unknown", children: copy.sexUnknown }), _jsx("option", { value: "female", children: copy.sexFemale }), _jsx("option", { value: "male", children: copy.sexMale }), _jsx("option", { value: "other", children: copy.sexOther })] })] }), _jsxs("label", { children: [copy.identifierSystem, _jsxs("select", { name: "identifierSystem", defaultValue: "urn:iso:std:iso:11784:11785", children: [_jsx("option", { value: "urn:iso:std:iso:11784:11785", children: "ISO 11784/11785" }), _jsx("option", { value: "https://europa.eu/pet-passport", children: "EU pet passport" }), _jsx("option", { value: "https://vetchain.app/identifier/tattoo", children: "Tattoo" })] })] }), _jsxs("label", { children: [copy.identifierValue, _jsx("input", { name: "identifierValue", required: true, maxLength: 80, autoComplete: "off" })] })] })] })] }), _jsxs("details", { children: [_jsxs("summary", { children: [_jsx(StepNumber, { value: 2 }), " ", copy.controllerStep] }), _jsxs("div", { className: "uhcp-animal-onboarding-fields", children: [_jsxs("label", { children: [copy.controllerLegalName, _jsx("input", { name: "controllerLegalName", required: true, maxLength: 160, autoComplete: "name" })] }), _jsxs("label", { children: [copy.controllerIdentifierType, _jsx("input", { name: "controllerIdentifierType", required: true, defaultValue: "national_identity_card", maxLength: 80 })] }), _jsxs("label", { children: [copy.controllerIdentifierValue, _jsx("input", { name: "controllerIdentifierValue", required: true, maxLength: 120, autoComplete: "off" })] }), _jsxs("label", { children: [copy.controllerCountry, _jsx("input", { name: "controllerCountry", required: true, pattern: "[A-Za-z]{2}", maxLength: 2, autoComplete: "country" })] }), _jsxs("label", { children: [copy.relationship, _jsxs("select", { name: "relationship", defaultValue: "owner", children: [_jsx("option", { value: "owner", children: copy.owner }), _jsx("option", { value: "caregiver", children: copy.caregiver })] })] }), _jsxs("label", { className: "uhcp-animal-controller-confirmation", children: [_jsx("input", { type: "checkbox", defaultChecked: true, required: true }), " ", copy.controllerIsLegalRepresentative] })] })] }), _jsxs("details", { children: [_jsxs("summary", { children: [_jsx(StepNumber, { value: 3 }), " ", copy.evidenceStep] }), _jsxs("div", { className: "uhcp-animal-onboarding-fields", children: [_jsxs("label", { children: [copy.ownerDocument, _jsx("input", { name: "ownerDocument", type: "file", accept: "application/pdf,image/jpeg,image/png", required: true })] }), _jsxs("label", { children: [copy.relationshipDocument, _jsx("input", { name: "relationshipDocument", type: "file", accept: "application/pdf,image/jpeg,image/png", required: true })] }), _jsxs("label", { children: [copy.controllerPin, _jsx("input", { name: "controllerPin", type: "password", inputMode: "numeric", pattern: "[0-9]{6,12}", required: true, autoComplete: "new-password" })] })] })] }), _jsx("button", { type: "submit", className: "uhcp-animal-onboarding-submit", "data-portal-action": submitAction, disabled: disabled, children: copy.submit })] })] });
60
+ }
61
+ function StepNumber({ value }) {
62
+ return _jsx("span", { className: "uhcp-identity-step-number", "aria-hidden": "true", children: value });
63
+ }
64
+ function requiredText(form, name) {
65
+ const value = form.get(name);
66
+ if (typeof value !== 'string' || !value.trim())
67
+ throw new Error(`animal_identity_${name}_required`);
68
+ return value.trim();
69
+ }
70
+ function optionalText(form, name) {
71
+ const value = form.get(name);
72
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
73
+ }
74
+ function requiredFile(form, name) {
75
+ const input = form.elements.namedItem(name);
76
+ const value = input instanceof HTMLInputElement ? input.files?.[0] : undefined;
77
+ if (!value || value.size < 1)
78
+ throw new Error(`animal_identity_${name}_required`);
79
+ return value;
80
+ }
package/dist/index.d.ts CHANGED
@@ -37,6 +37,7 @@ export * from './IdentityEvidenceWorkspace.js';
37
37
  export * from './IdentityEvidenceDocumentPanel.js';
38
38
  export * from './IdentityEvidenceDeclarationForm.js';
39
39
  export * from './AnimalIdentityOnboarding.js';
40
+ export * from './AnimalIdentityOnboardingV2.js';
40
41
  export * from './AnimalCardArtifact.js';
41
42
  export * from './AnimalControllerAttestationPanel.js';
42
43
  export * from './EmergencyTaskHub.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,yBAAyB,CAAA;AACvC,cAAc,mBAAmB,CAAA;AACjC,cAAc,2BAA2B,CAAA;AACzC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,4BAA4B,CAAA;AAC1C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,uBAAuB,CAAA;AACrC,cAAc,wCAAwC,CAAA;AACtD,cAAc,0BAA0B,CAAA;AACxC,cAAc,uBAAuB,CAAA;AACrC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,mCAAmC,CAAA;AACjD,cAAc,+BAA+B,CAAA;AAC7C,cAAc,qCAAqC,CAAA;AACnD,cAAc,mCAAmC,CAAA;AACjD,cAAc,gCAAgC,CAAA;AAC9C,cAAc,0BAA0B,CAAA;AACxC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,2BAA2B,CAAA;AACzC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,wBAAwB,CAAA;AACtC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,iCAAiC,CAAA;AAC/C,cAAc,0CAA0C,CAAA;AACxD,cAAc,kCAAkC,CAAA;AAChD,cAAc,0BAA0B,CAAA;AACxC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,oCAAoC,CAAA;AAClD,cAAc,sCAAsC,CAAA;AACpD,cAAc,+BAA+B,CAAA;AAC7C,cAAc,yBAAyB,CAAA;AACvC,cAAc,uCAAuC,CAAA;AACrD,cAAc,uBAAuB,CAAA;AACrC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,8BAA8B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,kBAAkB,CAAA;AAChC,cAAc,yBAAyB,CAAA;AACvC,cAAc,mBAAmB,CAAA;AACjC,cAAc,2BAA2B,CAAA;AACzC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,8BAA8B,CAAA;AAC5C,cAAc,4BAA4B,CAAA;AAC1C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,uBAAuB,CAAA;AACrC,cAAc,wCAAwC,CAAA;AACtD,cAAc,0BAA0B,CAAA;AACxC,cAAc,uBAAuB,CAAA;AACrC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,mCAAmC,CAAA;AACjD,cAAc,+BAA+B,CAAA;AAC7C,cAAc,qCAAqC,CAAA;AACnD,cAAc,mCAAmC,CAAA;AACjD,cAAc,gCAAgC,CAAA;AAC9C,cAAc,0BAA0B,CAAA;AACxC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,2BAA2B,CAAA;AACzC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,wBAAwB,CAAA;AACtC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,iCAAiC,CAAA;AAC/C,cAAc,0CAA0C,CAAA;AACxD,cAAc,kCAAkC,CAAA;AAChD,cAAc,0BAA0B,CAAA;AACxC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,oCAAoC,CAAA;AAClD,cAAc,sCAAsC,CAAA;AACpD,cAAc,+BAA+B,CAAA;AAC7C,cAAc,iCAAiC,CAAA;AAC/C,cAAc,yBAAyB,CAAA;AACvC,cAAc,uCAAuC,CAAA;AACrD,cAAc,uBAAuB,CAAA;AACrC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,8BAA8B,CAAA"}
package/dist/index.js CHANGED
@@ -37,6 +37,7 @@ export * from './IdentityEvidenceWorkspace.js';
37
37
  export * from './IdentityEvidenceDocumentPanel.js';
38
38
  export * from './IdentityEvidenceDeclarationForm.js';
39
39
  export * from './AnimalIdentityOnboarding.js';
40
+ export * from './AnimalIdentityOnboardingV2.js';
40
41
  export * from './AnimalCardArtifact.js';
41
42
  export * from './AnimalControllerAttestationPanel.js';
42
43
  export * from './EmergencyTaskHub.js';
package/dist/styles.css CHANGED
@@ -776,6 +776,20 @@ html[data-theme='dark'] .uhcp-brand-artwork-dark { display: block !important; }
776
776
  .uhcp-animal-onboarding-fields label { display: grid; gap: .35rem; }
777
777
  .uhcp-animal-onboarding-fields input, .uhcp-animal-onboarding-fields select { box-sizing: border-box; min-height: 2.8rem; width: 100%; }
778
778
  .uhcp-animal-onboarding-submit { justify-self: start; }
779
+ .uhcp-animal-onboarding-v2 details { background:var(--uhcp-surface, #fff); border:1px solid var(--uhcp-border, #d9dee7); border-radius:1rem; overflow:hidden; }
780
+ .uhcp-animal-onboarding-v2 summary { align-items:center; cursor:pointer; display:flex; font-weight:850; gap:.55rem; list-style:none; padding:1rem; }
781
+ .uhcp-animal-onboarding-v2 summary::-webkit-details-marker { display:none; }
782
+ .uhcp-animal-onboarding-v2 summary::after { content:'⌄'; margin-inline-start:auto; transition:transform .18s ease; }
783
+ .uhcp-animal-onboarding-v2 details[open] summary::after { transform:rotate(180deg); }
784
+ .uhcp-animal-onboarding-v2 details > .uhcp-animal-onboarding-fields,
785
+ .uhcp-animal-onboarding-v2 details > .uhcp-animal-onboarding-primary { border-top:1px solid var(--uhcp-border, #d9dee7); padding:1rem; }
786
+ .uhcp-animal-onboarding-primary { align-items:start; display:grid; gap:1rem; grid-template-columns:minmax(10rem, 14rem) minmax(0, 1fr); }
787
+ .uhcp-animal-photo { display:grid; gap:.65rem; }
788
+ .uhcp-animal-photo > img,.uhcp-animal-photo > span { align-items:center; aspect-ratio:1; background:var(--uhcp-accent-soft, #e9f3ff); border:2px solid color-mix(in srgb, var(--uhcp-turquoise, #1597a6) 42%, var(--uhcp-border, #d9dee7)); border-radius:1.25rem; display:flex; font-size:3rem; font-weight:850; justify-content:center; object-fit:cover; overflow:hidden; width:100%; }
789
+ .uhcp-animal-photo label { display:grid; font-size:.88rem; gap:.35rem; }
790
+ .uhcp-animal-controller-confirmation { align-items:center; background:var(--uhcp-accent-soft, #e9f3ff); border-radius:.75rem; display:flex !important; grid-column:1 / -1; padding:.75rem; }
791
+ .uhcp-animal-controller-confirmation input { min-height:auto !important; width:auto !important; }
792
+ @media (max-width: 42rem) { .uhcp-animal-onboarding-primary { grid-template-columns:1fr; } .uhcp-animal-photo { justify-self:center; max-width:15rem; width:100%; } }
779
793
  .uhcp-animal-card { aspect-ratio: 1.586; background: linear-gradient(145deg, #0a4169 0%, #087f8d 60%, #18a4a6 100%); border: 1px solid color-mix(in srgb, #fff 32%, transparent); border-radius: 1.15rem; box-shadow: 0 1rem 2.5rem rgb(2 30 51 / 22%); box-sizing: border-box; color: #fff; display: grid; gap: .8rem; grid-template-rows: auto 1fr auto; max-width: 33.5rem; min-height: 18.25rem; overflow: hidden; padding: 1.25rem; position: relative; width: 100%; }
780
794
  .uhcp-animal-card::after { background: radial-gradient(circle, rgb(255 255 255 / 18%), transparent 68%); content: ''; height: 18rem; inset: -8rem -7rem auto auto; pointer-events: none; position: absolute; width: 18rem; }
781
795
  .uhcp-animal-card-header, .uhcp-animal-card-body, .uhcp-animal-card-brand { align-items: center; display: flex; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uhc-portal-components-react",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "Brand-neutral React portal shell, account preferences and protected-profile UI for UHC and VetChain portals.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",