@incodetech/prizma-core-screens 0.2.4 → 0.2.12

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.
Files changed (243) hide show
  1. package/dist/BiometricConsentScreen.d.ts +95 -0
  2. package/dist/BiometricConsentScreen.js +91 -0
  3. package/dist/BiometricConsentStrings.d.ts +84 -0
  4. package/dist/BiometricConsentStrings.js +31 -0
  5. package/dist/CaptureScreen.d.ts +13 -0
  6. package/dist/CaptureScreen.js +2 -2
  7. package/dist/CurpEnterScreen.d.ts +7 -0
  8. package/dist/CurpEnterScreen.js +17 -3
  9. package/dist/CurpGenerateScreen.d.ts +8 -0
  10. package/dist/CurpGenerateScreen.js +28 -13
  11. package/dist/CurpLayout.css +8 -34
  12. package/dist/CurpStrings.d.ts +16 -2
  13. package/dist/DemoScreensLayout.css +16 -0
  14. package/dist/DemoScreensRedirectScreen.d.ts +6 -1
  15. package/dist/DemoScreensRedirectScreen.js +3 -2
  16. package/dist/DemoScreensStrings.d.ts +5 -0
  17. package/dist/DigitalIdUploadErrorScreen.d.ts +31 -0
  18. package/dist/DigitalIdUploadErrorScreen.js +26 -0
  19. package/dist/DigitalIdUploadFileTooLargeScreen.d.ts +27 -0
  20. package/dist/DigitalIdUploadFileTooLargeScreen.js +21 -0
  21. package/dist/DigitalIdUploadReviewScreen.d.ts +57 -0
  22. package/dist/DigitalIdUploadReviewScreen.js +44 -0
  23. package/dist/DigitalIdUploadStrings.d.ts +64 -0
  24. package/dist/DigitalIdUploadStrings.js +19 -0
  25. package/dist/DigitalIdUploadTutorialScreen.d.ts +28 -0
  26. package/dist/DigitalIdUploadTutorialScreen.js +23 -0
  27. package/dist/DocumentCaptureIllustration.d.ts +1 -1
  28. package/dist/EkybAddressDetailsScreen.d.ts +7 -0
  29. package/dist/EkybAddressDetailsScreen.js +25 -7
  30. package/dist/EkybBusinessDetailsScreen.d.ts +6 -0
  31. package/dist/EkybBusinessDetailsScreen.js +23 -6
  32. package/dist/EkybCountryScreen.d.ts +13 -5
  33. package/dist/EkybCountryScreen.js +41 -10
  34. package/dist/EkybOwnersScreen.d.ts +8 -0
  35. package/dist/EkybOwnersScreen.js +26 -3
  36. package/dist/EkybStrings.d.ts +32 -10
  37. package/dist/EkycCountryCatalog.d.ts +4 -1
  38. package/dist/EkycCountryCatalog.js +5 -3
  39. package/dist/EkycCountryFormScreen.d.ts +12 -3
  40. package/dist/EkycCountryFormScreen.js +28 -10
  41. package/dist/EkycDetailsScreen.d.ts +7 -0
  42. package/dist/EkycDetailsScreen.js +21 -4
  43. package/dist/EkycStrings.d.ts +14 -2
  44. package/dist/ElectronicSignatureLayout.css +6 -0
  45. package/dist/EmailInputStrings.d.ts +11 -1
  46. package/dist/EmailInputVerifyScreen.d.ts +7 -0
  47. package/dist/EmailInputVerifyScreen.js +17 -3
  48. package/dist/ErrorScreensScreen.d.ts +5 -2
  49. package/dist/ErrorScreensScreen.js +15 -4
  50. package/dist/ErrorScreensStrings.d.ts +17 -2
  51. package/dist/ErrorScreensStrings.js +18 -10
  52. package/dist/FaceCaptureCapturingScreen.d.ts +63 -0
  53. package/dist/FaceCaptureCapturingScreen.js +62 -2
  54. package/dist/FaceCaptureLayout.css +69 -4
  55. package/dist/FaceCaptureStrings.d.ts +23 -0
  56. package/dist/FaceMatchComparisonScreen.d.ts +3 -3
  57. package/dist/FaceMatchLayout.css +21 -12
  58. package/dist/FormScreen.css +30 -0
  59. package/dist/FormScreen.d.ts +28 -0
  60. package/dist/FormScreen.js +3 -2
  61. package/dist/FormsLongFormScreen.d.ts +8 -0
  62. package/dist/FormsLongFormScreen.js +34 -13
  63. package/dist/FormsStrings.d.ts +11 -1
  64. package/dist/GeolocationBrowserStepsScreen.d.ts +6 -2
  65. package/dist/GeolocationBrowserStepsScreen.js +7 -3
  66. package/dist/GeolocationStrings.d.ts +3 -0
  67. package/dist/IdCaptureLayout.css +205 -2
  68. package/dist/IdCaptureStageScreen.d.ts +47 -0
  69. package/dist/IdCaptureStageScreen.js +39 -3
  70. package/dist/IdCaptureStrings.d.ts +19 -0
  71. package/dist/ListScreen.d.ts +21 -0
  72. package/dist/ListScreen.js +7 -2
  73. package/dist/LoaderScreen.d.ts +7 -5
  74. package/dist/LoaderScreen.js +7 -5
  75. package/dist/LoadersStrings.d.ts +18 -5
  76. package/dist/OcrReviewErrorScreen.d.ts +30 -0
  77. package/dist/OcrReviewErrorScreen.js +24 -0
  78. package/dist/OcrReviewLayout.css +24 -0
  79. package/dist/OcrReviewLoadingScreen.d.ts +25 -0
  80. package/dist/OcrReviewLoadingScreen.js +19 -0
  81. package/dist/OcrReviewScreen.d.ts +76 -0
  82. package/dist/OcrReviewScreen.js +65 -0
  83. package/dist/OcrReviewStrings.d.ts +47 -0
  84. package/dist/OcrReviewStrings.js +17 -0
  85. package/dist/OcrReviewSuccessScreen.d.ts +30 -0
  86. package/dist/OcrReviewSuccessScreen.js +24 -0
  87. package/dist/PermissionsAllowScreen.d.ts +8 -11
  88. package/dist/PermissionsAllowScreen.js +8 -11
  89. package/dist/PermissionsAskScreen.d.ts +41 -0
  90. package/dist/PermissionsAskScreen.js +43 -0
  91. package/dist/PermissionsBrowserStepsScreen.d.ts +9 -2
  92. package/dist/PermissionsBrowserStepsScreen.js +12 -3
  93. package/dist/PermissionsLayout.css +47 -0
  94. package/dist/PermissionsStrings.d.ts +20 -9
  95. package/dist/PermissionsStrings.js +10 -4
  96. package/dist/PhoneInputLayout.css +10 -9
  97. package/dist/PhoneInputOtpScreen.d.ts +8 -0
  98. package/dist/PhoneInputOtpScreen.js +26 -19
  99. package/dist/PhoneInputStrings.d.ts +22 -4
  100. package/dist/PhoneInputVerifyScreen.d.ts +8 -0
  101. package/dist/PhoneInputVerifyScreen.js +36 -7
  102. package/dist/QualifiedSignatureAcceptScreen.js +6 -1
  103. package/dist/QualifiedSignatureDocumentScreen.d.ts +17 -10
  104. package/dist/QualifiedSignatureDocumentScreen.js +22 -17
  105. package/dist/QualifiedSignatureLayout.css +76 -0
  106. package/dist/QualifiedSignatureResultScreen.d.ts +16 -13
  107. package/dist/QualifiedSignatureResultScreen.js +17 -18
  108. package/dist/QualifiedSignatureStrings.d.ts +19 -2
  109. package/dist/ResultScreen.d.ts +8 -0
  110. package/dist/ResultScreen.js +14 -3
  111. package/dist/ReusabilityCredentialSelectionScreen.d.ts +95 -0
  112. package/dist/ReusabilityCredentialSelectionScreen.js +94 -0
  113. package/dist/ReusabilityLayout.css +241 -0
  114. package/dist/ReusabilityPrivacyPolicyScreen.d.ts +55 -0
  115. package/dist/ReusabilityPrivacyPolicyScreen.js +44 -0
  116. package/dist/ReusabilityStrings.d.ts +85 -0
  117. package/dist/ReusabilityStrings.js +37 -0
  118. package/dist/ReviewScreen.d.ts +38 -12
  119. package/dist/ReviewScreen.js +41 -8
  120. package/dist/ScreenLayout.css +382 -6
  121. package/dist/ScreenNav.css +8 -0
  122. package/dist/ScreenNav.d.ts +15 -2
  123. package/dist/ScreenNav.js +10 -3
  124. package/dist/VideoSelfieErrorScreen.js +1 -1
  125. package/dist/VideoSelfieIdCaptureScreen.d.ts +10 -3
  126. package/dist/VideoSelfieIdCaptureScreen.js +45 -14
  127. package/dist/VideoSelfieLayout.css +467 -34
  128. package/dist/VideoSelfieSelfieCaptureScreen.d.ts +44 -27
  129. package/dist/VideoSelfieSelfieCaptureScreen.js +65 -39
  130. package/dist/VideoSelfieSpeakScreen.js +19 -2
  131. package/dist/VideoSelfieStrings.d.ts +82 -13
  132. package/dist/VideoSelfieTurnHeadScreen.d.ts +51 -0
  133. package/dist/VideoSelfieTurnHeadScreen.js +66 -0
  134. package/dist/VideoSelfieTutorialScreen.d.ts +9 -0
  135. package/dist/VideoSelfieTutorialScreen.js +22 -5
  136. package/dist/VideoSelfieWaveScreen.d.ts +53 -0
  137. package/dist/VideoSelfieWaveScreen.js +62 -0
  138. package/dist/ViewfinderScreen.css +36 -0
  139. package/dist/WatchlistBusinessFormScreen.d.ts +11 -7
  140. package/dist/WatchlistBusinessFormScreen.js +27 -13
  141. package/dist/WatchlistBusinessStrings.d.ts +21 -7
  142. package/dist/flows.d.ts +8 -0
  143. package/dist/flows.js +17 -0
  144. package/dist/index.d.ts +19 -1
  145. package/dist/index.js +14 -0
  146. package/dist/styles.css +3 -0
  147. package/dist/types.d.ts +3 -1
  148. package/package.json +79 -3
  149. package/samples/video-selfie-camera-wide.jpg +0 -0
  150. package/samples/video-selfie-room-wide.jpg +0 -0
  151. package/samples/video-selfie-room.png +0 -0
  152. package/src/BiometricConsentScreen.tsx +192 -0
  153. package/src/BiometricConsentStrings.ts +119 -0
  154. package/src/CaptureScreen.tsx +17 -0
  155. package/src/CurpEnterScreen.tsx +21 -4
  156. package/src/CurpGenerateScreen.tsx +46 -22
  157. package/src/CurpLayout.css +8 -34
  158. package/src/CurpStrings.ts +22 -2
  159. package/src/DemoScreensLayout.css +16 -0
  160. package/src/DemoScreensRedirectScreen.tsx +14 -6
  161. package/src/DemoScreensStrings.ts +5 -0
  162. package/src/DigitalIdUploadErrorScreen.tsx +83 -0
  163. package/src/DigitalIdUploadFileTooLargeScreen.tsx +67 -0
  164. package/src/DigitalIdUploadReviewScreen.tsx +119 -0
  165. package/src/DigitalIdUploadStrings.ts +88 -0
  166. package/src/DigitalIdUploadTutorialScreen.tsx +59 -0
  167. package/src/DocumentCaptureIllustration.tsx +1 -1
  168. package/src/EkybAddressDetailsScreen.tsx +32 -4
  169. package/src/EkybBusinessDetailsScreen.tsx +23 -5
  170. package/src/EkybCountryScreen.tsx +45 -7
  171. package/src/EkybOwnersScreen.tsx +34 -3
  172. package/src/EkybStrings.ts +34 -10
  173. package/src/EkycCountryCatalog.ts +9 -4
  174. package/src/EkycCountryFormScreen.tsx +51 -12
  175. package/src/EkycDetailsScreen.tsx +22 -1
  176. package/src/EkycStrings.ts +14 -2
  177. package/src/ElectronicSignatureLayout.css +6 -0
  178. package/src/EmailInputStrings.ts +12 -1
  179. package/src/EmailInputVerifyScreen.tsx +21 -4
  180. package/src/ErrorScreensScreen.tsx +15 -4
  181. package/src/ErrorScreensStrings.ts +37 -12
  182. package/src/FaceCaptureCapturingScreen.tsx +102 -13
  183. package/src/FaceCaptureLayout.css +69 -4
  184. package/src/FaceCaptureStrings.ts +24 -0
  185. package/src/FaceMatchComparisonScreen.tsx +3 -3
  186. package/src/FaceMatchLayout.css +21 -12
  187. package/src/FormScreen.css +30 -0
  188. package/src/FormScreen.tsx +34 -0
  189. package/src/FormsLongFormScreen.tsx +41 -13
  190. package/src/FormsStrings.ts +12 -1
  191. package/src/GeolocationBrowserStepsScreen.tsx +6 -3
  192. package/src/GeolocationStrings.ts +3 -0
  193. package/src/IdCaptureLayout.css +205 -2
  194. package/src/IdCaptureStageScreen.tsx +75 -0
  195. package/src/IdCaptureStrings.ts +19 -0
  196. package/src/ListScreen.tsx +58 -10
  197. package/src/LoaderScreen.tsx +7 -5
  198. package/src/LoadersStrings.ts +18 -5
  199. package/src/OcrReviewErrorScreen.tsx +53 -0
  200. package/src/OcrReviewLayout.css +24 -0
  201. package/src/OcrReviewLoadingScreen.tsx +48 -0
  202. package/src/OcrReviewScreen.tsx +190 -0
  203. package/src/OcrReviewStrings.ts +69 -0
  204. package/src/OcrReviewSuccessScreen.tsx +52 -0
  205. package/src/PermissionsAllowScreen.tsx +9 -10
  206. package/src/PermissionsAskScreen.tsx +105 -0
  207. package/src/PermissionsBrowserStepsScreen.tsx +11 -3
  208. package/src/PermissionsLayout.css +47 -0
  209. package/src/PermissionsStrings.ts +42 -12
  210. package/src/PhoneInputLayout.css +10 -9
  211. package/src/PhoneInputOtpScreen.tsx +23 -3
  212. package/src/PhoneInputStrings.ts +23 -4
  213. package/src/PhoneInputVerifyScreen.tsx +41 -5
  214. package/src/QualifiedSignatureAcceptScreen.tsx +12 -1
  215. package/src/QualifiedSignatureDocumentScreen.tsx +31 -19
  216. package/src/QualifiedSignatureLayout.css +76 -0
  217. package/src/QualifiedSignatureResultScreen.tsx +28 -26
  218. package/src/QualifiedSignatureStrings.ts +19 -2
  219. package/src/ResultScreen.tsx +33 -2
  220. package/src/ReusabilityCredentialSelectionScreen.tsx +246 -0
  221. package/src/ReusabilityLayout.css +241 -0
  222. package/src/ReusabilityPrivacyPolicyScreen.tsx +109 -0
  223. package/src/ReusabilityStrings.ts +127 -0
  224. package/src/ReviewScreen.tsx +96 -27
  225. package/src/ScreenLayout.css +382 -6
  226. package/src/ScreenNav.css +8 -0
  227. package/src/ScreenNav.tsx +35 -5
  228. package/src/VideoSelfieErrorScreen.tsx +1 -1
  229. package/src/VideoSelfieIdCaptureScreen.tsx +125 -31
  230. package/src/VideoSelfieLayout.css +467 -34
  231. package/src/VideoSelfieSelfieCaptureScreen.tsx +110 -57
  232. package/src/VideoSelfieSpeakScreen.tsx +34 -15
  233. package/src/VideoSelfieStrings.ts +89 -14
  234. package/src/VideoSelfieTurnHeadScreen.tsx +162 -0
  235. package/src/VideoSelfieTutorialScreen.tsx +45 -23
  236. package/src/VideoSelfieWaveScreen.tsx +143 -0
  237. package/src/ViewfinderScreen.css +36 -0
  238. package/src/WatchlistBusinessFormScreen.tsx +35 -16
  239. package/src/WatchlistBusinessStrings.ts +21 -7
  240. package/src/flows.ts +25 -0
  241. package/src/index.ts +88 -3
  242. package/src/styles.css +3 -0
  243. package/src/types.ts +3 -1
