astryx-dracula 0.2.1 → 0.3.0

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 (83) hide show
  1. package/BRAND.md +30 -18
  2. package/README.md +17 -12
  3. package/USAGE.md +29 -10
  4. package/astryx-dracula.js +19 -13
  5. package/astryx-dracula.variants.d.ts +15 -0
  6. package/package.json +59 -17
  7. package/shared/auth-chrome-config.ts +18 -0
  8. package/shared/auth-chrome.tsx +25 -0
  9. package/shared/auth-copy.ts +22 -0
  10. package/shared/chaptered-doc-config.ts +92 -0
  11. package/shared/chaptered-doc.tsx +332 -0
  12. package/shared/chart-hues.ts +27 -0
  13. package/shared/chart-labels.tsx +44 -0
  14. package/shared/chart-legend.tsx +100 -0
  15. package/shared/chart-panel-style.ts +13 -0
  16. package/shared/data-bar.tsx +160 -0
  17. package/shared/gallery-image.ts +20 -0
  18. package/shared/login-demo.ts +7 -0
  19. package/shared/metric-delta.tsx +36 -0
  20. package/shared/order-desk.tsx +273 -0
  21. package/shared/revenue-chart.tsx +174 -0
  22. package/shared/scene-castle.tsx +322 -0
  23. package/shared/scene-frame.tsx +88 -0
  24. package/shared/scene-hues.ts +49 -0
  25. package/shared/scene-tile.tsx +128 -0
  26. package/shared/settings-data.ts +125 -0
  27. package/shared/settings-rows.tsx +179 -0
  28. package/shared/sparkline.tsx +94 -0
  29. package/shared/sso-icons.tsx +30 -0
  30. package/templates/ai-chat-landing.tsx +66 -33
  31. package/templates/ai-chat.tsx +36 -19
  32. package/templates/blank.tsx +4 -3
  33. package/templates/centered-hero.tsx +14 -125
  34. package/templates/classic-gallery.tsx +20 -66
  35. package/templates/contact-form.tsx +59 -39
  36. package/templates/dashboard-portfolio.tsx +143 -226
  37. package/templates/dashboard.tsx +122 -122
  38. package/templates/detail-page.tsx +95 -91
  39. package/templates/documentation-design.template.mjs +1 -1
  40. package/templates/documentation-design.tsx +83 -96
  41. package/templates/documentation-technical.template.mjs +1 -1
  42. package/templates/documentation-technical.tsx +29 -19
  43. package/templates/documentation.template.mjs +1 -1
  44. package/templates/documentation.tsx +52 -74
  45. package/templates/editor.tsx +112 -84
  46. package/templates/file-explorer.tsx +131 -84
  47. package/templates/form-two-column.tsx +61 -125
  48. package/templates/gallery-hero.tsx +15 -171
  49. package/templates/ide.tsx +22 -20
  50. package/templates/incident-console.tsx +104 -70
  51. package/templates/kanban-board.tsx +99 -42
  52. package/templates/library.template.mjs +2 -2
  53. package/templates/library.tsx +37 -42
  54. package/templates/login-card.tsx +58 -87
  55. package/templates/login-split.tsx +88 -126
  56. package/templates/login-sso.tsx +105 -64
  57. package/templates/login.tsx +107 -53
  58. package/templates/messaging-shell.tsx +171 -144
  59. package/templates/mixed-gallery.tsx +29 -130
  60. package/templates/payment-form.tsx +368 -290
  61. package/templates/product-detail.tsx +39 -58
  62. package/templates/product-gallery.tsx +30 -41
  63. package/templates/product-tour.template.mjs +6 -0
  64. package/templates/product-tour.tsx +916 -0
  65. package/templates/settings-dialog.tsx +135 -282
  66. package/templates/settings-sidebar.tsx +178 -268
  67. package/templates/settings.tsx +41 -14
  68. package/templates/shell-nav.tsx +31 -24
  69. package/templates/shell-side-nav.tsx +29 -20
  70. package/templates/shell-top-nav.tsx +80 -44
  71. package/templates/side-gallery.tsx +38 -94
  72. package/templates/table-grouped.tsx +62 -34
  73. package/templates/table-page-chart.tsx +23 -302
  74. package/templates/table-page-heatmap-status.tsx +156 -92
  75. package/templates/table-page-shoe-store-heatmap.tsx +18 -293
  76. package/templates/table-page.tsx +23 -7
  77. package/templates/table.tsx +23 -8
  78. package/templates/tech-report.template.mjs +6 -0
  79. package/templates/tech-report.tsx +1486 -0
  80. package/templates/theme-showcase.template.mjs +1 -1
  81. package/templates/theme-showcase.tsx +299 -237
  82. package/theme.css +76 -14
  83. package/tokens.css +425 -30
