keystone-design-bootstrap 1.0.73 → 1.0.75
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 +160 -1
- package/dist/contexts/index.js +3 -1
- package/dist/contexts/index.js.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/themes/index.d.ts +1 -0
- package/dist/themes/index.js +3 -1
- package/dist/themes/index.js.map +1 -1
- package/package.json +1 -1
- package/src/design_system/portal/LoginForm.tsx +39 -22
- package/src/next/layouts/root-layout.tsx +16 -12
- package/src/themes/index.ts +1 -0
package/dist/themes/index.d.ts
CHANGED
package/dist/themes/index.js
CHANGED
|
@@ -6,8 +6,10 @@ var THEME_CONFIG = {
|
|
|
6
6
|
// Aman Hotels variant files (hero-home.aman.tsx)
|
|
7
7
|
barelux: ".barelux",
|
|
8
8
|
// Bare Lux Studio variant files (hero-home.barelux.tsx)
|
|
9
|
-
balance: ".balance"
|
|
9
|
+
balance: ".balance",
|
|
10
10
|
// Balance Aesthetics variant files (hero-home.balance.tsx)
|
|
11
|
+
custom: ""
|
|
12
|
+
// Fully custom sites — no design-system CSS loaded, all styling built in the site itself
|
|
11
13
|
};
|
|
12
14
|
function getAvailableThemes() {
|
|
13
15
|
return Object.keys(THEME_CONFIG);
|
package/dist/themes/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/themes/index.ts"],"sourcesContent":["/**\n * Theme Configuration\n * Single source of truth for all themes\n */\n\nexport const THEME_CONFIG = {\n classic: '', // Base files with no suffix (hero-home.tsx)\n aman: '.aman', // Aman Hotels variant files (hero-home.aman.tsx)\n barelux: '.barelux', // Bare Lux Studio variant files (hero-home.barelux.tsx)\n balance: '.balance', // Balance Aesthetics variant files (hero-home.balance.tsx)\n} as const;\n\nexport type Theme = keyof typeof THEME_CONFIG;\n\nexport function getAvailableThemes(): Theme[] {\n return Object.keys(THEME_CONFIG) as Theme[];\n}\n\nexport function getThemeSuffix(theme: Theme): string {\n return THEME_CONFIG[theme] || '';\n}\n\nexport function isValidTheme(theme: string): theme is Theme {\n return theme in THEME_CONFIG;\n}\n"],"mappings":";AAKO,IAAM,eAAe;AAAA,EAC1B,SAAS;AAAA;AAAA,EACT,MAAM;AAAA;AAAA,EACN,SAAS;AAAA;AAAA,EACT,SAAS;AAAA;
|
|
1
|
+
{"version":3,"sources":["../../src/themes/index.ts"],"sourcesContent":["/**\n * Theme Configuration\n * Single source of truth for all themes\n */\n\nexport const THEME_CONFIG = {\n classic: '', // Base files with no suffix (hero-home.tsx)\n aman: '.aman', // Aman Hotels variant files (hero-home.aman.tsx)\n barelux: '.barelux', // Bare Lux Studio variant files (hero-home.barelux.tsx)\n balance: '.balance', // Balance Aesthetics variant files (hero-home.balance.tsx)\n custom: '', // Fully custom sites — no design-system CSS loaded, all styling built in the site itself\n} as const;\n\nexport type Theme = keyof typeof THEME_CONFIG;\n\nexport function getAvailableThemes(): Theme[] {\n return Object.keys(THEME_CONFIG) as Theme[];\n}\n\nexport function getThemeSuffix(theme: Theme): string {\n return THEME_CONFIG[theme] || '';\n}\n\nexport function isValidTheme(theme: string): theme is Theme {\n return theme in THEME_CONFIG;\n}\n"],"mappings":";AAKO,IAAM,eAAe;AAAA,EAC1B,SAAS;AAAA;AAAA,EACT,MAAM;AAAA;AAAA,EACN,SAAS;AAAA;AAAA,EACT,SAAS;AAAA;AAAA,EACT,QAAQ;AAAA;AACV;AAIO,SAAS,qBAA8B;AAC5C,SAAO,OAAO,KAAK,YAAY;AACjC;AAEO,SAAS,eAAe,OAAsB;AACnD,SAAO,aAAa,KAAK,KAAK;AAChC;AAEO,SAAS,aAAa,OAA+B;AAC1D,SAAO,SAAS;AAClB;","names":[]}
|
package/package.json
CHANGED
|
@@ -44,6 +44,13 @@ export function LoginForm({ onSuccess, onClose }: LoginFormProps) {
|
|
|
44
44
|
const [error, setError] = useState<string | null>(null);
|
|
45
45
|
const [loading, setLoading] = useState(false);
|
|
46
46
|
|
|
47
|
+
// Additional contact method collected on the new-user step.
|
|
48
|
+
// Kept separate so typing here doesn't affect the identifier-step conditions.
|
|
49
|
+
const [additionalPhone, setAdditionalPhone] = useState('');
|
|
50
|
+
const [additionalEmail, setAdditionalEmail] = useState('');
|
|
51
|
+
// Snapshot of which method the user identified with, set when moving to step 'new'.
|
|
52
|
+
const [identifiedWith, setIdentifiedWith] = useState<'email' | 'phone' | null>(null);
|
|
53
|
+
|
|
47
54
|
// Phone helpers
|
|
48
55
|
const country = useMemo(() => countries.find((c) => c.code === selectedCountry), [selectedCountry]);
|
|
49
56
|
const phoneCode = country ? (country.phoneCode.startsWith('+') ? country.phoneCode : `+${country.phoneCode}`) : '+1';
|
|
@@ -59,17 +66,11 @@ export function LoginForm({ onSuccess, onClose }: LoginFormProps) {
|
|
|
59
66
|
const fullPhone = phoneDigits.length > 0 ? `${phoneCode}${phoneDigits}` : null;
|
|
60
67
|
const emailVal = email.trim() || null;
|
|
61
68
|
|
|
62
|
-
const
|
|
63
|
-
const digits = e.target.value.replace(/\D/g, '');
|
|
64
|
-
const formatted = nationalMask ? formatDigitsToMask(digits, nationalMask) : digits;
|
|
65
|
-
setPhoneValue(formatted);
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const emailInput = (required = false) => (
|
|
69
|
+
const emailInput = (value: string, onChange: (v: string) => void, required = false) => (
|
|
69
70
|
<input
|
|
70
71
|
type="email"
|
|
71
|
-
value={
|
|
72
|
-
onChange={(e) =>
|
|
72
|
+
value={value}
|
|
73
|
+
onChange={(e) => onChange(e.target.value)}
|
|
73
74
|
placeholder="you@example.com"
|
|
74
75
|
className={inputClass}
|
|
75
76
|
autoComplete="email"
|
|
@@ -77,11 +78,11 @@ export function LoginForm({ onSuccess, onClose }: LoginFormProps) {
|
|
|
77
78
|
/>
|
|
78
79
|
);
|
|
79
80
|
|
|
80
|
-
const phoneInput = (required = false) => (
|
|
81
|
+
const phoneInput = (value: string, onChange: (v: string) => void, required = false) => (
|
|
81
82
|
<div className="flex rounded-input border border-primary overflow-hidden focus-within:border-brand focus-within:ring-1 focus-within:ring-brand transition-colors">
|
|
82
83
|
<select
|
|
83
84
|
value={selectedCountry}
|
|
84
|
-
onChange={(e) => { setSelectedCountry(e.target.value);
|
|
85
|
+
onChange={(e) => { setSelectedCountry(e.target.value); onChange(''); }}
|
|
85
86
|
className="border-r border-secondary bg-secondary px-2 py-2.5 text-base text-secondary focus:outline-none"
|
|
86
87
|
aria-label="Country code"
|
|
87
88
|
>
|
|
@@ -91,8 +92,11 @@ export function LoginForm({ onSuccess, onClose }: LoginFormProps) {
|
|
|
91
92
|
</select>
|
|
92
93
|
<input
|
|
93
94
|
type="tel"
|
|
94
|
-
value={
|
|
95
|
-
onChange={
|
|
95
|
+
value={value}
|
|
96
|
+
onChange={(e) => {
|
|
97
|
+
const digits = e.target.value.replace(/\D/g, '');
|
|
98
|
+
onChange(nationalMask ? formatDigitsToMask(digits, nationalMask) : digits);
|
|
99
|
+
}}
|
|
96
100
|
placeholder={nationalPlaceholder}
|
|
97
101
|
className="flex-1 px-3 py-2.5 text-base text-primary placeholder-quaternary bg-transparent focus:outline-none"
|
|
98
102
|
autoComplete="tel"
|
|
@@ -120,11 +124,17 @@ export function LoginForm({ onSuccess, onClose }: LoginFormProps) {
|
|
|
120
124
|
firePixelEvent('Lead');
|
|
121
125
|
captureEvent('portal_login_identified', { method, user_exists: true });
|
|
122
126
|
setWelcomeName(result.firstName ?? null);
|
|
123
|
-
|
|
127
|
+
if (result.hasPassword === false) {
|
|
128
|
+
setIdentifiedWith(emailVal ? 'email' : 'phone');
|
|
129
|
+
setStep('new');
|
|
130
|
+
} else {
|
|
131
|
+
setStep('returning');
|
|
132
|
+
}
|
|
124
133
|
} else if (result.exists === false) {
|
|
125
134
|
await setPixelUserData({ email: emailVal, phone: fullPhone });
|
|
126
135
|
firePixelEvent('Lead');
|
|
127
136
|
captureEvent('portal_login_identified', { method, user_exists: false });
|
|
137
|
+
setIdentifiedWith(emailVal ? 'email' : 'phone');
|
|
128
138
|
setStep('new');
|
|
129
139
|
} else {
|
|
130
140
|
captureEvent('portal_login_failed', { step: 'identifier', reason: 'unknown_error' });
|
|
@@ -177,12 +187,16 @@ export function LoginForm({ onSuccess, onClose }: LoginFormProps) {
|
|
|
177
187
|
setError(null);
|
|
178
188
|
setLoading(true);
|
|
179
189
|
try {
|
|
190
|
+
const additionalPhoneDigits = additionalPhone.replace(/\D/g, '');
|
|
191
|
+
const additionalFullPhone = additionalPhoneDigits.length > 0 ? `${phoneCode}${additionalPhoneDigits}` : null;
|
|
192
|
+
const additionalEmailVal = additionalEmail.trim() || null;
|
|
193
|
+
|
|
180
194
|
const res = await fetch('/api/consumer/signup', {
|
|
181
195
|
method: 'POST',
|
|
182
196
|
headers: { 'Content-Type': 'application/json' },
|
|
183
197
|
body: JSON.stringify({
|
|
184
|
-
email: emailVal,
|
|
185
|
-
phone: fullPhone,
|
|
198
|
+
email: emailVal ?? additionalEmailVal,
|
|
199
|
+
phone: fullPhone ?? additionalFullPhone,
|
|
186
200
|
password,
|
|
187
201
|
password_confirmation: passwordConfirm,
|
|
188
202
|
first_name: firstName.trim() || undefined,
|
|
@@ -209,6 +223,9 @@ export function LoginForm({ onSuccess, onClose }: LoginFormProps) {
|
|
|
209
223
|
setStep('identifier');
|
|
210
224
|
setPassword('');
|
|
211
225
|
setPasswordConfirm('');
|
|
226
|
+
setAdditionalPhone('');
|
|
227
|
+
setAdditionalEmail('');
|
|
228
|
+
setIdentifiedWith(null);
|
|
212
229
|
setError(null);
|
|
213
230
|
};
|
|
214
231
|
|
|
@@ -257,7 +274,7 @@ export function LoginForm({ onSuccess, onClose }: LoginFormProps) {
|
|
|
257
274
|
<form onSubmit={handleContinue} className="space-y-3">
|
|
258
275
|
<div>
|
|
259
276
|
<label className={labelClass}>Email address</label>
|
|
260
|
-
{emailInput()}
|
|
277
|
+
{emailInput(email, setEmail)}
|
|
261
278
|
</div>
|
|
262
279
|
<div className="flex items-center gap-3">
|
|
263
280
|
<hr className="flex-1 border-secondary" />
|
|
@@ -266,7 +283,7 @@ export function LoginForm({ onSuccess, onClose }: LoginFormProps) {
|
|
|
266
283
|
</div>
|
|
267
284
|
<div>
|
|
268
285
|
<label className={labelClass}>Phone number</label>
|
|
269
|
-
{phoneInput()}
|
|
286
|
+
{phoneInput(phoneValue, setPhoneValue)}
|
|
270
287
|
</div>
|
|
271
288
|
<div className="pt-1">
|
|
272
289
|
<button
|
|
@@ -347,16 +364,16 @@ export function LoginForm({ onSuccess, onClose }: LoginFormProps) {
|
|
|
347
364
|
</div>
|
|
348
365
|
)}
|
|
349
366
|
{/* Collect the missing contact method so the business can reach out */}
|
|
350
|
-
{
|
|
367
|
+
{identifiedWith === 'email' && (
|
|
351
368
|
<div>
|
|
352
369
|
<label className={labelClass}>Phone number</label>
|
|
353
|
-
{phoneInput(true)}
|
|
370
|
+
{phoneInput(additionalPhone, setAdditionalPhone, true)}
|
|
354
371
|
</div>
|
|
355
372
|
)}
|
|
356
|
-
{
|
|
373
|
+
{identifiedWith === 'phone' && (
|
|
357
374
|
<div>
|
|
358
375
|
<label className={labelClass}>Email address</label>
|
|
359
|
-
{emailInput(true)}
|
|
376
|
+
{emailInput(additionalEmail, setAdditionalEmail, true)}
|
|
360
377
|
</div>
|
|
361
378
|
)}
|
|
362
379
|
<div>
|
|
@@ -194,19 +194,23 @@ export async function KeystoneRootLayout(props: {
|
|
|
194
194
|
jobApplicationFormDefinition={jobApplicationFormDefinition ?? null}
|
|
195
195
|
marketingListSignupFormDefinition={marketingListSignupFormDefinition ?? null}
|
|
196
196
|
>
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
197
|
+
{theme !== 'custom' && (
|
|
198
|
+
<HeaderNavigation
|
|
199
|
+
config={dynamicConfig}
|
|
200
|
+
companyInformation={companyInformation}
|
|
201
|
+
websitePhotos={websitePhotos}
|
|
202
|
+
props={headerProps}
|
|
203
|
+
logoText={headerOverrides?.logoText}
|
|
204
|
+
/>
|
|
205
|
+
)}
|
|
204
206
|
{children}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
207
|
+
{theme !== 'custom' && (
|
|
208
|
+
<FooterHome
|
|
209
|
+
config={dynamicConfig}
|
|
210
|
+
companyInformation={companyInformation}
|
|
211
|
+
websitePhotos={websitePhotos}
|
|
212
|
+
/>
|
|
213
|
+
)}
|
|
210
214
|
{chatEnabled ? (
|
|
211
215
|
<ChatWidget
|
|
212
216
|
position={options?.chatPosition || 'bottom-right'}
|
package/src/themes/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ export const THEME_CONFIG = {
|
|
|
8
8
|
aman: '.aman', // Aman Hotels variant files (hero-home.aman.tsx)
|
|
9
9
|
barelux: '.barelux', // Bare Lux Studio variant files (hero-home.barelux.tsx)
|
|
10
10
|
balance: '.balance', // Balance Aesthetics variant files (hero-home.balance.tsx)
|
|
11
|
+
custom: '', // Fully custom sites — no design-system CSS loaded, all styling built in the site itself
|
|
11
12
|
} as const;
|
|
12
13
|
|
|
13
14
|
export type Theme = keyof typeof THEME_CONFIG;
|