@@ -56,6 +56,14 @@ export interface CurpGenerateScreenProps
56
56
  * The `dropdown` section IS the open-menu spec state, which Core exposes as the
57
57
  * documentation-only `forceOpen` (a screen is one static state, never a live
58
58
  * interaction) — the same mechanism Phone Input's `countryCode` section uses.
59
+ *
60
+ * `emptyError` is the empty-submit state (rule from Elena, 2026-09-30,
61
+ * GUIDELINES §6-E empty-submit rule): "Generate" was pressed with the form
62
+ * empty. All six fields carry the required `*`, so all six render empty with no
63
+ * placeholder, in Core's error state with `requiredError`; no menu is open and
64
+ * the primary CTA is disabled, following the drawn precedent in
65
+ * `FormsFieldScreen`. The resting `default` keeps the CTA enabled so the user
66
+ * can reach this state.
59
67
  */
60
68
  export const CurpGenerateScreen = forwardRef<HTMLDivElement, CurpGenerateScreenProps>(
61
69
  (
@@ -75,21 +83,30 @@ export const CurpGenerateScreen = forwardRef<HTMLDivElement, CurpGenerateScreenP
75
83
  ) => {
76
84
  const copy = strings.generate;
77
85
 
86
+ // Empty submit: every required field drops its value and placeholder and
87
+ // takes Core's error state with the shared "required" row; the CTA greys.
88
+ const emptyError = state === 'emptyError';
89
+ const ph = (placeholder?: string) => (emptyError ? undefined : placeholder);
90
+ const err = {
91
+ error: emptyError,
92
+ errorText: emptyError ? strings.requiredError : undefined,
93
+ };
94
+
78
95
  return (
79
96
  <PrizmaFormScreen
80
97
  ref={ref}
81
- /* Scopes the two module rules in CurpLayout.css (see the foundation and
82
- Core requests recorded there):
83
- - `--scroll` lets the six-field stack scroll inside the 844 frame
84
- instead of overflowing its box and painting over the CTA. EVERY
85
- section gets it, `dropdown` included — Figma draws "Generate"
86
- clear and unobstructed under the open menu.
87
- - `--menu-open` flips Core's absolutely-positioned menu above its
88
- trigger on the one section that forces it open, so the three
89
- gender rows stay inside the scrolling column. */
98
+ /* The six-field stack scrolls inside the 844 frame through the
99
+ foundation's `scrollFields` (overflow + the Spacing/16 that keeps the
100
+ cut clear of "Generate"). EVERY section gets it, `dropdown` included:
101
+ Figma draws "Generate" clear and unobstructed under the open menu.
102
+ It replaces the module's old `--scroll` rule, which scrolled but
103
+ carried no spacing, so the last field ran flush into the button.
104
+ `--menu-open` (CurpLayout.css) flips Core's absolutely-positioned
105
+ menu above its trigger on the one section that forces it open, so
106
+ the three gender rows stay inside the scrolling column. */
107
+ scrollFields
90
108
  className={[
91
109
  'curp-generate',
92
- 'curp-generate--scroll',
93
110
  state === 'dropdown' ? 'curp-generate--menu-open' : '',
94
111
  className,
95
112
  ]
@@ -98,6 +115,7 @@ export const CurpGenerateScreen = forwardRef<HTMLDivElement, CurpGenerateScreenP
98
115
  title={copy.title}
99
116
  subtitle={copy.subtitle}
100
117
  ctaLabel={copy.ctaLabel}
118
+ ctaDisabled={emptyError}
101
119
  ctaLoadingLabel={copy.ctaLoadingLabel}
102
120
  verifiedLabel={strings.verifiedLabel}
103
121
  navLabel={strings.navLabel}
@@ -112,54 +130,60 @@ export const CurpGenerateScreen = forwardRef<HTMLDivElement, CurpGenerateScreenP
112
130
  (DEVIATIONS #PI-6). */}
113
131
  <PrizmaTextField
114
132
  label={copy.firstName.label}
115
- placeholder={copy.firstName.placeholder}
133
+ placeholder={ph(copy.firstName.placeholder)}
116
134
  required
117
135
  requiredHint={strings.requiredHint}
118
136
  autoComplete="off"
119
- defaultValue={values?.firstName ?? ''}
137
+ defaultValue={emptyError ? '' : values?.firstName ?? ''}
138
+ {...err}
120
139
  />
121
140
  <PrizmaTextField
122
141
  label={copy.firstLastName.label}
123
- placeholder={copy.firstLastName.placeholder}
142
+ placeholder={ph(copy.firstLastName.placeholder)}
124
143
  required
125
144
  requiredHint={strings.requiredHint}
126
145
  autoComplete="off"
127
- defaultValue={values?.firstLastName ?? ''}
146
+ defaultValue={emptyError ? '' : values?.firstLastName ?? ''}
147
+ {...err}
128
148
  />
129
149
  <PrizmaTextField
130
150
  label={copy.secondLastName.label}
131
- placeholder={copy.secondLastName.placeholder}
151
+ placeholder={ph(copy.secondLastName.placeholder)}
132
152
  required
133
153
  requiredHint={strings.requiredHint}
134
154
  autoComplete="off"
135
- defaultValue={values?.secondLastName ?? ''}
155
+ defaultValue={emptyError ? '' : values?.secondLastName ?? ''}
156
+ {...err}
136
157
  />
137
158
  <PrizmaDropdown
138
159
  label={copy.gender.label}
139
- placeholder={copy.gender.placeholder}
160
+ placeholder={ph(copy.gender.placeholder)}
140
161
  required
141
162
  requiredHint={strings.requiredHint}
142
163
  options={genders}
143
- defaultValue={values?.gender ?? null}
164
+ defaultValue={emptyError ? null : values?.gender ?? null}
165
+ {...err}
144
166
  searchPlaceholder={copy.gender.searchPlaceholder}
145
167
  /* The `dropdown` section is the open Gender menu. */
146
168
  forceOpen={state === 'dropdown'}
147
169
  />
148
170
  <PrizmaDateField
149
171
  label={copy.dateOfBirth.label}
150
- placeholder={copy.dateOfBirth.placeholder}
172
+ placeholder={ph(copy.dateOfBirth.placeholder)}
151
173
  required
152
174
  requiredHint={strings.requiredHint}
153
175
  calendarLabel={copy.dateOfBirth.calendarLabel}
154
- defaultValue={values?.dateOfBirth ?? ''}
176
+ defaultValue={emptyError ? '' : values?.dateOfBirth ?? ''}
177
+ {...err}
155
178
  />
156
179
  <PrizmaDropdown
157
180
  label={copy.birthState.label}
158
- placeholder={copy.birthState.placeholder}
181
+ placeholder={ph(copy.birthState.placeholder)}
159
182
  required
160
183
  requiredHint={strings.requiredHint}
161
184
  options={states}
162
- defaultValue={values?.birthState ?? null}
185
+ defaultValue={emptyError ? null : values?.birthState ?? null}
186
+ {...err}
163
187
  searchable
164
188
  searchPlaceholder={copy.birthState.searchPlaceholder}
165
189
  />
@@ -1,6 +1,6 @@
1
1
  /* CURP — module layout additions.
2
2
  *
3
- * THREE rules, and none of them declares spacing: the content column, the 24px
3
+ * TWO rules, and none of them declares spacing: the content column, the 24px
4
4
  * gutters, the title → fields Spacing/32, the field gaps, the CTA column and
5
5
  * the anchored footer all stay the foundation's (`ScreenLayout.css`,
6
6
  * `FormScreen.css`). Nothing here re-declares an archetype value, invents a
@@ -10,40 +10,14 @@
10
10
  */
11
11
 
12
12
  /* ── The generator's field stack is taller than the screen ───────────────────
13
- * `CURP.Gene.default` is a 1051-tall frame: its `input-list` alone is 548 tall
14
- * (six 78px labelled fields at Spacing/16) and the CONTENT frame grows to 861
15
- * instead of the usual 654. At the 390×844 reference the column has ~470 for
16
- * it, so the last dropdown and the CTA fall past the fold.
17
- *
18
- * Archetype E's `.prizma-form-screen__fields` is `flex: 1; min-height: 0` with
19
- * no overflow behaviour — it was measured against Phone Input, whose forms are
20
- * one or two fields — so the surplus is silently clipped by `ScreenShell`'s
21
- * `overflow: hidden`, and (because the fields cannot shrink below
22
- * `--prizma-input-size-field-height`) it first PAINTS OVER the sibling CTA.
23
- * Screens are authored at 390×844 and never reflow (README / DEVIATIONS #17),
24
- * so growing the frame is not an option either.
25
- *
26
- * The stack is therefore allowed to scroll INSIDE the column: the title, the
27
- * CTA and the anchored tag stay put and the six fields pan, which is the
28
- * closest a fixed 844 frame gets to Figma's taller page. It adds NO padding, NO
29
- * gap and NO size of its own.
30
- *
31
- * Applied to EVERY `CURP.Gene.*` section including `dropdown`. The earlier
32
- * build withheld it from `dropdown` to keep Core's absolutely-positioned menu
33
- * from being clipped, and that traded one clipped element for a worse one: with
34
- * `overflow: visible` the 548-tall stack overflowed its box and painted the
35
- * open menu straight across the "Generate" button, which Figma (2058:16373)
36
- * draws clear and unobstructed under the menu. The CTA is never allowed to be
37
- * overlapped; the menu is handled by the next rule instead.
38
- *
39
- * FOUNDATION REQUEST: promote this to `FormScreen.css` as a
40
- * `.prizma-form-screen__fields--scroll` variant (or make overflow the default),
41
- * driven by a `scrollFields` prop on `PrizmaFormScreen`. Every long-form module
42
- * still to come in Wave 3 — eKYC, eKYB, address entry — will hit the same wall.
13
+ * `CURP.Gene.default` is a 1051-tall frame (six 78px labelled fields at
14
+ * Spacing/16), so at 390×844 the stack scrolls inside the column. That is the
15
+ * foundation's `scrollFields` on `PrizmaFormScreen` now (FormScreen.css
16
+ * `__fields--scroll`), which also carries the Spacing/16 between the cut and
17
+ * the CTA. The module used to hand-roll `overflow-y: auto` here with no
18
+ * spacing, so the last field ran flush into "Generate" (Elena, 2026-09-30).
19
+ * Removed in favour of the foundation rule.
43
20
  */
44
- .curp-generate--scroll .prizma-form-screen__fields {
45
- overflow-y: auto;
46
- }
47
21
 
48
22
  /* ── The open Gender menu must stay inside the scrolling column ──────────────
49
23
  * `CURP.Gene.dropdown` exists to show the three gender rows, so they may not be
@@ -27,16 +27,30 @@
27
27
  * - `error` — negative field + clear ✕ + the "Invalid CURP" row
28
28
  * - `loading` — both actions inert while the lookup runs; the spinner rides the
29
29
  * CTA, never the field (the Phone Input lesson, DEVIATIONS #PI-5)
30
+ * - `emptyError` — "Continue" pressed with the field still empty: the field
31
+ * stays empty (no placeholder) in Core's error state with `requiredError`,
32
+ * and the CTA is disabled. GUIDELINES §6-E empty-submit rule (Elena,
33
+ * 2026-09-30).
30
34
  */
31
- export type CurpEnterState = 'default' | 'focused' | 'filled' | 'error' | 'loading';
35
+ export type CurpEnterState =
36
+ | 'default'
37
+ | 'focused'
38
+ | 'filled'
39
+ | 'error'
40
+ | 'loading'
41
+ | 'emptyError';
32
42
 
33
43
  /**
34
44
  * The `CURP.Gene.*` form sections — the six-field generator:
35
45
  * - `default` — every field empty on its placeholder
36
46
  * - `dropdown` — the Gender menu open over the form (Core's `forceOpen`)
37
47
  * - `filled` — every field carrying a value
48
+ * - `emptyError` — "Generate" pressed with the form still empty: every required
49
+ * field stays empty (no placeholder) in Core's error state with
50
+ * `requiredError`, no menu is open, and the CTA is disabled. GUIDELINES §6-E
51
+ * empty-submit rule (Elena, 2026-09-30).
38
52
  */
39
- export type CurpGenerateState = 'default' | 'dropdown' | 'filled';
53
+ export type CurpGenerateState = 'default' | 'dropdown' | 'filled' | 'emptyError';
40
54
 
41
55
  /**
42
56
  * The two spinner sections. `verifying` is `CURP.EnterCURP.veryfing` (the
@@ -96,6 +110,12 @@ export interface CurpStrings {
96
110
  * the marker is localizable and never hardcoded in a screen.
97
111
  */
98
112
  requiredHint?: string;
113
+ /**
114
+ * Error row under every required field left empty when the primary CTA is
115
+ * pressed (the `emptyError` state of the entry and generator forms):
116
+ * "This field is required". SDK key `dynamicForms.fieldRequired`.
117
+ */
118
+ requiredError: string;
99
119
 
100
120
  /** CURP.EnterCURP.{default,focused,filled,error,loading} */
101
121
  enter: {
@@ -389,6 +389,22 @@
389
389
  background: var(--prizma-surface-neutral-50);
390
390
  align-items: center;
391
391
  justify-content: center;
392
+ /* The card hugs its content and is taller than a short window: on the Duo
393
+ * open full screen (626) unsafe centring put 90px of it above the page and
394
+ * 90 below, heading cut off (sweep 2026-10-20). `safe` starts the card at
395
+ * the top of the page when it does not fit, and does nothing when it does
396
+ * (#F-37, same mechanism). The plain `center` is the fallback. */
397
+ justify-content: safe center;
398
+ }
399
+
400
+ /* …and under 720 the page scrolls, as a desktop page does when the window is
401
+ * shorter than its content. The shell is `overflow: hidden` for the screens
402
+ * that pin chrome to its edges; this page pins nothing, the tag rides in the
403
+ * card's band. */
404
+ @container pcs-screen (max-height: 720px) {
405
+ .prizma-screen-shell.demo-screens-redirect {
406
+ overflow-y: auto;
407
+ }
392
408
  }
393
409
 
394
410
  .demo-screens-redirect__card {
@@ -21,8 +21,13 @@ import './DemoScreensLayout.css';
21
21
  * - `resend` "Link sent!", resend available
22
22
  * - `resent` "Link sent!" with the "resent" snackbar
23
23
  * - `tooMany` "Too many attempts"
24
+ * - `invalidUuid` the link's UUID was rejected (expired or unknown): no leg
25
+ * to offer, only the error. One message for Flows and
26
+ * Workflows alike, since the backend answers the same way for
27
+ * both (Elena, 2026-10-21: web SDK 2.0 showed "QR code
28
+ * expired" for Flows and "Session expired" with 2.0 off).
24
29
  */
25
- export type DemoScreensRedirectStage = 'qr' | 'sms' | 'smsFilled' | 'sent' | 'resend' | 'resent' | 'tooMany';
30
+ export type DemoScreensRedirectStage = 'qr' | 'sms' | 'smsFilled' | 'sent' | 'resend' | 'resent' | 'tooMany' | 'invalidUuid';
26
31
 
27
32
  export interface DemoScreensRedirectScreenProps
28
33
  extends Omit<PrizmaScreenShellProps, 'dark' | 'title' | 'children'> {
@@ -79,7 +84,8 @@ export const DemoScreensRedirectScreen = forwardRef<HTMLDivElement, DemoScreensR
79
84
  { code: 'RS', name: 'Serbia', dialCode: '+381', flag: '' },
80
85
  ].find(c => c.code === r.countryCode);
81
86
  const subtitle = sms ? r.subtitle : desktop ? r.subtitleQrDesktop : r.subtitleQrOnly;
82
- const verdict = stage === 'sent' || stage === 'resend' || stage === 'resent' || stage === 'tooMany';
87
+ const invalid = stage === 'invalidUuid';
88
+ const verdict = stage === 'sent' || stage === 'resend' || stage === 'resent' || stage === 'tooMany' || invalid;
83
89
 
84
90
  return (
85
91
  <PrizmaScreenShell
@@ -96,8 +102,8 @@ export const DemoScreensRedirectScreen = forwardRef<HTMLDivElement, DemoScreensR
96
102
  <div className="demo-screens-redirect__card">
97
103
  <PrizmaScreenNav logo={overrides?.logo} navLabel={strings.navLabel} />
98
104
  <div className="prizma-screen-layout__content demo-screens-redirect__content">
99
- <PrizmaTitle variant="centered" title={r.title} showSubtitle subtitle={subtitle} />
100
- {sms ? (
105
+ {invalid ? null : <PrizmaTitle variant="centered" title={r.title} showSubtitle subtitle={subtitle} />}
106
+ {sms && !invalid ? (
101
107
  <div className="demo-screens-tabs" role="tablist">
102
108
  <button type="button" role="tab" aria-selected={!onSms} className={['demo-screens-tabs__tab', !onSms ? 'demo-screens-tabs__tab--active' : ''].join(' ')} onClick={() => onAction?.({ type: 'secondary', screenId })}>
103
109
  {r.tabs.qr}
@@ -138,7 +144,9 @@ export const DemoScreensRedirectScreen = forwardRef<HTMLDivElement, DemoScreensR
138
144
 
139
145
  {verdict ? (
140
146
  <div className="demo-screens-redirect__leg demo-screens-redirect__leg--verdict">
141
- {stage === 'tooMany' ? (
147
+ {invalid ? (
148
+ <PrizmaTitle variant="centered" title={r.invalidUuid.title} showSubtitle subtitle={r.invalidUuid.subtitle} icon={{ type: 'status', status: 'error' }} />
149
+ ) : stage === 'tooMany' ? (
142
150
  <PrizmaTitle variant="centered" title={r.tooMany.title} showSubtitle subtitle={r.tooMany.subtitle} icon={{ type: 'status', status: 'error' }} />
143
151
  ) : (
144
152
  <PrizmaTitle
@@ -149,7 +157,7 @@ export const DemoScreensRedirectScreen = forwardRef<HTMLDivElement, DemoScreensR
149
157
  icon={{ type: 'status', status: 'success' }}
150
158
  />
151
159
  )}
152
- {stage === 'tooMany' ? (
160
+ {invalid ? null : stage === 'tooMany' ? (
153
161
  <PrizmaButton variant="tertiary-link" size="L" onClick={() => onAction?.({ type: 'tertiary', screenId })}>
154
162
  {r.tooMany.changeLabel}
155
163
  </PrizmaButton>
@@ -171,6 +171,11 @@ export interface DemoScreensStrings {
171
171
  subtitle: string;
172
172
  changeLabel: string;
173
173
  };
174
+ /** The link's UUID was rejected: the same copy for Flows and Workflows. */
175
+ invalidUuid: {
176
+ title: string;
177
+ subtitle: string;
178
+ };
174
179
  };
175
180
 
176
181
  /** WEB · `demo_2` — the front door: language, the wordmark, one CTA. */
@@ -0,0 +1,83 @@
1
+ import React, { forwardRef } from 'react';
2
+ import { PrizmaCaptureDigitalId } from '@incodetech/prizma-core-web/capture-digital-id';
3
+ import { PrizmaReviewScreen, type PrizmaReviewScreenProps } from './ReviewScreen';
4
+ import type { PrizmaScreenActionHandler, PrizmaScreenOverrides } from './types';
5
+ import type { DigitalIdUploadStrings } from './DigitalIdUploadStrings';
6
+ import './ScreenLayout.css';
7
+
8
+ export interface DigitalIdUploadErrorScreenProps
9
+ extends Omit<
10
+ PrizmaReviewScreenProps,
11
+ | 'stage'
12
+ | 'title'
13
+ | 'subtitle'
14
+ | 'successTitle'
15
+ | 'analyzingLabel'
16
+ | 'primaryLabel'
17
+ | 'secondaryLabel'
18
+ | 'verifiedLabel'
19
+ | 'children'
20
+ > {
21
+ /** Module copy — this screen reads the `error` slice. */
22
+ strings: DigitalIdUploadStrings;
23
+ screenId?: string;
24
+ overrides?: PrizmaScreenOverrides;
25
+ onAction?: PrizmaScreenActionHandler;
26
+ }
27
+
28
+ /**
29
+ * Digital ID Upload — the uploaded file was rejected, built on the
30
+ * `PrizmaReviewScreen` archetype's `error` stage (GUIDELINES.md §6-H). Figma:
31
+ * `Upload Digital ID` file (wxFsHRvllern4VnjRqM4JO), `Errors` section,
32
+ * `Error` frame (node 25874:11957).
33
+ *
34
+ * Kept separate from `DigitalIdUploadReviewScreen` (the confirm flow) rather
35
+ * than folded into it as a fourth stage of that wrapper — same split Document
36
+ * Capture draws between its confirm screens and
37
+ * `DocumentCaptureGenericErrorScreen`, even though here both wrappers happen
38
+ * to share the same archetype under the hood. The media is
39
+ * `PrizmaCaptureDigitalId state="error"` (red 4px border) — the same managed
40
+ * component every other stage uses, not a bespoke verdict illustration — and
41
+ * the header/footer are hidden exactly like `analyzing`/`success`
42
+ * (`centeredWhenNoNav`, since this Figma frame draws the identical symmetric
43
+ * header/media/CTA skeleton).
44
+ */
45
+ export const DigitalIdUploadErrorScreen = forwardRef<
46
+ HTMLDivElement,
47
+ DigitalIdUploadErrorScreenProps
48
+ >(
49
+ (
50
+ {
51
+ strings,
52
+ screenId = 'digital-id-upload-error',
53
+ showHeader = false,
54
+ showFooter = false,
55
+ overrides,
56
+ onAction,
57
+ ...shell
58
+ },
59
+ ref,
60
+ ) => (
61
+ <PrizmaReviewScreen
62
+ ref={ref}
63
+ stage="error"
64
+ title={strings.error.title}
65
+ subtitle={strings.error.subtitle}
66
+ primaryLabel={strings.error.ctaLabel}
67
+ centeredWhenNoNav
68
+ verifiedLabel={strings.verifiedLabel}
69
+ navLabel={strings.navLabel}
70
+ showHeader={showHeader}
71
+ showFooter={showFooter}
72
+ screenId={screenId}
73
+ overrides={overrides}
74
+ onAction={onAction}
75
+ {...shell}
76
+ >
77
+ <PrizmaCaptureDigitalId state="error" />
78
+ </PrizmaReviewScreen>
79
+ ),
80
+ );
81
+ DigitalIdUploadErrorScreen.displayName = 'DigitalIdUploadErrorScreen';
82
+
83
+ export default DigitalIdUploadErrorScreen;
@@ -0,0 +1,67 @@
1
+ import React, { forwardRef } from 'react';
2
+ import { PrizmaResultScreen, type PrizmaResultScreenProps } from './ResultScreen';
3
+ import type { PrizmaScreenActionHandler, PrizmaScreenOverrides } from './types';
4
+ import type { DigitalIdUploadStrings } from './DigitalIdUploadStrings';
5
+ import './ScreenLayout.css';
6
+
7
+ export interface DigitalIdUploadFileTooLargeScreenProps
8
+ extends Omit<
9
+ PrizmaResultScreenProps,
10
+ 'status' | 'title' | 'subtitle' | 'ctaLabel' | 'verifiedLabel' | 'navLabel'
11
+ > {
12
+ /** Module copy — this screen reads the `fileTooLarge` slice. */
13
+ strings: DigitalIdUploadStrings;
14
+ screenId?: string;
15
+ overrides?: PrizmaScreenOverrides;
16
+ onAction?: PrizmaScreenActionHandler;
17
+ }
18
+
19
+ /**
20
+ * Digital ID Upload — the selected file exceeds the size limit, built on the
21
+ * `PrizmaResultScreen` archetype (GUIDELINES.md §6-F). Figma: `Upload Digital
22
+ * ID` file (wxFsHRvllern4VnjRqM4JO), `Errors` section, `File Limit Error`
23
+ * frame (node 26083:3981).
24
+ *
25
+ * Unlike `DigitalIdUploadErrorScreen` (the rejected-document verdict, built on
26
+ * the Review archetype because it reuses `PrizmaCaptureDigitalId`), this
27
+ * screen never had a file to show — the size check runs before anything
28
+ * uploads — so Figma draws the plain F recipe: status icon + title + subtitle
29
+ * centred in the full height, one CTA pinned near the bottom. No media slot,
30
+ * no card, nothing to reuse from the capture component.
31
+ */
32
+ export const DigitalIdUploadFileTooLargeScreen = forwardRef<
33
+ HTMLDivElement,
34
+ DigitalIdUploadFileTooLargeScreenProps
35
+ >(
36
+ (
37
+ {
38
+ strings,
39
+ screenId = 'digital-id-upload-file-too-large',
40
+ showHeader = false,
41
+ showFooter = false,
42
+ overrides,
43
+ onAction,
44
+ ...shell
45
+ },
46
+ ref,
47
+ ) => (
48
+ <PrizmaResultScreen
49
+ ref={ref}
50
+ status="error"
51
+ title={strings.fileTooLarge.title}
52
+ subtitle={strings.fileTooLarge.subtitle}
53
+ ctaLabel={strings.fileTooLarge.ctaLabel}
54
+ verifiedLabel={strings.verifiedLabel}
55
+ navLabel={strings.navLabel}
56
+ showHeader={showHeader}
57
+ showFooter={showFooter}
58
+ screenId={screenId}
59
+ overrides={overrides}
60
+ onAction={onAction}
61
+ {...shell}
62
+ />
63
+ ),
64
+ );
65
+ DigitalIdUploadFileTooLargeScreen.displayName = 'DigitalIdUploadFileTooLargeScreen';
66
+
67
+ export default DigitalIdUploadFileTooLargeScreen;
@@ -0,0 +1,119 @@
1
+ import React, { forwardRef } from 'react';
2
+ import { PrizmaCaptureDigitalId } from '@incodetech/prizma-core-web/capture-digital-id';
3
+ import { PrizmaReviewScreen, type PrizmaReviewScreenProps } from './ReviewScreen';
4
+ import type { PrizmaScreenActionHandler, PrizmaScreenOverrides } from './types';
5
+ import type {
6
+ DigitalIdUploadConfirmStage,
7
+ DigitalIdUploadStrings,
8
+ } from './DigitalIdUploadStrings';
9
+ import './ScreenLayout.css';
10
+
11
+ export interface DigitalIdUploadReviewScreenProps
12
+ extends Omit<
13
+ PrizmaReviewScreenProps,
14
+ | 'title'
15
+ | 'subtitle'
16
+ | 'successTitle'
17
+ | 'analyzingLabel'
18
+ | 'primaryLabel'
19
+ | 'secondaryLabel'
20
+ | 'verifiedLabel'
21
+ | 'children'
22
+ | 'stage'
23
+ > {
24
+ /** Module copy — this screen reads the `review` slice. */
25
+ strings: DigitalIdUploadStrings;
26
+ /** Which stage of the confirm flow to render. Default `review`. */
27
+ stage?: DigitalIdUploadConfirmStage;
28
+ /** The uploaded file's name, shown under the card on `review` only. Not
29
+ * copy — it is runtime data from the picker, so it stays out of
30
+ * `strings` (same contract as `DocumentCaptureConfirmUploadScreen`). */
31
+ filename?: string;
32
+ /** Loading-bar progress on `analyzing`, 0–100. */
33
+ progress?: number;
34
+ screenId?: string;
35
+ overrides?: PrizmaScreenOverrides;
36
+ onAction?: PrizmaScreenActionHandler;
37
+ }
38
+
39
+ /**
40
+ * Digital ID Upload — review / analyzing / success, built on the
41
+ * `PrizmaReviewScreen` archetype (GUIDELINES.md §6-H). Figma: `Upload Digital
42
+ * ID` file (wxFsHRvllern4VnjRqM4JO), "Web Happy Path - Light Mode" section —
43
+ * `Review Document` / `Analyzing` / `Success` (nodes 25351:14725/6/7).
44
+ *
45
+ * The media stage is Core's `PrizmaCaptureDigitalId` across all three stages
46
+ * — its own state enum (`review` / `analyzing` / `success`) maps directly
47
+ * onto the archetype's stage axis, so it already owns the border treatment
48
+ * (neutral on review, green 4px on analyzing/success), the scan-flash sweep
49
+ * and the filename row. Nothing is hand-built here, unlike
50
+ * `DocumentCaptureConfirmUploadScreen`'s bespoke file-card — Core ships a
51
+ * purpose-built component for this module.
52
+ *
53
+ * Header/footer visibility is genuinely per-stage in this Figma (logo nav +
54
+ * verified tag show on `review`, both are absent on `analyzing` and
55
+ * `success`) — not a layout choice this file invents, just the existing
56
+ * `showHeader`/`showFooter` chrome knobs (owner rule 2026-08-18: header and
57
+ * footer are independently controlled) applied per stage.
58
+ *
59
+ * Always opts into the archetype's `centeredWhenNoNav` — it only takes effect
60
+ * while the nav is hidden (`analyzing`/`success` here), where this Figma
61
+ * draws the header + media as one group centred with equal space above and
62
+ * below it, not a pinned header (see ReviewScreen.tsx for the mechanics).
63
+ * `review` always shows its own nav, so the flag is a no-op there, and the
64
+ * default `false` leaves Document Capture's existing analyzing/success
65
+ * screens byte-for-byte unchanged (neither ever hides the nav here).
66
+ *
67
+ * GAP (report, not workaround — README rule 2): Figma's `Success` section
68
+ * also shows a subtitle ("Let's continue") under the success title, but the
69
+ * shared `PrizmaReviewScreen` archetype only renders a subtitle on
70
+ * `stage="review"` — every other current consumer's success state has none.
71
+ * Left unset here pending an owner call on whether to extend the shared
72
+ * archetype with a `successSubtitle` slot.
73
+ */
74
+ export const DigitalIdUploadReviewScreen = forwardRef<
75
+ HTMLDivElement,
76
+ DigitalIdUploadReviewScreenProps
77
+ >(
78
+ (
79
+ {
80
+ strings,
81
+ stage = 'review',
82
+ filename,
83
+ progress = 45,
84
+ showHeader = stage === 'review',
85
+ showFooter = stage === 'review',
86
+ screenId = `digital-id-upload-${stage}`,
87
+ overrides,
88
+ onAction,
89
+ ...shell
90
+ },
91
+ ref,
92
+ ) => (
93
+ <PrizmaReviewScreen
94
+ ref={ref}
95
+ stage={stage}
96
+ title={strings.review.title}
97
+ subtitle={strings.review.subtitle}
98
+ successTitle={strings.review.successTitle}
99
+ analyzingLabel={strings.review.analyzingLabel}
100
+ centeredWhenNoNav
101
+ progress={progress}
102
+ primaryLabel={strings.review.ctaLabel}
103
+ secondaryLabel={strings.review.replaceLabel}
104
+ verifiedLabel={strings.verifiedLabel}
105
+ navLabel={strings.navLabel}
106
+ showHeader={showHeader}
107
+ showFooter={showFooter}
108
+ screenId={screenId}
109
+ overrides={overrides}
110
+ onAction={onAction}
111
+ {...shell}
112
+ >
113
+ <PrizmaCaptureDigitalId state={stage} filename={filename} />
114
+ </PrizmaReviewScreen>
115
+ ),
116
+ );
117
+ DigitalIdUploadReviewScreen.displayName = 'DigitalIdUploadReviewScreen';
118
+
119
+ export default DigitalIdUploadReviewScreen;