@@ -1,8 +1,24 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
  // XLE (canonical structure, validated with `bunx astryx layout check`):
3
- // Ctr > C[p8 mw400] > V[g4] > (V[g1 a=center] > Hd"Welcome back to the night"[level=2] + Tx"Whisper your details"[t=body]) + (V[g2] > TI"Work email"[t=email] + TI"Password"[t=password]) + Lk"Having trouble signing in?" + B.primary"Enter the night" + D"Or sign in with" + B.secondary"Continue with SSO" + (V[a=center] > Tx"New to the castle?"[t=supporting])
3
+ // Ctr > C[p=4 mw=400] > V[g=4] > ((V[g=1 a=center] > Hd"Welcome back to the night"[level=1] + Tx"Whisper your details to enter the night"[t=body]) + TI"Work email"[t=email] + Lk"Having trouble signing in?" + B.primary"Continue" + D"Or continue with" + B.secondary"Continue with SSO" + (V[a=center] > Tx"New to the castle?"[t=supporting] + Lk"Request access") + (V[g=2 a=center] > Av + Hd"Sign in with Google Workspace"[level=1] + Tx"You will be redirected back after signing in."[t=body]) + (C[p=0] > S[p=4 muted] > (H[g=2 a=center] > Ic + (V[g=0] > Tx"Google Workspace"[t=label] + Tx"vlad@castle.ro"[t=supporting]))) + (V[g=3] > B.primary"Continue with Google Workspace" + B.ghost"Use a different email") + (V[g=1 a=center] > Hd"Welcome back to the night"[level=1] + Tx"vlad@castle.ro"[t=body]) + (V[g=4] > (V[g=1] > TI"Password"[t=password] + Lk"Forgot password?") + B.primary"Enter the night" + B.ghost"Use a different email") + (V[a=center] > Tx"By clicking continue, you agree to our Terms of service and Privacy policy"[t=supporting]))
4
4
 
5
- import {useState, type CSSProperties} from 'react';
5
+ // Step-conditional h1: the email and SSO-confirm steps each render their own
6
+ // h1 (email entry vs provider confirm); the password fallback reuses the
7
+ // family heading, so exactly one h1 shows per step.
8
+ import {useState, useTransition, type CSSProperties} from 'react';
9
+ import {demoLogin} from 'astryx-dracula/shared/login-demo';
10
+ import {
11
+ AUTH_HEADING,
12
+ AUTH_PRIMARY_CTA,
13
+ AUTH_SIGNUP_PROMPT,
14
+ AUTH_SSO_DIVIDER,
15
+ AUTH_FORGOT_PASSWORD,
16
+ AUTH_ERROR_MESSAGE,
17
+ AUTH_EMAIL_PLACEHOLDER,
18
+ AUTH_TERMS_PREFIX,
19
+ AUTH_TERMS_SERVICE,
20
+ AUTH_TERMS_PRIVACY,
21
+ } from 'astryx-dracula/shared/auth-copy';
6
22
  import {VStack, HStack} from '@astryxdesign/core/Layout';
