@umkmall/proto 0.1.0-h03e952a96ac5
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/README.md +12 -0
- package/package.json +19 -0
- package/umkmall-components.md +288 -0
- package/umkmall-prototype-starter.html +1101 -0
- package/umkmall-rules.md +158 -0
- package/umkmall-starter-app.md +365 -0
- package/umkmall-starter.md +847 -0
- package/umkmall-tailwind.js +599 -0
- package/umkmall-tokens.css +409 -0
- package/umkmall-ui.js +209 -0
|
@@ -0,0 +1,1101 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="id">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Prototipe UMKMall</title>
|
|
7
|
+
<!--
|
|
8
|
+
UMKMall prototyping starter — generated, do not hand-edit the parts above the demo.
|
|
9
|
+
|
|
10
|
+
Built from commit the @umkmall/proto version named in the asset URLs below
|
|
11
|
+
Regenerate with: npm run proto:assets
|
|
12
|
+
|
|
13
|
+
HOW TO USE THIS FILE
|
|
14
|
+
1. Open it by double-click. It runs with no build step and no npm install.
|
|
15
|
+
2. Edit the <script type="text/babel"> block at the bottom. Start with <Konten />.
|
|
16
|
+
3. Reload the browser. That is the whole loop.
|
|
17
|
+
|
|
18
|
+
WHAT THIS FILE NEEDS FROM THE NETWORK
|
|
19
|
+
Four hosts, every time it is opened — not just the first time:
|
|
20
|
+
|
|
21
|
+
https://cdn.jsdelivr.net/npm/@umkmall/proto@0.1.0-h03e952a96ac5/umkmall-tailwind.js
|
|
22
|
+
https://cdn.jsdelivr.net/npm/@umkmall/proto@0.1.0-h03e952a96ac5/umkmall-ui.js
|
|
23
|
+
https://cdn.jsdelivr.net/npm/@babel/standalone@7.26.4/babel.min.js
|
|
24
|
+
— all on jsDelivr: the design system's assets (pinned) and the JSX compiler.
|
|
25
|
+
The token CSS is NOT fetched: it is inlined in the <style> below, because a claude.ai
|
|
26
|
+
artifact loads stylesheets only from Google Fonts. Keep that <style> as it is.
|
|
27
|
+
the Tailwind Play CDN, for the styling engine
|
|
28
|
+
Google Fonts (fonts.googleapis.com and fonts.gstatic.com), for the typeface
|
|
29
|
+
|
|
30
|
+
If any of them is unreachable the page renders BLANK or unstyled with no error. That is
|
|
31
|
+
the first thing to check when it looks broken. They are public CDNs on purpose: Claude's and
|
|
32
|
+
Gemini's previews only load scripts from hosts on their allowlists, and these are on both.
|
|
33
|
+
|
|
34
|
+
YOU DO NOT HAVE TO START FROM THIS FILE
|
|
35
|
+
Four tags add UMKMall styling to a page you already have: the token CSS as a <link> to
|
|
36
|
+
https://cdn.jsdelivr.net/npm/@umkmall/proto@0.1.0-h03e952a96ac5/umkmall-tokens.css
|
|
37
|
+
then the Play CDN, the config and the bundle below. Keep the order — the config must come after
|
|
38
|
+
the Play CDN script, or the theme arrives too late and every class resolves to nothing.
|
|
39
|
+
|
|
40
|
+
UMKMall UI — 57 components, and the values each axis accepts.
|
|
41
|
+
Generated from packages/ui/src/contract/variants.ts. Do not hand-edit.
|
|
42
|
+
|
|
43
|
+
Button (Foundations)
|
|
44
|
+
type: primary | ghost | text
|
|
45
|
+
color: primary | light | danger | dark
|
|
46
|
+
size: large | medium | small | micro
|
|
47
|
+
width: auto | full
|
|
48
|
+
booleans: disabled, isLoading
|
|
49
|
+
|
|
50
|
+
Alert (Feedback)
|
|
51
|
+
type: alert | toast
|
|
52
|
+
function: success | info | warning | danger
|
|
53
|
+
size: large | medium | small
|
|
54
|
+
treatment: full | accent | coach | banner
|
|
55
|
+
appearance: default | a2ui
|
|
56
|
+
|
|
57
|
+
Badge (Content)
|
|
58
|
+
color: primary | warning | success | danger | light | bestseller | gradient
|
|
59
|
+
size: xlarge | medium | small | micro
|
|
60
|
+
appearance: default | a2ui
|
|
61
|
+
booleans: showIconLeft, showIconRight
|
|
62
|
+
|
|
63
|
+
Chip (Content)
|
|
64
|
+
state: default | active
|
|
65
|
+
size: huge | xlarge | large | medium | small | micro
|
|
66
|
+
appearance: default | a2ui
|
|
67
|
+
booleans: showIconLeft, showIconRight
|
|
68
|
+
|
|
69
|
+
TextField (Form)
|
|
70
|
+
size: large | small
|
|
71
|
+
appearance: default | a2ui
|
|
72
|
+
booleans: error, disabled
|
|
73
|
+
REQUIRED props (not axes): label
|
|
74
|
+
|
|
75
|
+
CheckBox (Form)
|
|
76
|
+
size: xlarge | large | medium | small
|
|
77
|
+
appearance: default | native
|
|
78
|
+
booleans: error, disabled, indeterminate
|
|
79
|
+
REQUIRED props (not axes): label
|
|
80
|
+
|
|
81
|
+
Radio (Form)
|
|
82
|
+
size: large | medium | small
|
|
83
|
+
booleans: error, disabled
|
|
84
|
+
REQUIRED props (not axes): label
|
|
85
|
+
|
|
86
|
+
SegmentedControl (Form)
|
|
87
|
+
size: medium | small
|
|
88
|
+
booleans: disabled
|
|
89
|
+
REQUIRED props (not axes): options, value, onChange
|
|
90
|
+
|
|
91
|
+
Switch (Form)
|
|
92
|
+
size: large | medium | small
|
|
93
|
+
booleans: disabled, loading
|
|
94
|
+
REQUIRED props (not axes): label
|
|
95
|
+
|
|
96
|
+
Slider (Form)
|
|
97
|
+
size: large | small
|
|
98
|
+
booleans: disabled
|
|
99
|
+
|
|
100
|
+
DateTimeInput (Form)
|
|
101
|
+
size: large | small
|
|
102
|
+
selection: single | range
|
|
103
|
+
appearance: default | a2ui
|
|
104
|
+
booleans: error, disabled, withTime, portal
|
|
105
|
+
|
|
106
|
+
OtpInput (Form)
|
|
107
|
+
size: large | small
|
|
108
|
+
booleans: error, disabled
|
|
109
|
+
REQUIRED props (not axes): value, onChange
|
|
110
|
+
|
|
111
|
+
Tabs (Navigation)
|
|
112
|
+
size: large | small
|
|
113
|
+
state: default | active
|
|
114
|
+
appearance: default | a2ui
|
|
115
|
+
booleans: showIconLeft, showNotif, showTitle
|
|
116
|
+
|
|
117
|
+
Tooltip (Feedback)
|
|
118
|
+
color: dark | white
|
|
119
|
+
size: large | small
|
|
120
|
+
booleans: showClose
|
|
121
|
+
REQUIRED props (not axes): content
|
|
122
|
+
|
|
123
|
+
Skeleton (Feedback)
|
|
124
|
+
shape: text | circle | rect
|
|
125
|
+
|
|
126
|
+
Text (Foundations)
|
|
127
|
+
role: display | h1 | h2 | h3 | h4 | h5 | h6 | paragraph | bodyLg | bodyMd | bodySm
|
|
128
|
+
weight: bold | semibold | medium | regular
|
|
129
|
+
color: primary | secondary | disable | invert | brand | success | error | warning
|
|
130
|
+
|
|
131
|
+
Icon (Foundations)
|
|
132
|
+
size: xs | sm | md | lg | xl
|
|
133
|
+
color: primary | secondary | disable | invert | brand | success | error | warning
|
|
134
|
+
REQUIRED props (not axes): name
|
|
135
|
+
|
|
136
|
+
Sidebar (Shell)
|
|
137
|
+
state: menu | submenu
|
|
138
|
+
booleans: collapsed, open, active, expanded, nested
|
|
139
|
+
REQUIRED props (not axes): label
|
|
140
|
+
|
|
141
|
+
Header (Shell)
|
|
142
|
+
type: nonLogin | nonLoginBlack | login | temanAiLearn | temanAiChat | dashboard | detailPage
|
|
143
|
+
booleans: showBack, showTab, showTextLeft, scrolled
|
|
144
|
+
|
|
145
|
+
AppShell (Shell)
|
|
146
|
+
booleans: scrolled
|
|
147
|
+
|
|
148
|
+
Lightbox (Overlays)
|
|
149
|
+
REQUIRED props (not axes): open, onOpenChange, src
|
|
150
|
+
|
|
151
|
+
ChatComposer (Chat)
|
|
152
|
+
tone: neutral | info | warning | danger
|
|
153
|
+
REQUIRED props (not axes): value, onValueChange, editorLabel
|
|
154
|
+
|
|
155
|
+
ChatComposerInput (Chat)
|
|
156
|
+
REQUIRED props (not axes): value, onValueChange, label
|
|
157
|
+
|
|
158
|
+
ChatDrawer (Chat)
|
|
159
|
+
variant: inherit | neutral | info | warning | danger
|
|
160
|
+
booleans: collapsed
|
|
161
|
+
|
|
162
|
+
ChatAttachment (Chat)
|
|
163
|
+
variant: image | file
|
|
164
|
+
booleans: showSize
|
|
165
|
+
REQUIRED props (not axes): name
|
|
166
|
+
|
|
167
|
+
ChatToolsButton (Chat)
|
|
168
|
+
state: default | active
|
|
169
|
+
REQUIRED props (not axes): label
|
|
170
|
+
|
|
171
|
+
ChatAttachmentButton (Chat)
|
|
172
|
+
state: default | active
|
|
173
|
+
REQUIRED props (not axes): label
|
|
174
|
+
|
|
175
|
+
ChatVoiceButton (Chat)
|
|
176
|
+
state: default | active
|
|
177
|
+
REQUIRED props (not axes): label
|
|
178
|
+
|
|
179
|
+
ChatSendButton (Chat)
|
|
180
|
+
state: send | stop | waiting
|
|
181
|
+
REQUIRED props (not axes): label
|
|
182
|
+
|
|
183
|
+
ChatSoundWave (Chat)
|
|
184
|
+
state: idle | listening
|
|
185
|
+
|
|
186
|
+
ChatVoiceBar (Chat)
|
|
187
|
+
size: compact | landing
|
|
188
|
+
REQUIRED props (not axes): size, label, stopLabel, onStop
|
|
189
|
+
|
|
190
|
+
ChatMessage (Chat)
|
|
191
|
+
sender: assistant | user | system
|
|
192
|
+
density: compact | balanced | spacious
|
|
193
|
+
|
|
194
|
+
ChatMessageBubble (Chat)
|
|
195
|
+
group: none | first | middle | last
|
|
196
|
+
width: auto | full
|
|
197
|
+
booleans: ghost
|
|
198
|
+
|
|
199
|
+
ChatMessageMetadata (Chat)
|
|
200
|
+
status: sending | sent | delivered | read | error
|
|
201
|
+
|
|
202
|
+
ChatMessageList (Chat)
|
|
203
|
+
density: compact | balanced | spacious
|
|
204
|
+
align: top | bottom
|
|
205
|
+
gap: none | 2xs | xs | sm | md | lg
|
|
206
|
+
booleans: streaming
|
|
207
|
+
|
|
208
|
+
ChatThread (Chat)
|
|
209
|
+
|
|
210
|
+
ChatScrollButton (Chat)
|
|
211
|
+
shape: icon | labelled
|
|
212
|
+
booleans: hasNewMessages
|
|
213
|
+
REQUIRED props (not axes): label
|
|
214
|
+
|
|
215
|
+
ChatMessageTTSButton (Chat)
|
|
216
|
+
state: idle | loading | playing | paused | error
|
|
217
|
+
REQUIRED props (not axes): state, label, onToggle
|
|
218
|
+
|
|
219
|
+
Dialog (Overlays)
|
|
220
|
+
size: small | medium | large | xlarge
|
|
221
|
+
side: none | left | right
|
|
222
|
+
booleans: centered, showCloseButton, dismissible, closeDisabled
|
|
223
|
+
REQUIRED props (not axes): open, onOpenChange
|
|
224
|
+
|
|
225
|
+
Menu (Overlays)
|
|
226
|
+
variant: pill | boxed
|
|
227
|
+
REQUIRED props (not axes): trigger, items
|
|
228
|
+
|
|
229
|
+
Popover (Overlays)
|
|
230
|
+
align: start | center | end
|
|
231
|
+
booleans: portal
|
|
232
|
+
|
|
233
|
+
Card (Content)
|
|
234
|
+
variant: elevated | outlined | plain
|
|
235
|
+
padding: none | small | medium | large
|
|
236
|
+
appearance: default | a2ui | a2ui-hero
|
|
237
|
+
|
|
238
|
+
Divider (Content)
|
|
239
|
+
variant: default | dashed
|
|
240
|
+
orientation: horizontal | vertical
|
|
241
|
+
appearance: default | a2ui
|
|
242
|
+
|
|
243
|
+
List (Content)
|
|
244
|
+
|
|
245
|
+
Disclosure (Content)
|
|
246
|
+
appearance: default | a2ui
|
|
247
|
+
REQUIRED props (not axes): title
|
|
248
|
+
|
|
249
|
+
Image (Content)
|
|
250
|
+
type: thumbnail | banner | natural | avatar
|
|
251
|
+
size: regular | small | blog
|
|
252
|
+
appearance: default | a2ui
|
|
253
|
+
booleans: hover
|
|
254
|
+
|
|
255
|
+
SelectionCard (Form)
|
|
256
|
+
size: large | small
|
|
257
|
+
state: default | selected | disabled
|
|
258
|
+
layout: row | tile
|
|
259
|
+
appearance: default | choice
|
|
260
|
+
REQUIRED props (not axes): options
|
|
261
|
+
|
|
262
|
+
Upload (Form)
|
|
263
|
+
size: large | small
|
|
264
|
+
status: default | filled | error | disabled | busy
|
|
265
|
+
border: solid | dashed
|
|
266
|
+
appearance: default | a2ui
|
|
267
|
+
|
|
268
|
+
Select (Form)
|
|
269
|
+
size: large | small
|
|
270
|
+
status: default | filled | error | disabled
|
|
271
|
+
booleans: multiple, portal
|
|
272
|
+
REQUIRED props (not axes): options
|
|
273
|
+
|
|
274
|
+
CodeBlock (Content)
|
|
275
|
+
size: large | small
|
|
276
|
+
appearance: default | a2ui
|
|
277
|
+
|
|
278
|
+
Stepper (Navigation)
|
|
279
|
+
position: first | middle | last
|
|
280
|
+
state: default | current | done
|
|
281
|
+
orientation: horizontal | vertical
|
|
282
|
+
REQUIRED props (not axes): steps
|
|
283
|
+
|
|
284
|
+
Pagination (Navigation)
|
|
285
|
+
size: large | small
|
|
286
|
+
density: compact | comfortable
|
|
287
|
+
booleans: disabled
|
|
288
|
+
REQUIRED props (not axes): page, onPageChange
|
|
289
|
+
|
|
290
|
+
Table (Content)
|
|
291
|
+
density: compact | comfortable
|
|
292
|
+
booleans: loading, serverSide, hidePagination, fixedLayout, noBorder, slimScrollbar
|
|
293
|
+
|
|
294
|
+
Spinner (Feedback)
|
|
295
|
+
variant: primary | white | gray | gradient
|
|
296
|
+
size: sm | md | lg | xl
|
|
297
|
+
|
|
298
|
+
ProgressBar (Feedback)
|
|
299
|
+
variant: default | success | warning | error | gray | gradient
|
|
300
|
+
size: large | medium | small
|
|
301
|
+
shape: linear | radial
|
|
302
|
+
|
|
303
|
+
Toaster (Feedback)
|
|
304
|
+
position: top-left | top-center | top-right | bottom-left | bottom-center | bottom-right
|
|
305
|
+
|
|
306
|
+
Label (Form)
|
|
307
|
+
variant: default | error | success
|
|
308
|
+
size: sm | md | lg
|
|
309
|
+
booleans: required
|
|
310
|
+
|
|
311
|
+
TRAPS — each of these renders or ignores silently, with no error:
|
|
312
|
+
|
|
313
|
+
1. `Text` roles are camelCase: `bodyLg`, `paragraphSm`, `labelMd`. A kebab-case or Title-Case role matches no variant and the text renders with no type styling at all.
|
|
314
|
+
2. `Header`’s `title` is rendered ONLY when `showTextLeft` is also passed. `title` alone is silently dropped — the header renders, without its title.
|
|
315
|
+
3. `Alert` has no `title` prop. Put the heading in `children`; a `title` passed to it lands on the underlying `div` as a DOM attribute and shows up as a tooltip.
|
|
316
|
+
4. THE AXES ARE NOT THE PROP LIST. An axis is a STYLING choice; a component can also require DATA, and any entry above with a “REQUIRED props” line will not render without it. `Stepper` is the one that catches everyone: its axes are `position`, `state` and `orientation`, and all three belong to a STEP — the component itself takes `steps={[{ label, state, position }]}`. Writing `<Stepper position="first" state="done" />` throws inside the component, React unmounts the whole tree, and the page goes BLANK. Same shape for `SelectionCard` (`options`), `Select` (`options`), `Menu` (`trigger`, `items`) and `Dialog` (`open`, `onOpenChange`).
|
|
317
|
+
5. AN AXIS NAME IS NOT ALWAYS THE PROP NAME. Every axis listed above is passed under its own name EXCEPT two: write `variant` (not `function`) for `Alert`, and `variant` (not `role`) for `Text` — taking the legal values from the `function` and `role` axes respectively. Those two keep Figma’s word in the contract because `function` is a reserved word and `role` is an ARIA attribute. Passing the axis name instead of the prop renders the component with no styling and no error.
|
|
318
|
+
6. The `Table` entry exports the plain parts AND `DataTable`, and the REQUIRED props line above reads only the parts. `DataTable` needs `columns` (tanstack `ColumnDef`s, e.g. `[{ accessorKey: "name", header: "Nama" }]`) and `data`; without them it throws and the page goes blank. `Pagination` needs `page` (1-based) and `onPageChange`; pass `pageCount` for a numbered strip, or omit it and pass `hasNext` for a cursor source, or next stays disabled.
|
|
319
|
+
|
|
320
|
+
-->
|
|
321
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
322
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
323
|
+
<link href="https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
|
324
|
+
<style>
|
|
325
|
+
/* Generated from Ensyra — DS by `npm run tokens:build`. Do not edit by hand. */
|
|
326
|
+
:root {
|
|
327
|
+
--brand-b600: 0 61 121;
|
|
328
|
+
--brand-b500: 0 81 162;
|
|
329
|
+
--brand-b400: 0 116 231;
|
|
330
|
+
--brand-b300: 43 140 235;
|
|
331
|
+
--brand-b200: 107 174 241;
|
|
332
|
+
--brand-b100: 150 198 245;
|
|
333
|
+
--brand-b10: 240 247 255;
|
|
334
|
+
--brand-b10-pct: 0 116 231;
|
|
335
|
+
--neutral-n999: 0 0 0;
|
|
336
|
+
--neutral-n600: 2 22 41;
|
|
337
|
+
--neutral-n500: 3 32 61;
|
|
338
|
+
--neutral-n400: 4 38 72;
|
|
339
|
+
--neutral-n300: 49 77 105;
|
|
340
|
+
--neutral-n200: 97 118 140;
|
|
341
|
+
--neutral-n100: 147 162 176;
|
|
342
|
+
--neutral-n40: 195 203 211;
|
|
343
|
+
--neutral-n30: 240 240 240;
|
|
344
|
+
--neutral-n20: 246 246 246;
|
|
345
|
+
--neutral-n10: 251 251 251;
|
|
346
|
+
--neutral-n0: 255 255 255;
|
|
347
|
+
--red-r600: 124 0 0;
|
|
348
|
+
--red-r500: 162 0 0;
|
|
349
|
+
--red-r400: 231 0 0;
|
|
350
|
+
--red-r450: 219 0 0;
|
|
351
|
+
--red-r300: 235 43 43;
|
|
352
|
+
--red-r200: 241 107 107;
|
|
353
|
+
--red-r100: 245 150 150;
|
|
354
|
+
--red-r10: 255 243 243;
|
|
355
|
+
--red-r10-pct: 231 0 0;
|
|
356
|
+
--green-g600: 0 88 71;
|
|
357
|
+
--green-g500: 8 118 96;
|
|
358
|
+
--green-g400: 14 160 110;
|
|
359
|
+
--green-g300: 28 198 142;
|
|
360
|
+
--green-g200: 110 233 192;
|
|
361
|
+
--green-g100: 153 239 211;
|
|
362
|
+
--green-g10: 236 253 247;
|
|
363
|
+
--green-g10-pct: 14 160 110;
|
|
364
|
+
--yellow-y600: 149 96 7;
|
|
365
|
+
--yellow-y500: 172 111 8;
|
|
366
|
+
--yellow-y400: 245 158 11;
|
|
367
|
+
--yellow-y300: 251 188 4;
|
|
368
|
+
--yellow-y200: 253 216 109;
|
|
369
|
+
--yellow-y100: 253 228 152;
|
|
370
|
+
--yellow-y10: 255 248 230;
|
|
371
|
+
--yellow-y10-pct: 245 158 11;
|
|
372
|
+
--font-size-3xs: 8;
|
|
373
|
+
--font-size-2xs: 10;
|
|
374
|
+
--font-size-xs: 12;
|
|
375
|
+
--font-size-sm: 14;
|
|
376
|
+
--font-size-md: 16;
|
|
377
|
+
--font-size-lg: 18;
|
|
378
|
+
--font-size-xl: 20;
|
|
379
|
+
--font-size-2xl: 22;
|
|
380
|
+
--font-size-3xl: 24;
|
|
381
|
+
--font-size-4xl: 28;
|
|
382
|
+
--font-size-5xl: 32;
|
|
383
|
+
--font-size-6xl: 36;
|
|
384
|
+
--font-size-7xl: 40;
|
|
385
|
+
--font-size-8xl: 56;
|
|
386
|
+
--font-letter-spacing-none: 0;
|
|
387
|
+
--font-letter-spacing-sm: -0.10000000149011612;
|
|
388
|
+
--font-letter-spacing-md: -0.30000001192092896;
|
|
389
|
+
--font-letter-spacing-lg: -1;
|
|
390
|
+
--font-line-height-4xs: 14;
|
|
391
|
+
--font-line-height-3xs: 16;
|
|
392
|
+
--font-line-height-2xs: 18;
|
|
393
|
+
--font-line-height-xs: 20;
|
|
394
|
+
--font-line-height-sm: 22;
|
|
395
|
+
--font-line-height-md: 24;
|
|
396
|
+
--font-line-height-lg: 26;
|
|
397
|
+
--font-line-height-xl: 28;
|
|
398
|
+
--font-line-height-2xl: 32;
|
|
399
|
+
--font-line-height-3xl: 36;
|
|
400
|
+
--font-line-height-4xl: 40;
|
|
401
|
+
--font-line-height-5xl: 48;
|
|
402
|
+
--font-line-height-6xl: 64;
|
|
403
|
+
--font-familiy-brand: Be Vietnam Pro;
|
|
404
|
+
--font-weight-regular: Regular;
|
|
405
|
+
--font-weight-medium: Medium;
|
|
406
|
+
--font-weight-semibold: SemiBold;
|
|
407
|
+
--font-weight-bold: Bold;
|
|
408
|
+
--percentage-0-pct: 0;
|
|
409
|
+
--percentage-10-pct: 10;
|
|
410
|
+
--percentage-60-pct: 60;
|
|
411
|
+
--percentage-100-pct: 100;
|
|
412
|
+
--measurement-0: 0;
|
|
413
|
+
--measurement-1: 16;
|
|
414
|
+
--measurement-2: 32;
|
|
415
|
+
--measurement-3: 48;
|
|
416
|
+
--measurement-4: 64;
|
|
417
|
+
--measurement-5: 80;
|
|
418
|
+
--measurement-0-0625: 1;
|
|
419
|
+
--measurement-0-125: 2;
|
|
420
|
+
--measurement-0-25: 4;
|
|
421
|
+
--measurement-0-375: 6;
|
|
422
|
+
--measurement-0-5: 8;
|
|
423
|
+
--measurement-0-625: 10;
|
|
424
|
+
--measurement-0-75: 12;
|
|
425
|
+
--measurement-1-25: 20;
|
|
426
|
+
--measurement-1-375: 22;
|
|
427
|
+
--measurement-1-5: 24;
|
|
428
|
+
--measurement-1-625: 26;
|
|
429
|
+
--measurement-1-75: 28;
|
|
430
|
+
--measurement-2-25: 36;
|
|
431
|
+
--measurement-2-5: 40;
|
|
432
|
+
--measurement-2-75: 44;
|
|
433
|
+
--measurement-3-5: 56;
|
|
434
|
+
--measurement-4-5: 72;
|
|
435
|
+
--measurement-5-25: 84;
|
|
436
|
+
--measurement-7-5: 120;
|
|
437
|
+
--measurement-full: 360;
|
|
438
|
+
--text-default-primary: 4 38 72;
|
|
439
|
+
--text-default-secondary: 147 162 176;
|
|
440
|
+
--text-default-invert: 255 255 255;
|
|
441
|
+
--text-default-brand: 0 116 231;
|
|
442
|
+
--text-default-success: 14 160 110;
|
|
443
|
+
--text-default-error: 219 0 0;
|
|
444
|
+
--text-default-warning: 245 158 11;
|
|
445
|
+
--text-hover-primary: 3 32 61;
|
|
446
|
+
--text-hover-secondary: 97 118 140;
|
|
447
|
+
--text-hover-invert: 246 246 246;
|
|
448
|
+
--text-hover-brand: 0 81 162;
|
|
449
|
+
--text-hover-success: 8 118 96;
|
|
450
|
+
--text-hover-error: 162 0 0;
|
|
451
|
+
--text-hover-warning: 172 111 8;
|
|
452
|
+
--text-active-primary: 2 22 41;
|
|
453
|
+
--text-active-secondary: 49 77 105;
|
|
454
|
+
--text-active-invert: 240 240 240;
|
|
455
|
+
--text-active-brand: 0 61 121;
|
|
456
|
+
--text-active-success: 0 88 71;
|
|
457
|
+
--text-active-error: 124 0 0;
|
|
458
|
+
--text-active-warning: 149 96 7;
|
|
459
|
+
--text-focus-primary: 4 38 72;
|
|
460
|
+
--text-focus-invert: 255 255 255;
|
|
461
|
+
--text-focus-brand: 0 116 231;
|
|
462
|
+
--text-focus-success: 14 160 110;
|
|
463
|
+
--text-focus-error: 231 0 0;
|
|
464
|
+
--text-focus-warning: 245 158 11;
|
|
465
|
+
--text-disable-primary: 195 203 211;
|
|
466
|
+
--text-disable-brand: 150 198 245;
|
|
467
|
+
--text-disable-success: 153 239 211;
|
|
468
|
+
--text-disable-error: 245 150 150;
|
|
469
|
+
--text-disable-warning: 253 228 152;
|
|
470
|
+
--text-disable-invert: 49 77 105;
|
|
471
|
+
--icon-default-primary: 4 38 72;
|
|
472
|
+
--icon-default-secondary: 147 162 176;
|
|
473
|
+
--icon-default-invert: 255 255 255;
|
|
474
|
+
--icon-default-brand: 0 116 231;
|
|
475
|
+
--icon-default-success: 14 160 110;
|
|
476
|
+
--icon-default-error: 219 0 0;
|
|
477
|
+
--icon-default-warning: 245 158 11;
|
|
478
|
+
--icon-hover-primary: 3 32 61;
|
|
479
|
+
--icon-hover-secondary: 97 118 140;
|
|
480
|
+
--icon-hover-invert: 246 246 246;
|
|
481
|
+
--icon-hover-brand: 0 81 162;
|
|
482
|
+
--icon-hover-success: 8 118 96;
|
|
483
|
+
--icon-hover-error: 162 0 0;
|
|
484
|
+
--icon-hover-warning: 172 111 8;
|
|
485
|
+
--icon-active-primary: 2 22 41;
|
|
486
|
+
--icon-active-secondary: 49 77 105;
|
|
487
|
+
--icon-active-invert: 240 240 240;
|
|
488
|
+
--icon-active-brand: 0 61 121;
|
|
489
|
+
--icon-active-success: 0 88 71;
|
|
490
|
+
--icon-active-error: 124 0 0;
|
|
491
|
+
--icon-active-warning: 149 96 7;
|
|
492
|
+
--icon-focus-primary: 4 38 72;
|
|
493
|
+
--icon-focus-invert: 255 255 255;
|
|
494
|
+
--icon-focus-brand: 0 116 231;
|
|
495
|
+
--icon-focus-success: 14 160 110;
|
|
496
|
+
--icon-focus-error: 231 0 0;
|
|
497
|
+
--icon-focus-warning: 245 158 11;
|
|
498
|
+
--icon-disable-primary: 195 203 211;
|
|
499
|
+
--icon-disable-brand: 150 198 245;
|
|
500
|
+
--icon-disable-success: 153 239 211;
|
|
501
|
+
--icon-disable-error: 245 150 150;
|
|
502
|
+
--icon-disable-warning: 253 228 152;
|
|
503
|
+
--icon-disable-invert: 49 77 105;
|
|
504
|
+
--surface-default-primary: 255 255 255;
|
|
505
|
+
--surface-default-secondary: 246 246 246;
|
|
506
|
+
--surface-default-invert: 0 0 0;
|
|
507
|
+
--surface-default-brand: 0 116 231;
|
|
508
|
+
--surface-default-success: 14 160 110;
|
|
509
|
+
--surface-default-error: 231 0 0;
|
|
510
|
+
--surface-default-warning: 245 158 11;
|
|
511
|
+
--surface-default-overlay: 4 38 72;
|
|
512
|
+
--surface-hover-primary: 240 247 255;
|
|
513
|
+
--surface-hover-secondary: 240 240 240;
|
|
514
|
+
--surface-hover-invert: 2 22 41;
|
|
515
|
+
--surface-hover-brand: 0 81 162;
|
|
516
|
+
--surface-hover-success: 8 118 96;
|
|
517
|
+
--surface-hover-error: 162 0 0;
|
|
518
|
+
--surface-hover-warning: 172 111 8;
|
|
519
|
+
--surface-active-primary: 240 240 240;
|
|
520
|
+
--surface-active-brand: 0 61 121;
|
|
521
|
+
--surface-active-success: 0 88 71;
|
|
522
|
+
--surface-active-error: 124 0 0;
|
|
523
|
+
--surface-active-warning: 149 96 7;
|
|
524
|
+
--surface-focus-brand: 0 116 231;
|
|
525
|
+
--surface-focus-success: 14 160 110;
|
|
526
|
+
--surface-focus-error: 231 0 0;
|
|
527
|
+
--surface-focus-warning: 245 158 11;
|
|
528
|
+
--surface-disable-primary: 240 240 240;
|
|
529
|
+
--surface-disable-brand: 150 198 245;
|
|
530
|
+
--surface-disable-success: 153 239 211;
|
|
531
|
+
--surface-disable-error: 245 150 150;
|
|
532
|
+
--surface-disable-warning: 253 228 152;
|
|
533
|
+
--surface-disable-invert: 3 32 61;
|
|
534
|
+
--surface-weak-brand: 240 247 255;
|
|
535
|
+
--surface-weak-error: 255 243 243;
|
|
536
|
+
--surface-weak-success: 236 253 247;
|
|
537
|
+
--surface-weak-warning: 255 248 230;
|
|
538
|
+
--surface-weak-neutral: 246 246 246;
|
|
539
|
+
--surface-gradient-brand-start: 0 116 231;
|
|
540
|
+
--surface-gradient-brand-end: 0 81 162;
|
|
541
|
+
--surface-gradient-green-start: 14 160 110;
|
|
542
|
+
--surface-gradient-green-end: 8 118 96;
|
|
543
|
+
--surface-gradient-red-start: 231 0 0;
|
|
544
|
+
--surface-gradient-red-end: 162 0 0;
|
|
545
|
+
--surface-gradient-yellow-start: 245 158 11;
|
|
546
|
+
--surface-gradient-yellow-end: 172 111 8;
|
|
547
|
+
--surface-gradient-callout-start: 0 81 162;
|
|
548
|
+
--surface-gradient-callout-end: 8 118 96;
|
|
549
|
+
--surface-hover-neutral: 247 248 250;
|
|
550
|
+
--surface-selected-hover: 227 239 252;
|
|
551
|
+
--border-default-primary: 240 240 240;
|
|
552
|
+
--border-default-invert: 195 203 211;
|
|
553
|
+
--border-default-brand: 0 116 231;
|
|
554
|
+
--border-default-success: 14 160 110;
|
|
555
|
+
--border-default-error: 231 0 0;
|
|
556
|
+
--border-default-warning: 245 158 11;
|
|
557
|
+
--border-default-black: 4 38 72;
|
|
558
|
+
--border-default-light: 255 255 255;
|
|
559
|
+
--border-hover-primary: 195 203 211;
|
|
560
|
+
--border-hover-brand: 0 81 162;
|
|
561
|
+
--border-hover-success: 8 118 96;
|
|
562
|
+
--border-hover-error: 162 0 0;
|
|
563
|
+
--border-hover-warning: 172 111 8;
|
|
564
|
+
--border-hover-invert: 3 32 61;
|
|
565
|
+
--border-hover-light: 246 246 246;
|
|
566
|
+
--border-active-primary: 147 162 176;
|
|
567
|
+
--border-active-brand: 0 61 121;
|
|
568
|
+
--border-active-success: 0 88 71;
|
|
569
|
+
--border-active-error: 124 0 0;
|
|
570
|
+
--border-active-warning: 149 96 7;
|
|
571
|
+
--border-active-invert: 2 22 41;
|
|
572
|
+
--border-active-light: 240 240 240;
|
|
573
|
+
--border-focus-brand: 0 116 231;
|
|
574
|
+
--border-focus-success: 14 160 110;
|
|
575
|
+
--border-focus-error: 231 0 0;
|
|
576
|
+
--border-focus-warning: 245 158 11;
|
|
577
|
+
--border-disable-primary: 195 203 211;
|
|
578
|
+
--border-disable-brand: 150 198 245;
|
|
579
|
+
--border-disable-success: 153 239 211;
|
|
580
|
+
--border-disable-error: 245 150 150;
|
|
581
|
+
--border-disable-warning: 253 228 152;
|
|
582
|
+
--border-disable-invert: 49 77 105;
|
|
583
|
+
--radius-null: 0;
|
|
584
|
+
--radius-xs: 4;
|
|
585
|
+
--radius-sm: 6;
|
|
586
|
+
--radius-md: 8;
|
|
587
|
+
--radius-lg: 12;
|
|
588
|
+
--radius-xl: 16;
|
|
589
|
+
--radius-2xl: 24;
|
|
590
|
+
--radius-3xl: 32;
|
|
591
|
+
--radius-full: 360;
|
|
592
|
+
--spacing-null: 0;
|
|
593
|
+
--spacing-5xs: 2;
|
|
594
|
+
--spacing-4xs: 4;
|
|
595
|
+
--spacing-3xs: 6;
|
|
596
|
+
--spacing-2xs: 8;
|
|
597
|
+
--spacing-xs: 10;
|
|
598
|
+
--spacing-sm: 12;
|
|
599
|
+
--spacing-md: 16;
|
|
600
|
+
--spacing-lg: 20;
|
|
601
|
+
--spacing-xl: 24;
|
|
602
|
+
--spacing-2xl: 32;
|
|
603
|
+
--spacing-3xl-mobile: 48;
|
|
604
|
+
--spacing-4xl: 84;
|
|
605
|
+
--spacing-5xl-desktop: 120;
|
|
606
|
+
--margin-mobile: 16;
|
|
607
|
+
--margin-desktop: 84;
|
|
608
|
+
--shadow-blur-none: 0;
|
|
609
|
+
--shadow-blur-sm: 4;
|
|
610
|
+
--shadow-blur-md: 12;
|
|
611
|
+
--shadow-blur-lg: 16;
|
|
612
|
+
--shadow-blur-xl: 20;
|
|
613
|
+
--shadow-position-none: 0;
|
|
614
|
+
--shadow-position-sm: 2;
|
|
615
|
+
--shadow-position-md: 4;
|
|
616
|
+
--shadow-position-lg: 8;
|
|
617
|
+
--shadow-position-xl: 12;
|
|
618
|
+
--shadow-5-pct: 0 4px 12px 0 #0000000d;
|
|
619
|
+
--shadow-card: 0 2px 8px 0 #0000001a;
|
|
620
|
+
--shadow-10-pct: 0 4px 16px 0 #0000001a;
|
|
621
|
+
--opacity-10: 10;
|
|
622
|
+
--opacity-60: 60;
|
|
623
|
+
--opacity-null: 0;
|
|
624
|
+
--opacity-full: 100;
|
|
625
|
+
--border-width-none: 0;
|
|
626
|
+
--border-width-sm: 1;
|
|
627
|
+
--border-width-md: 2;
|
|
628
|
+
--border-width-l: 4;
|
|
629
|
+
--callout-default-surface: 240 247 255;
|
|
630
|
+
--callout-default-border: 0 116 231;
|
|
631
|
+
--callout-default-text: 0 81 162;
|
|
632
|
+
--callout-coach-surface: 236 253 247;
|
|
633
|
+
--callout-coach-border: 8 118 96;
|
|
634
|
+
--callout-coach-text: 8 118 96;
|
|
635
|
+
--callout-good-surface: 236 253 247;
|
|
636
|
+
--callout-good-border: 8 118 96;
|
|
637
|
+
--callout-good-text: 4 38 72;
|
|
638
|
+
--callout-warn-surface: 255 248 230;
|
|
639
|
+
--callout-warn-border: 172 111 8;
|
|
640
|
+
--callout-warn-text: 4 38 72;
|
|
641
|
+
--callout-neg-surface: 255 243 243;
|
|
642
|
+
--callout-neg-border: 231 0 0;
|
|
643
|
+
--callout-neg-text: 4 38 72;
|
|
644
|
+
--focus-halo: 150 198 245;
|
|
645
|
+
--typography-mobile-display-bold: 700 calc(2.25rem * var(--ds-text-scale, 1))/calc(2.5rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
646
|
+
--typography-mobile-display-semibold: 600 calc(2.25rem * var(--ds-text-scale, 1))/calc(2.5rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
647
|
+
--typography-mobile-h1-bold: 700 calc(1.75rem * var(--ds-text-scale, 1))/calc(2rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
648
|
+
--typography-mobile-h1-semibold: 600 calc(1.75rem * var(--ds-text-scale, 1))/calc(2rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
649
|
+
--typography-mobile-h2-bold: 700 calc(1.5rem * var(--ds-text-scale, 1))/calc(1.75rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
650
|
+
--typography-mobile-h2-semibold: 600 calc(1.5rem * var(--ds-text-scale, 1))/calc(1.75rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
651
|
+
--typography-mobile-h2-medium: 500 calc(1.5rem * var(--ds-text-scale, 1))/calc(1.75rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
652
|
+
--typography-mobile-h2-regular: 400 calc(1.5rem * var(--ds-text-scale, 1))/calc(1.75rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
653
|
+
--typography-mobile-h3-bold: 700 calc(1.25rem * var(--ds-text-scale, 1))/calc(1.625rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
654
|
+
--typography-mobile-h3-semibold: 600 calc(1.25rem * var(--ds-text-scale, 1))/calc(1.625rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
655
|
+
--typography-mobile-h3-medium: 500 calc(1.25rem * var(--ds-text-scale, 1))/calc(1.625rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
656
|
+
--typography-mobile-h3-regular: 400 calc(1.25rem * var(--ds-text-scale, 1))/calc(1.625rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
657
|
+
--typography-mobile-h4-bold: 700 calc(1.25rem * var(--ds-text-scale, 1))/calc(1.625rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
658
|
+
--typography-mobile-h4-semibold: 600 calc(1.25rem * var(--ds-text-scale, 1))/calc(1.625rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
659
|
+
--typography-mobile-h4-medium: 500 calc(1.25rem * var(--ds-text-scale, 1))/calc(1.625rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
660
|
+
--typography-mobile-h4-regular: 400 calc(1.25rem * var(--ds-text-scale, 1))/calc(1.625rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
661
|
+
--typography-mobile-h5-bold: 700 calc(1.125rem * var(--ds-text-scale, 1))/calc(1.5rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
662
|
+
--typography-mobile-h5-semibold: 600 calc(1.125rem * var(--ds-text-scale, 1))/calc(1.5rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
663
|
+
--typography-mobile-h5-medium: 500 calc(1.125rem * var(--ds-text-scale, 1))/calc(1.5rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
664
|
+
--typography-mobile-h5-regular: 400 calc(1.125rem * var(--ds-text-scale, 1))/calc(1.5rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
665
|
+
--typography-mobile-h6-bold: 700 calc(1rem * var(--ds-text-scale, 1))/calc(1.5rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
666
|
+
--typography-mobile-h6-semibold: 600 calc(1rem * var(--ds-text-scale, 1))/calc(1.5rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
667
|
+
--typography-mobile-h6-medium: 500 calc(1rem * var(--ds-text-scale, 1))/calc(1.5rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
668
|
+
--typography-mobile-h6-regular: 400 calc(1rem * var(--ds-text-scale, 1))/calc(1.5rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
669
|
+
--typography-mobile-paragraph-bold: 700 calc(0.875rem * var(--ds-text-scale, 1))/calc(1.25rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
670
|
+
--typography-mobile-paragraph-semibold: 600 calc(0.875rem * var(--ds-text-scale, 1))/calc(1.25rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
671
|
+
--typography-mobile-paragraph-medium: 500 calc(0.875rem * var(--ds-text-scale, 1))/calc(1.25rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
672
|
+
--typography-mobile-paragraph-regular: 400 calc(0.875rem * var(--ds-text-scale, 1))/calc(1.25rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
673
|
+
--typography-mobile-body-lg-bold: 700 calc(0.75rem * var(--ds-text-scale, 1))/calc(1.125rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
674
|
+
--typography-mobile-body-lg-semibold: 600 calc(0.75rem * var(--ds-text-scale, 1))/calc(1.125rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
675
|
+
--typography-mobile-body-lg-medium: 500 calc(0.75rem * var(--ds-text-scale, 1))/calc(1.125rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
676
|
+
--typography-mobile-body-lg-regular: 400 calc(0.75rem * var(--ds-text-scale, 1))/calc(1.125rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
677
|
+
--typography-mobile-body-md-bold: 700 calc(0.625rem * var(--ds-text-scale, 1))/calc(1rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
678
|
+
--typography-mobile-body-md-semibold: 600 calc(0.625rem * var(--ds-text-scale, 1))/calc(1rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
679
|
+
--typography-mobile-body-md-medium: 500 calc(0.625rem * var(--ds-text-scale, 1))/calc(1rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
680
|
+
--typography-mobile-body-md-regular: 400 calc(0.625rem * var(--ds-text-scale, 1))/calc(1rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
681
|
+
--typography-mobile-body-sm-bold: 700 calc(0.5rem * var(--ds-text-scale, 1))/calc(0.875rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
682
|
+
--typography-mobile-body-sm-semibold: 600 calc(0.5rem * var(--ds-text-scale, 1))/calc(0.875rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
683
|
+
--typography-mobile-body-sm-medium: 500 calc(0.5rem * var(--ds-text-scale, 1))/calc(0.875rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
684
|
+
--typography-mobile-body-sm-regular: 400 calc(0.5rem * var(--ds-text-scale, 1))/calc(0.875rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
685
|
+
--typography-desktop-display-bold: 700 calc(3.5rem * var(--ds-text-scale, 1))/calc(4rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
686
|
+
--typography-desktop-display-semibold: 600 calc(3.5rem * var(--ds-text-scale, 1))/calc(4rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
687
|
+
--typography-desktop-h1-bold: 700 calc(2.5rem * var(--ds-text-scale, 1))/calc(3rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
688
|
+
--typography-desktop-h1-semibold: 600 calc(2.5rem * var(--ds-text-scale, 1))/calc(3rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
689
|
+
--typography-desktop-h2-bold: 700 calc(2rem * var(--ds-text-scale, 1))/calc(2.5rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
690
|
+
--typography-desktop-h2-semibold: 600 calc(2rem * var(--ds-text-scale, 1))/calc(2.5rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
691
|
+
--typography-desktop-h2-medium: 500 calc(2rem * var(--ds-text-scale, 1))/calc(2.5rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
692
|
+
--typography-desktop-h2-regular: 400 calc(2rem * var(--ds-text-scale, 1))/calc(2.5rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
693
|
+
--typography-desktop-h3-bold: 700 calc(1.75rem * var(--ds-text-scale, 1))/calc(2.25rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
694
|
+
--typography-desktop-h3-semibold: 600 calc(1.75rem * var(--ds-text-scale, 1))/calc(2.25rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
695
|
+
--typography-desktop-h3-medium: 500 calc(1.75rem * var(--ds-text-scale, 1))/calc(2.25rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
696
|
+
--typography-desktop-h3-regular: 400 calc(1.75rem * var(--ds-text-scale, 1))/calc(2.25rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
697
|
+
--typography-desktop-h4-bold: 700 calc(1.5rem * var(--ds-text-scale, 1))/calc(2rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
698
|
+
--typography-desktop-h4-semibold: 600 calc(1.5rem * var(--ds-text-scale, 1))/calc(2rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
699
|
+
--typography-desktop-h4-medium: 500 calc(1.5rem * var(--ds-text-scale, 1))/calc(2rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
700
|
+
--typography-desktop-h4-regular: 400 calc(1.5rem * var(--ds-text-scale, 1))/calc(2rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
701
|
+
--typography-desktop-h5-bold: 700 calc(1.25rem * var(--ds-text-scale, 1))/calc(1.75rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
702
|
+
--typography-desktop-h5-semibold: 600 calc(1.25rem * var(--ds-text-scale, 1))/calc(1.75rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
703
|
+
--typography-desktop-h5-medium: 500 calc(1.25rem * var(--ds-text-scale, 1))/calc(1.75rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
704
|
+
--typography-desktop-h5-regular: 400 calc(1.25rem * var(--ds-text-scale, 1))/calc(1.75rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
705
|
+
--typography-desktop-h6-bold: 700 calc(1.125rem * var(--ds-text-scale, 1))/calc(1.75rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
706
|
+
--typography-desktop-h6-semibold: 600 calc(1.125rem * var(--ds-text-scale, 1))/calc(1.75rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
707
|
+
--typography-desktop-h6-medium: 500 calc(1.125rem * var(--ds-text-scale, 1))/calc(1.75rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
708
|
+
--typography-desktop-h6-regular: 400 calc(1.125rem * var(--ds-text-scale, 1))/calc(1.75rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
709
|
+
--typography-desktop-paragraph-bold: 700 calc(1rem * var(--ds-text-scale, 1))/calc(1.5rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
710
|
+
--typography-desktop-paragraph-semibold: 600 calc(1rem * var(--ds-text-scale, 1))/calc(1.5rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
711
|
+
--typography-desktop-paragraph-medium: 500 calc(1rem * var(--ds-text-scale, 1))/calc(1.5rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
712
|
+
--typography-desktop-paragraph-regular: 400 calc(1rem * var(--ds-text-scale, 1))/calc(1.5rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
713
|
+
--typography-desktop-body-lg-bold: 700 calc(0.875rem * var(--ds-text-scale, 1))/calc(1.25rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
714
|
+
--typography-desktop-body-lg-semibold: 600 calc(0.875rem * var(--ds-text-scale, 1))/calc(1.25rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
715
|
+
--typography-desktop-body-lg-medium: 500 calc(0.875rem * var(--ds-text-scale, 1))/calc(1.25rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
716
|
+
--typography-desktop-body-lg-regular: 400 calc(0.875rem * var(--ds-text-scale, 1))/calc(1.25rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
717
|
+
--typography-desktop-body-md-bold: 700 calc(0.75rem * var(--ds-text-scale, 1))/calc(1.125rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
718
|
+
--typography-desktop-body-md-semibold: 600 calc(0.75rem * var(--ds-text-scale, 1))/calc(1.125rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
719
|
+
--typography-desktop-body-md-medium: 500 calc(0.75rem * var(--ds-text-scale, 1))/calc(1.125rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
720
|
+
--typography-desktop-body-md-regular: 400 calc(0.75rem * var(--ds-text-scale, 1))/calc(1.125rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
721
|
+
--typography-desktop-body-sm-bold: 700 calc(0.625rem * var(--ds-text-scale, 1))/calc(1rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
722
|
+
--typography-desktop-body-sm-semibold: 600 calc(0.625rem * var(--ds-text-scale, 1))/calc(1rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
723
|
+
--typography-desktop-body-sm-medium: 500 calc(0.625rem * var(--ds-text-scale, 1))/calc(1rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
724
|
+
--typography-desktop-body-sm-regular: 400 calc(0.625rem * var(--ds-text-scale, 1))/calc(1rem * var(--ds-text-scale, 1)) 'Be Vietnam Pro';
|
|
725
|
+
--elevation-overlay: 99999;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/*
|
|
729
|
+
* Required by the preset's font stack — see the note in scripts/proto/build-assets.mjs.
|
|
730
|
+
* An undefined var() invalidates the declaration rather than falling through, so without
|
|
731
|
+
* this the page renders in the browser's default serif with no error anywhere.
|
|
732
|
+
*/
|
|
733
|
+
:root { --font-be-vietnam-pro: 'Be Vietnam Pro'; }
|
|
734
|
+
|
|
735
|
+
:root { color-scheme: light; }
|
|
736
|
+
body { color: rgb(var(--text-default-primary)); }
|
|
737
|
+
</style>
|
|
738
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
739
|
+
<script src="https://cdn.jsdelivr.net/npm/@umkmall/proto@0.1.0-h03e952a96ac5/umkmall-tailwind.js"></script>
|
|
740
|
+
<script src="https://cdn.jsdelivr.net/npm/@umkmall/proto@0.1.0-h03e952a96ac5/umkmall-ui.js"></script>
|
|
741
|
+
<script src="https://cdn.jsdelivr.net/npm/@babel/standalone@7.26.4/babel.min.js"></script>
|
|
742
|
+
</head>
|
|
743
|
+
<body class="bg-default-primary text-default-primary">
|
|
744
|
+
<div id="root"></div>
|
|
745
|
+
<script type="text/babel" data-presets="react">
|
|
746
|
+
/*
|
|
747
|
+
* The starter's demo: an MLJ journey as a CHAT ROOM. AUTHORED — this file is the content of the
|
|
748
|
+
* `text/babel` block in the generated starter, copied in verbatim.
|
|
749
|
+
*
|
|
750
|
+
* IT IS A CHAT APP, AND THAT IS THE WHOLE CORRECTION. The first version drew the journey as a
|
|
751
|
+
* document — a column of cards you scrolled. Every real MLJ prototype is a chat: a fixed header,
|
|
752
|
+
* the journey's chrome under it, a scrolling transcript filling the rest, a composer pinned at
|
|
753
|
+
* the bottom. Starting from a document layout means rebuilding the room before you can begin,
|
|
754
|
+
* which is exactly the hand-building this kit exists to remove.
|
|
755
|
+
*
|
|
756
|
+
* THE LIBRARY PUBLISHES FOURTEEN CHAT COMPONENTS and the document version used none of them.
|
|
757
|
+
* `ChatThread` owns the scroller and the "jump to latest" pill; `ChatMessageList` owns the
|
|
758
|
+
* measure and the bottom alignment; `ChatComposer` owns the editor, its tray and its button
|
|
759
|
+
* rows. Hand-rolling any of that is the drift this kit exists to stop.
|
|
760
|
+
*
|
|
761
|
+
* Mission 2 (Insight) runs end to end; the other three are named in the rail on the same shape.
|
|
762
|
+
* Building all four would make this file long enough that people edit it without reading it.
|
|
763
|
+
* The AI calls are STUBS — wiring a model is the author's job, and a key in a file that gets
|
|
764
|
+
* sent to other people is the thing to avoid.
|
|
765
|
+
*
|
|
766
|
+
* It is a `.jsx` file so an editor highlights it. It is never imported or bundled — it is read
|
|
767
|
+
* as text by `build-assets.mjs`, which is why it uses the globals below rather than imports.
|
|
768
|
+
*/
|
|
769
|
+
|
|
770
|
+
const {
|
|
771
|
+
React,
|
|
772
|
+
ReactDOMClient,
|
|
773
|
+
AppShell,
|
|
774
|
+
Button,
|
|
775
|
+
ChatAttachmentButton,
|
|
776
|
+
ChatComposer,
|
|
777
|
+
ChatMessage,
|
|
778
|
+
ChatMessageBubble,
|
|
779
|
+
ChatMessageList,
|
|
780
|
+
ChatMessageMetadata,
|
|
781
|
+
ChatSendButton,
|
|
782
|
+
ChatThread,
|
|
783
|
+
ChatToolsButton,
|
|
784
|
+
ChatVoiceButton,
|
|
785
|
+
Chip,
|
|
786
|
+
Dialog,
|
|
787
|
+
Header,
|
|
788
|
+
Icon,
|
|
789
|
+
ProgressBar,
|
|
790
|
+
SelectionCard,
|
|
791
|
+
Stepper,
|
|
792
|
+
Text,
|
|
793
|
+
TextField,
|
|
794
|
+
} = window.UmkmallUI;
|
|
795
|
+
|
|
796
|
+
/* THE READING COLUMN, AND NO COMPONENT OWNS IT. `AppShell`'s content slot deliberately imposes
|
|
797
|
+
no measure — that would be wrong for every non-prose surface — so the width lives here, once,
|
|
798
|
+
shared by the three regions that have to line up: chrome, transcript and composer. */
|
|
799
|
+
const COLUMN = 'mx-auto w-full max-w-3xl px-ds-md';
|
|
800
|
+
|
|
801
|
+
/* ---------------------------------------------------------------- the journey's shape */
|
|
802
|
+
|
|
803
|
+
/** The four-mission arc. Mission 1 is the swappable tool that differs per journey. */
|
|
804
|
+
const MISSIONS = [
|
|
805
|
+
{ id: 'foto', label: 'Foto Produk' },
|
|
806
|
+
{ id: 'insight', label: 'Insight' },
|
|
807
|
+
{ id: 'strategi', label: 'Strategi' },
|
|
808
|
+
{ id: 'aksi', label: 'Rencana Aksi' },
|
|
809
|
+
];
|
|
810
|
+
|
|
811
|
+
/* ---------------------------------------------------------------- chrome */
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* The journey chrome: progress rail + quota meters, in one `shrink-0` band under the header.
|
|
815
|
+
*
|
|
816
|
+
* IT SITS OUTSIDE THE SCROLLER ON PURPOSE. The rail says where you are and the meters say what
|
|
817
|
+
* you have left to spend — both are answers you want *while* reading the transcript, so
|
|
818
|
+
* scrolling them away would be wrong. Same reason the composer is pinned.
|
|
819
|
+
*/
|
|
820
|
+
const JourneyChrome = ({ current }) => (
|
|
821
|
+
<div className="shrink-0 border-b border-default-primary py-ds-xs">
|
|
822
|
+
<div className={`${COLUMN} flex flex-col gap-ds-2xs`}>
|
|
823
|
+
<Stepper
|
|
824
|
+
orientation="horizontal"
|
|
825
|
+
steps={MISSIONS.map((mission, index) => ({
|
|
826
|
+
label: mission.label,
|
|
827
|
+
state: index < current ? 'done' : index === current ? 'current' : 'default',
|
|
828
|
+
position: index === 0 ? 'first' : index === MISSIONS.length - 1 ? 'last' : 'middle',
|
|
829
|
+
}))}
|
|
830
|
+
/>
|
|
831
|
+
{/* `flex-1` ON EACH BAR, not just a gap on the row. `ProgressBar` justifies its label and
|
|
832
|
+
its percentage to opposite ends, so a bar sized to its content jams the two together —
|
|
833
|
+
"Kuota AI hari ini60%" — which reads as a rendering bug rather than a meter. */}
|
|
834
|
+
<div className="flex gap-ds-md">
|
|
835
|
+
<ProgressBar className="flex-1" label="Kuota AI hari ini" value={3} max={5} size="small" variant="default" />
|
|
836
|
+
<ProgressBar
|
|
837
|
+
className="flex-1"
|
|
838
|
+
label="Misi selesai"
|
|
839
|
+
value={current}
|
|
840
|
+
max={MISSIONS.length}
|
|
841
|
+
size="small"
|
|
842
|
+
variant="success"
|
|
843
|
+
/>
|
|
844
|
+
</div>
|
|
845
|
+
</div>
|
|
846
|
+
</div>
|
|
847
|
+
);
|
|
848
|
+
|
|
849
|
+
/* ---------------------------------------------------------------- the turn parts */
|
|
850
|
+
|
|
851
|
+
/** The assistant's face. */
|
|
852
|
+
const Face = () => (
|
|
853
|
+
<span
|
|
854
|
+
aria-hidden
|
|
855
|
+
className="flex h-7 w-7 items-center justify-center rounded-full bg-default-secondary text-[10px] text-default-secondary"
|
|
856
|
+
>
|
|
857
|
+
AI
|
|
858
|
+
</span>
|
|
859
|
+
);
|
|
860
|
+
|
|
861
|
+
/* Named buttons — an icon-only control with no accessible name announces nothing at all. */
|
|
862
|
+
const ACTIONS = [
|
|
863
|
+
['thumbs-up', 'Jawaban ini membantu'],
|
|
864
|
+
['thumbs-down', 'Jawaban ini tidak membantu'],
|
|
865
|
+
['rotate-cw', 'Buat ulang jawaban'],
|
|
866
|
+
];
|
|
867
|
+
|
|
868
|
+
const Actions = () => (
|
|
869
|
+
<div className="flex items-center gap-ds-4xs">
|
|
870
|
+
{ACTIONS.map(([icon, label]) => (
|
|
871
|
+
<Button key={icon} type="text" color="light" size="micro" iconOnly aria-label={label}>
|
|
872
|
+
<Icon name={icon} aria-hidden className="h-4 w-4" />
|
|
873
|
+
</Button>
|
|
874
|
+
))}
|
|
875
|
+
</div>
|
|
876
|
+
);
|
|
877
|
+
|
|
878
|
+
/** The follow-up chips offered under a settled reply. */
|
|
879
|
+
const Suggestions = ({ items, onPick }) => (
|
|
880
|
+
<div className="flex flex-wrap gap-ds-4xs">
|
|
881
|
+
{items.map((item) => (
|
|
882
|
+
<Chip key={item} size="small" onClick={() => onPick(item)}>
|
|
883
|
+
{item}
|
|
884
|
+
</Chip>
|
|
885
|
+
))}
|
|
886
|
+
</div>
|
|
887
|
+
);
|
|
888
|
+
|
|
889
|
+
/* ---------------------------------------------------------------- the transcript */
|
|
890
|
+
|
|
891
|
+
/** The opening turns of Mission 2, as a conversation rather than a card list. */
|
|
892
|
+
const OPENING = [
|
|
893
|
+
{ from: 'assistant', text: 'Halo! Saya sudah lihat data toko Anda minggu ini. Siap mulai Misi 2?' },
|
|
894
|
+
{ from: 'user', text: 'Siap. Apa temuan terbesarnya?' },
|
|
895
|
+
{
|
|
896
|
+
from: 'assistant',
|
|
897
|
+
text: 'Empat temuan. Yang paling besar: harga Anda 18% di bawah rata-rata pasar, jadi margin tipis padahal permintaan sedang naik.',
|
|
898
|
+
suggestions: ['Jelaskan temuan lainnya', 'Kenapa itu penting?', 'Lanjut ke refleksi'],
|
|
899
|
+
},
|
|
900
|
+
];
|
|
901
|
+
|
|
902
|
+
/** One reply, stubbed. Wiring a real model is the prototype author's job — see the header. */
|
|
903
|
+
const stubReply = (question) =>
|
|
904
|
+
`(Contoh jawaban) Anda bertanya "${question}". Ganti fungsi stubReply dengan panggilan model Anda.`;
|
|
905
|
+
|
|
906
|
+
/* =====================================================================
|
|
907
|
+
* REPLACE THIS. Everything above is the journey shell; this is your prototype.
|
|
908
|
+
*
|
|
909
|
+
* EVERY ASSISTANT BUBBLE IS A GHOST. The live surface fills and rounds the USER side only, so a
|
|
910
|
+
* reply draws as prose in the message column with no container around it. `ghost` is exactly
|
|
911
|
+
* that: the fill goes, the inline padding stays, so text still lines up with anything beneath it.
|
|
912
|
+
* ===================================================================== */
|
|
913
|
+
function Konten({ messages, onSuggestion }) {
|
|
914
|
+
return (
|
|
915
|
+
<ChatMessageList label="Percakapan Mini Learning Journey" align="bottom" className={COLUMN}>
|
|
916
|
+
{messages.map((message, index) =>
|
|
917
|
+
message.from === 'user' ? (
|
|
918
|
+
<ChatMessage key={index} sender="user">
|
|
919
|
+
<ChatMessageBubble metadata={<ChatMessageMetadata timestamp="09:41" status="read" />}>
|
|
920
|
+
{message.text}
|
|
921
|
+
</ChatMessageBubble>
|
|
922
|
+
</ChatMessage>
|
|
923
|
+
) : (
|
|
924
|
+
<ChatMessage key={index} sender="assistant" avatar={<Face />} name="Teman AI">
|
|
925
|
+
<ChatMessageBubble
|
|
926
|
+
ghost
|
|
927
|
+
metadata={
|
|
928
|
+
<ChatMessageMetadata
|
|
929
|
+
footer={
|
|
930
|
+
<div className="flex flex-col gap-ds-4xs">
|
|
931
|
+
<Actions />
|
|
932
|
+
{message.suggestions ? (
|
|
933
|
+
<Suggestions items={message.suggestions} onPick={onSuggestion} />
|
|
934
|
+
) : null}
|
|
935
|
+
</div>
|
|
936
|
+
}
|
|
937
|
+
/>
|
|
938
|
+
}
|
|
939
|
+
>
|
|
940
|
+
{message.text}
|
|
941
|
+
</ChatMessageBubble>
|
|
942
|
+
</ChatMessage>
|
|
943
|
+
),
|
|
944
|
+
)}
|
|
945
|
+
</ChatMessageList>
|
|
946
|
+
);
|
|
947
|
+
}
|
|
948
|
+
/* ===================== end of the part you replace ==================== */
|
|
949
|
+
|
|
950
|
+
/* ---------------------------------------------------------------- the composer */
|
|
951
|
+
|
|
952
|
+
/* STATEFUL, BECAUSE THE COMPOSER IS CONTROLLED. A fixed value and a no-op handler render an
|
|
953
|
+
editor nobody can type into — a defect this family's own stories shipped once. */
|
|
954
|
+
function Composer({ onSend }) {
|
|
955
|
+
const [value, setValue] = React.useState('');
|
|
956
|
+
const submit = () => {
|
|
957
|
+
const trimmed = value.trim();
|
|
958
|
+
if (!trimmed) return;
|
|
959
|
+
onSend(trimmed);
|
|
960
|
+
setValue('');
|
|
961
|
+
};
|
|
962
|
+
return (
|
|
963
|
+
<ChatComposer
|
|
964
|
+
value={value}
|
|
965
|
+
onValueChange={setValue}
|
|
966
|
+
editorLabel="Tulis pesan"
|
|
967
|
+
placeholder="Tanya apa saja tentang misi ini..."
|
|
968
|
+
toolActions={
|
|
969
|
+
<>
|
|
970
|
+
<ChatToolsButton label="Alat" />
|
|
971
|
+
<ChatAttachmentButton label="Lampirkan foto produk" />
|
|
972
|
+
</>
|
|
973
|
+
}
|
|
974
|
+
sendActions={
|
|
975
|
+
<>
|
|
976
|
+
<ChatVoiceButton label="Input suara" />
|
|
977
|
+
<ChatSendButton label="Kirim pesan" onClick={submit} />
|
|
978
|
+
</>
|
|
979
|
+
}
|
|
980
|
+
/>
|
|
981
|
+
);
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
/* ---------------------------------------------------------------- the reflection overlay */
|
|
985
|
+
|
|
986
|
+
/* One open question and one feasibility choice, after each mission. A `Dialog` rather than a
|
|
987
|
+
route, because the journey is one continuous surface. */
|
|
988
|
+
function Reflection({ open, onOpenChange, mission }) {
|
|
989
|
+
const [answer, setAnswer] = React.useState('');
|
|
990
|
+
const [feasible, setFeasible] = React.useState('bisa');
|
|
991
|
+
return (
|
|
992
|
+
<Dialog
|
|
993
|
+
open={open}
|
|
994
|
+
onOpenChange={onOpenChange}
|
|
995
|
+
showCloseButton
|
|
996
|
+
title={`Refleksi — ${mission.label}`}
|
|
997
|
+
/* `description` is VISUALLY HIDDEN — it wires `aria-describedby`, it does not draw
|
|
998
|
+
subtext. Passing prose here and expecting to see it is how this dialog first shipped
|
|
999
|
+
with an invisible subtitle. Visible subtext is a `Text` in the body, below. */
|
|
1000
|
+
description="Dua pertanyaan singkat sebelum melanjutkan ke misi berikutnya."
|
|
1001
|
+
>
|
|
1002
|
+
{/* THE PANEL PADS ITS TITLE AND NOT ITS CHILDREN. `Dialog` gives the heading `px-6 pt-4`
|
|
1003
|
+
and renders the body with none, so the caller owns it — without this the field, the
|
|
1004
|
+
cards and the button all run edge to edge against the panel border. */}
|
|
1005
|
+
<div className="flex flex-col gap-ds-md px-6 pb-6 pt-ds-sm">
|
|
1006
|
+
<Text variant="bodySm" color="secondary">
|
|
1007
|
+
Dua pertanyaan singkat sebelum lanjut.
|
|
1008
|
+
</Text>
|
|
1009
|
+
<TextField
|
|
1010
|
+
label="Apa satu hal yang akan Anda ubah minggu ini?"
|
|
1011
|
+
value={answer}
|
|
1012
|
+
onChange={(event) => setAnswer(event.target.value)}
|
|
1013
|
+
placeholder="Tulis jawaban Anda..."
|
|
1014
|
+
/>
|
|
1015
|
+
<div className="flex flex-col gap-ds-2xs">
|
|
1016
|
+
<Text variant="bodySm" weight="semibold">
|
|
1017
|
+
Seberapa mungkin Anda melakukannya?
|
|
1018
|
+
</Text>
|
|
1019
|
+
{/* ONE GROUP, NOT ONE CARD PER OPTION. `SelectionCard` takes the whole `options` array
|
|
1020
|
+
and owns selection itself; a per-option `selected` prop does not exist, and an
|
|
1021
|
+
invented prop renders with no styling and no error. */}
|
|
1022
|
+
<SelectionCard
|
|
1023
|
+
options={[
|
|
1024
|
+
{ value: 'bisa', label: 'Bisa saya lakukan minggu ini' },
|
|
1025
|
+
{ value: 'usaha', label: 'Perlu usaha ekstra' },
|
|
1026
|
+
{ value: 'belum', label: 'Belum bisa sekarang' },
|
|
1027
|
+
]}
|
|
1028
|
+
value={[feasible]}
|
|
1029
|
+
onValueChange={(picked) => setFeasible(picked[0] ?? 'bisa')}
|
|
1030
|
+
/>
|
|
1031
|
+
</div>
|
|
1032
|
+
<div className="flex">
|
|
1033
|
+
<Button type="primary" color="primary" size="medium" width="auto" onClick={() => onOpenChange(false)}>
|
|
1034
|
+
Simpan & lanjut
|
|
1035
|
+
</Button>
|
|
1036
|
+
</div>
|
|
1037
|
+
</div>
|
|
1038
|
+
</Dialog>
|
|
1039
|
+
);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
/* ---------------------------------------------------------------- the room */
|
|
1043
|
+
|
|
1044
|
+
function App() {
|
|
1045
|
+
const [reflecting, setReflecting] = React.useState(false);
|
|
1046
|
+
const [current, setCurrent] = React.useState(1);
|
|
1047
|
+
const [messages, setMessages] = React.useState(OPENING);
|
|
1048
|
+
|
|
1049
|
+
/* One turn: the user's message, then a stubbed reply. Replace `stubReply` with your model. */
|
|
1050
|
+
const send = (text) => {
|
|
1051
|
+
if (text === 'Lanjut ke refleksi') {
|
|
1052
|
+
setMessages((previous) => [...previous, { from: 'user', text }]);
|
|
1053
|
+
setReflecting(true);
|
|
1054
|
+
setCurrent((step) => Math.min(step + 1, MISSIONS.length));
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
setMessages((previous) => [
|
|
1058
|
+
...previous,
|
|
1059
|
+
{ from: 'user', text },
|
|
1060
|
+
{ from: 'assistant', text: stubReply(text) },
|
|
1061
|
+
]);
|
|
1062
|
+
};
|
|
1063
|
+
|
|
1064
|
+
return (
|
|
1065
|
+
<React.Fragment>
|
|
1066
|
+
<AppShell
|
|
1067
|
+
header={({ scrolled }) => (
|
|
1068
|
+
<Header
|
|
1069
|
+
type="temanAiChat"
|
|
1070
|
+
title="Mini Learning Journey"
|
|
1071
|
+
showTextLeft
|
|
1072
|
+
scrolled={scrolled}
|
|
1073
|
+
/>
|
|
1074
|
+
)}
|
|
1075
|
+
/* NO SIDEBAR. A prototype is one journey on one screen — an app-navigation rail is
|
|
1076
|
+
chrome it does not have, and a drawer needs a trigger and state before it can open. */
|
|
1077
|
+
>
|
|
1078
|
+
<JourneyChrome current={current} />
|
|
1079
|
+
<ChatThread scrollToBottomLabel="Ke pesan terbaru">
|
|
1080
|
+
<Konten messages={messages} onSuggestion={send} />
|
|
1081
|
+
</ChatThread>
|
|
1082
|
+
<div className="shrink-0 py-ds-md">
|
|
1083
|
+
<div className={COLUMN}>
|
|
1084
|
+
<Composer onSend={send} />
|
|
1085
|
+
</div>
|
|
1086
|
+
</div>
|
|
1087
|
+
</AppShell>
|
|
1088
|
+
<Reflection
|
|
1089
|
+
open={reflecting}
|
|
1090
|
+
onOpenChange={setReflecting}
|
|
1091
|
+
mission={MISSIONS[Math.min(current, MISSIONS.length - 1)]}
|
|
1092
|
+
/>
|
|
1093
|
+
</React.Fragment>
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
ReactDOMClient.createRoot(document.getElementById('root')).render(<App />);
|
|
1098
|
+
|
|
1099
|
+
</script>
|
|
1100
|
+
</body>
|
|
1101
|
+
</html>
|