7
23
  import {Center} from '@astryxdesign/core/Center';
8
24
  import {Text, Heading} from '@astryxdesign/core/Text';
@@ -18,6 +34,7 @@ import {Avatar} from '@astryxdesign/core/Avatar';
18
34
  // ============= ICONS (verified lucide-react exports) =============
19
35
  // ShieldCheck ← ShieldCheckIcon.
20
36
  import {ShieldCheck} from 'lucide-react';
37
+ import {inputAutoComplete} from 'astryx-dracula/shared/auth-chrome-config';
21
38
 
22
39
  // ---------------------------------------------------------------------------
23
40
  // Styles
@@ -61,16 +78,21 @@ export default function LoginSso() {
61
78
  const [step, setStep] = useState<Step>('email');
62
79
  const [email, setEmail] = useState('');
63
80
  const [password, setPassword] = useState('');
64
- const [loginFailed, setLoginFailed] = useState(false);
65
- const [isLoading, setIsLoading] = useState(false);
81
+ const [error, setError] = useState<string | null>(null);
82
+ const [isLoading, startTransition] = useTransition();
66
83
 
67
84
  const provider = getProvider(email);
68
85
  const emailValid = isValidEmail(email);
69
86
 
70
87
  const handleContinue = () => {
71
88
  if (!emailValid) {
89
+ // The Continue buttons stay enabled and explain themselves. Disabling
90
+ // them greyed the buttons out with no stated reason, so a malformed
91
+ // address produced silence rather than guidance.
92
+ setError('Enter a valid email address to continue.');
72
93
  return;
73
94
  }
95
+ setError(null);
74
96
  if (provider) {
75
97
  setStep('sso-confirm');
76
98
  } else {
@@ -80,89 +102,83 @@ export default function LoginSso() {
80
102
 
81
103
  const handleBack = () => {
82
104
  setStep('email');
83
- setLoginFailed(false);
84
- setIsLoading(false);
105
+ setError(null);
85
106
  };
86
107
 
87
108
  const handleSignIn = () => {
88
109
  if (!password) {
89
- setLoginFailed(true);
110
+ setError(AUTH_ERROR_MESSAGE);
90
111
  return;
91
112
  }
92
- setIsLoading(true);
93
- setLoginFailed(false);
94
- setTimeout(() => {
95
- setIsLoading(false);
96
- setLoginFailed(true);
97
- }, 2000);
113
+ setError(null);
114
+ startTransition(async () => {
115
+ await demoLogin();
116
+ setError(AUTH_ERROR_MESSAGE);
117
+ });
98
118
  };
99
119
 
100
120
  return (
101
121
  <Center axis="both" style={pageStyle}>
102
- <Card padding={8} width="100%" maxWidth={400}>
122
+ <Card padding={4} width="100%" maxWidth={400}>
103
123
  <VStack gap={4} hAlign="stretch">
104
- {/* ── Step 1: Email entry ── */}
124
+ {/* ── Step 1: Email entry, route by domain ──
125
+ The password is collected on the fallback step below, never
126
+ here: a recognised domain goes straight to SSO. */}
105
127
  {step === 'email' && (
106
128
  <>
107
129
  <VStack gap={1} hAlign="center">
108
- <Heading level={2}>Welcome back to the night</Heading>
130
+ <Heading level={1} type="display-2">
131
+ {AUTH_HEADING}
132
+ </Heading>
109
133
  <Text type="body" color="secondary">
110
134
  Whisper your details to enter the night
111
135
  </Text>
112
136
  </VStack>
113
137
 
114
- <VStack gap={2}>
115
- <TextInput
116
- label="Work email"
117
- isLabelHidden
118
- type="email"
119
- placeholder="you@castle.dracula"
120
- value={email}
121
- onChange={setEmail}
122
- size="lg"
123
- onKeyDown={(e: React.KeyboardEvent) => {
124
- if (e.key === 'Enter') {
125
- handleContinue();
126
- }
127
- }}
128
- />
129
- <TextInput
130
- label="Password"
131
- isLabelHidden
132
- type="password"
133
- placeholder="Whisper your password"
134
- value={password}
135
- onChange={setPassword}
136
- size="lg"
137
- />
138
- </VStack>
138
+ <TextInput
139
+ label="Work email"
140
+ isLabelHidden
141
+ type="email"
142
+ {...inputAutoComplete('email')}
143
+ placeholder={AUTH_EMAIL_PLACEHOLDER}
144
+ value={email}
145
+ onChange={(v: string) => {
146
+ setEmail(v);
147
+ setError(null);
148
+ }}
149
+ size="lg"
150
+ onKeyDown={(e: React.KeyboardEvent) => {
151
+ if (e.key === 'Enter') {
152
+ handleContinue();
153
+ }
154
+ }}
155
+ status={error ? {type: 'error', message: error} : undefined}
156
+ />
139
157
 
140
158
  <Link href="#/templates/login-sso">
141
159
  Having trouble signing in?
142
160
  </Link>
143
161
 
144
162
  <Button
145
- label="Enter the night"
163
+ label="Continue"
146
164
  variant="primary"
147
165
  size="lg"
148
166
  onClick={handleContinue}
149
- isDisabled={!emailValid}
150
167
  />
151
168
 
152
- <Divider label="Or sign in with" />
169
+ <Divider label={AUTH_SSO_DIVIDER} />
153
170
 
154
171
  <Button
155
172
  label="Continue with SSO"
156
173
  variant="secondary"
157
174
  size="lg"
158
175
  onClick={handleContinue}
159
- isDisabled={!emailValid}
160
176
  />
161
177
 
162
178
  <VStack hAlign="center">
163
179
  <Text type="supporting" color="secondary">
164
- New to the castle?{' '}
165
- <Link href="#/templates/login-sso">
180
+ {AUTH_SIGNUP_PROMPT}{' '}
181
+ <Link href="#/templates/login-sso" type="supporting">
166
182
  Request access
167
183
  </Link>
168
184
  </Text>
@@ -175,14 +191,23 @@ export default function LoginSso() {
175
191
  <>
176
192
  <VStack gap={2} hAlign="center">
177
193
  <Avatar name={provider.name} size={48} />
178
- <Heading level={2}>Sign in with {provider.name}</Heading>
194
+ <Heading level={1} type="display-2">
195
+ Sign in with {provider.name}
196
+ </Heading>
179
197
  <Text type="body" color="secondary">
180
198
  You will be redirected back after signing in.
181
199
  </Text>
182
200
  </VStack>
183
201
 
184
- <Card padding={0}>
185
- <Section variant="muted" padding={4}>
202
+ {/* The gutter lives on the Card, not the Section. A Section
203
+ escapes its container's --container-padding-* (Section.tsx:
204
+ 56-71), but with Card padding={0} that published var is
205
+ --spacing-0, so the escape cancelled nothing and the muted
206
+ fill painted flush to the card border. 3 + 1 keeps the text
207
+ inset at the 16px it already had while giving the fill a
208
+ real one. */}
209
+ <Card padding={3}>
210
+ <Section variant="muted" padding={1}>
186
211
  <HStack gap={2} vAlign="center">
187
212
  <Icon icon={ShieldCheck} color="secondary" />
188
213
  <VStack gap={0}>
@@ -201,7 +226,11 @@ export default function LoginSso() {
201
226
  variant="primary"
202
227
  size="lg"
203
228
  isLoading={isLoading}
204
- onClick={() => setIsLoading(true)}
229
+ onClick={() => {
230
+ startTransition(async () => {
231
+ await demoLogin();
232
+ });
233
+ }}
205
234
  />
206
235
  <Button
207
236
  label="Use a different email"
@@ -217,7 +246,9 @@ export default function LoginSso() {
217
246
  {step === 'password-fallback' && (
218
247
  <>
219
248
  <VStack gap={1} hAlign="center">
220
- <Heading level={2}>Welcome back to the night</Heading>
249
+ <Heading level={1} type="display-2">
250
+ {AUTH_HEADING}
251
+ </Heading>
221
252
  <Text type="body" color="secondary">
222
253
  {email}
223
254
  </Text>
@@ -228,32 +259,27 @@ export default function LoginSso() {
228
259
  <TextInput
229
260
  label="Password"
230
261
  type="password"
262
+ {...inputAutoComplete('current-password')}
231
263
  value={password}
232
264
  size="lg"
233
265
  onChange={(v: string) => {
234
266
  setPassword(v);
235
- setLoginFailed(false);
267
+ setError(null);
236
268
  }}
237
- status={
238
- loginFailed
239
- ? {
240
- type: 'error',
241
- message: 'Wrong incantation. Try again.',
242
- }
243
- : undefined
244
- }
269
+ onEnter={handleSignIn}
270
+ status={error ? {type: 'error', message: error} : undefined}
245
271
  />
246
- {loginFailed && (
272
+ {error && (
247
273
  <VStack hAlign="end">
248
274
  <Link href="#/templates/login-sso">
249
- Forgot password?
275
+ {AUTH_FORGOT_PASSWORD}
250
276
  </Link>
251
277
  </VStack>
252
278
  )}
253
279
  </VStack>
254
280
 
255
281
  <Button
256
- label="Enter the night"
282
+ label={AUTH_PRIMARY_CTA}
257
283
  variant="primary"
258
284
  size="lg"
259
285
  isLoading={isLoading}
@@ -268,6 +294,21 @@ export default function LoginSso() {
268
294
  </VStack>
269
295
  </>
270
296
  )}
297
+
298
+ {/* Terms — family line, shown on every step */}
299
+ <VStack hAlign="center" width="100%">
300
+ <Text type="supporting" color="secondary" justify="center">
301
+ {AUTH_TERMS_PREFIX}{' '}
302
+ <Link href="#/templates/login-sso" type="supporting">
303
+ {AUTH_TERMS_SERVICE}
304
+ </Link>{' '}
305
+ and{' '}
306
+ <Link href="#/templates/login-sso" type="supporting">
307
+ {AUTH_TERMS_PRIVACY}
308
+ </Link>
309
+ .
310
+ </Text>
311
+ </VStack>
271
312
  </VStack>
272
313
  </Card>
273
314
  </Center>
@@ -1,99 +1,153 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
  // XLE (canonical structure, validated with `bunx astryx layout check`):
3
- // Ctr > V[g=4 a=center] > (V[g=2 a=center] > Ic + Tx"Castle Dracula"[t=body]) + (C[p=8] > V[g=4] > (V[g=1 a=center] > Hd"Welcome back to the night"[level=2] + Tx"Sign in to your crypt"[t=body]) + TI"Email"[t=email] + TI"Password"[t=password] + B.primary"Enter the night")
3
+ // Ctr > V[g=4 a=center] > (V[g=2 a=center] > Ic + Tx"Castle Dracula"[t=body]) + (C[p=4] > V[g=4] > (V[g=1 a=center] > Hd"Welcome back to the night"[level=1] + Tx"Sign in to your crypt"[t=body]) + TI"Email"[t=email] + (V[g=1] > TI"Password"[t=password] + Lk"Forgot password?") + B.primary"Enter the night" + (V[a=center] > Tx"New to the castle?"[t=supporting] + Lk"Sign up")) + (V[a=center] > Tx"By clicking continue, you agree to our Terms of service and Privacy policy"[t=supporting])
4
4
 
5
- import {useState, type CSSProperties} from 'react';
6
- import {Moon} from 'lucide-react';
5
+ import {useState, useTransition} from 'react';
6
+ import {demoLogin} from 'astryx-dracula/shared/login-demo';
7
+ import {
8
+ AUTH_HEADING,
9
+ AUTH_SUBTITLE,
10
+ AUTH_PRIMARY_CTA,
11
+ AUTH_SIGNUP_PROMPT,
12
+ AUTH_SIGNUP_LINK,
13
+ AUTH_FORGOT_PASSWORD,
14
+ AUTH_ERROR_MESSAGE,
15
+ AUTH_EMAIL_PLACEHOLDER,
16
+ AUTH_PASSWORD_PLACEHOLDER,
17
+ AUTH_TERMS_PREFIX,
18
+ AUTH_TERMS_SERVICE,
19
+ AUTH_TERMS_PRIVACY,
20
+ } from 'astryx-dracula/shared/auth-copy';
7
21
  import {VStack} from '@astryxdesign/core/Layout';
8
22
  import {Center} from '@astryxdesign/core/Center';
9
23
  import {Text, Heading} from '@astryxdesign/core/Text';
10
24
  import {TextInput} from '@astryxdesign/core/TextInput';
11
25
  import {Button} from '@astryxdesign/core/Button';
12
26
  import {Card} from '@astryxdesign/core/Card';
13
- import {Icon} from '@astryxdesign/core/Icon';
14
- import {Banner} from '@astryxdesign/core/Banner';
27
+ import {Link} from '@astryxdesign/core/Link';
15
28
 
16
- // Standalone auth page paints its own body background (no host shell).
17
- const pageStyle: CSSProperties = {
18
- minHeight: '100%',
19
- backgroundColor: 'var(--color-background-body)',
20
- padding: 'var(--spacing-6)',
21
- };
22
- // Cap the column at 400px but let it shrink to fit narrow screens (Stack
23
- // has no maxWidth prop, so it's set here).
24
- const contentStyle: CSSProperties = {
25
- width: '100%',
26
- maxWidth: 400,
27
- };
29
+ import {authPageStyle as pageStyle, authContentStyle as contentStyle, inputAutoComplete} from 'astryx-dracula/shared/auth-chrome-config';
30
+ import {LoginBrand} from 'astryx-dracula/shared/auth-chrome';
28
31
 
29
32
  export default function LoginPage() {
30
33
  const [email, setEmail] = useState('');
31
34
  const [password, setPassword] = useState('');
32
- const [isLoading, setIsLoading] = useState(false);
33
- const [error, setError] = useState('');
35
+ const [isLoading, startTransition] = useTransition();
36
+ const [error, setError] = useState<string | null>(null);
34
37
 
35
38
  const handleSignIn = () => {
36
- setError('');
39
+ setError(null);
37
40
  if (!email || !password) {
38
- setError('Whisper both your email and password to enter the night.');
41
+ setError(AUTH_ERROR_MESSAGE);
39
42
  return;
40
43
  }
41
- setIsLoading(true);
42
- setTimeout(() => setIsLoading(false), 2000);
44
+ startTransition(async () => {
45
+ await demoLogin();
46
+ });
43
47
  };
44
48
 
45
49
  return (
46
50
  <Center axis="both" style={pageStyle}>
47
51
  <VStack gap={4} hAlign="center" style={contentStyle}>
48
- {/* Logo */}
49
- <VStack gap={2} hAlign="center">
50
- <Icon icon={Moon} size="lg" color="accent" />
51
- <Text type="body" weight="bold" size="lg">
52
- Castle Dracula
53
- </Text>
54
- </VStack>
52
+ <LoginBrand />
55
53
 
56
54
  {/* Card */}
57
- <Card padding={8} width="100%">
55
+ <Card padding={4} width="100%">
58
56
  <VStack gap={4} hAlign="stretch">
59
57
  <VStack gap={1} hAlign="center">
60
- <Heading level={2} justify="center">
61
- Welcome back to the night
58
+ <Heading level={1} type="display-2" justify="center">
59
+ {AUTH_HEADING}
62
60
  </Heading>
63
61
  <Text type="body" color="secondary">
64
- Sign in to your crypt
62
+ {AUTH_SUBTITLE}
65
63
  </Text>
66
64
  </VStack>
67
65
 
68
- {error && <Banner status="error" title={error} container="card" />}
66
+ {/* The field group, as its three siblings carry it
67
+ (login-card.tsx:76, login-split.tsx:160, login-sso.tsx:192).
68
+ Without it these two inputs sat 16px apart -- the outer
69
+ VStack's gap={4} -- where all three twins render 8px, so the
70
+ gap between the two fields was twice the gap between the
71
+ fields and the sign-up link below. */}
72
+ <VStack gap={2}>
73
+ <TextInput
74
+ label="Email"
75
+ isLabelHidden
76
+ value={email}
77
+ onChange={v => {
78
+ setEmail(v);
79
+ setError(null);
80
+ }}
81
+ placeholder={AUTH_EMAIL_PLACEHOLDER}
82
+ type="email"
83
+ {...inputAutoComplete('email')}
84
+ size="lg"
85
+ onEnter={handleSignIn}
86
+ status={error ? {type: 'error', message: error} : undefined}
87
+ />
69
88
 
70
- <TextInput
71
- label="Email"
72
- value={email}
73
- onChange={setEmail}
74
- placeholder="you@castle.dracula"
75
- type="email"
76
- size="lg"
77
- />
78
-
79
- <TextInput
80
- label="Password"
81
- value={password}
82
- onChange={setPassword}
83
- placeholder="Whisper your password"
84
- type="password"
85
- size="lg"
86
- />
89
+ <VStack gap={1}>
90
+ <TextInput
91
+ label="Password"
92
+ isLabelHidden
93
+ value={password}
94
+ onChange={v => {
95
+ setPassword(v);
96
+ setError(null);
97
+ }}
98
+ placeholder={AUTH_PASSWORD_PLACEHOLDER}
99
+ type="password"
100
+ {...inputAutoComplete('current-password')}
101
+ size="lg"
102
+ onEnter={handleSignIn}
103
+ status={error ? {type: 'error', message: error} : undefined}
104
+ />
105
+ {error && (
106
+ <VStack hAlign="end">
107
+ <Link
108
+ href="#/templates/login"
109
+ color="secondary">
110
+ {AUTH_FORGOT_PASSWORD}
111
+ </Link>
112
+ </VStack>
113
+ )}
114
+ </VStack>
115
+ </VStack>
87
116
 
88
117
  <Button
89
- label="Enter the night"
118
+ label={AUTH_PRIMARY_CTA}
90
119
  variant="primary"
91
120
  size="lg"
92
121
  isLoading={isLoading}
93
122
  onClick={handleSignIn}
94
123
  />
124
+
125
+ {/* Sign up link */}
126
+ <VStack hAlign="center">
127
+ <Text type="supporting" color="secondary">
128
+ {AUTH_SIGNUP_PROMPT}{' '}
129
+ <Link href="#/templates/login" type="supporting">
130
+ {AUTH_SIGNUP_LINK}
131
+ </Link>
132
+ </Text>
133
+ </VStack>
95
134
  </VStack>
96
135
  </Card>
136
+
137
+ {/* Terms */}
138
+ <VStack hAlign="center" width="100%">
139
+ <Text type="supporting" color="secondary" justify="center">
140
+ {AUTH_TERMS_PREFIX}{' '}
141
+ <Link href="#/templates/login" type="supporting">
142
+ {AUTH_TERMS_SERVICE}
143
+ </Link>{' '}
144
+ and{' '}
145
+ <Link href="#/templates/login" type="supporting">
146
+ {AUTH_TERMS_PRIVACY}
147
+ </Link>
148
+ .
149
+ </Text>
150
+ </VStack>
97
151
  </VStack>
98
152
  </Center>
99
153
  );