astro-tractstack 2.0.30 → 2.0.32
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/dist/index.js +10 -36
- package/package.json +1 -1
- package/templates/src/components/compositor/Compositor.tsx +8 -0
- package/templates/src/components/compositor/nodes/Pane_DesignLibrary.tsx +169 -48
- package/templates/src/components/compositor/nodes/Pane_eraser.tsx +153 -36
- package/templates/src/stores/nodes.ts +10 -0
- package/templates/src/utils/auth.ts +6 -3
- package/utils/inject-files.ts +0 -27
- package/templates/src/components/tenant/RegistrationForm.tsx +0 -449
- package/templates/src/pages/sandbox/activate.astro +0 -258
- package/templates/src/pages/sandbox/register.astro +0 -44
- package/templates/src/pages/sandbox/success.astro +0 -179
- package/templates/src/utils/api/tenantConfig.ts +0 -97
- package/templates/src/utils/api/tenantHelpers.ts +0 -172
package/utils/inject-files.ts
CHANGED
|
@@ -2086,20 +2086,6 @@ export async function injectTemplateFiles(
|
|
|
2086
2086
|
src: resolve('../templates/socials/youtube.svg'),
|
|
2087
2087
|
dest: 'public/socials/youtube.svg',
|
|
2088
2088
|
},
|
|
2089
|
-
|
|
2090
|
-
// Multi-Tenant Features
|
|
2091
|
-
{
|
|
2092
|
-
src: resolve('../templates/src/components/tenant/RegistrationForm.tsx'),
|
|
2093
|
-
dest: 'src/components/tenant/RegistrationForm.tsx',
|
|
2094
|
-
},
|
|
2095
|
-
{
|
|
2096
|
-
src: resolve('../templates/src/utils/api/tenantConfig.ts'),
|
|
2097
|
-
dest: 'src/utils/api/tenantConfig.ts',
|
|
2098
|
-
},
|
|
2099
|
-
{
|
|
2100
|
-
src: resolve('../templates/src/utils/api/tenantHelpers.ts'),
|
|
2101
|
-
dest: 'src/utils/api/tenantHelpers.ts',
|
|
2102
|
-
},
|
|
2103
2089
|
// Multi-Tenant Features (Conditional)
|
|
2104
2090
|
...(config?.enableMultiTenant
|
|
2105
2091
|
? [
|
|
@@ -2108,19 +2094,6 @@ export async function injectTemplateFiles(
|
|
|
2108
2094
|
src: resolve('../templates/src/middleware.ts'),
|
|
2109
2095
|
dest: 'src/middleware.ts',
|
|
2110
2096
|
},
|
|
2111
|
-
// Pages
|
|
2112
|
-
{
|
|
2113
|
-
src: resolve('../templates/src/pages/sandbox/register.astro'),
|
|
2114
|
-
dest: 'src/pages/sandbox/register.astro',
|
|
2115
|
-
},
|
|
2116
|
-
{
|
|
2117
|
-
src: resolve('../templates/src/pages/sandbox/activate.astro'),
|
|
2118
|
-
dest: 'src/pages/sandbox/activate.astro',
|
|
2119
|
-
},
|
|
2120
|
-
{
|
|
2121
|
-
src: resolve('../templates/src/pages/sandbox/success.astro'),
|
|
2122
|
-
dest: 'src/pages/sandbox/success.astro',
|
|
2123
|
-
},
|
|
2124
2097
|
]
|
|
2125
2098
|
: []),
|
|
2126
2099
|
// Multi-Tenant Types (Always included due to plan reference)
|
|
@@ -1,449 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react';
|
|
2
|
-
import { useFormState } from '@/hooks/useFormState';
|
|
3
|
-
import {
|
|
4
|
-
convertToLocalState,
|
|
5
|
-
convertToBackendFormat,
|
|
6
|
-
validateTenantRegistration,
|
|
7
|
-
tenantStateIntercept,
|
|
8
|
-
} from '@/utils/api/tenantHelpers';
|
|
9
|
-
import { checkTenantCapacity, provisionTenant } from '@/utils/api/tenantConfig';
|
|
10
|
-
import { TractStackAPI } from '@/utils/api';
|
|
11
|
-
import UnsavedChangesBar from '@/components/form/UnsavedChangesBar';
|
|
12
|
-
import StringInput from '@/components/form/StringInput';
|
|
13
|
-
import BooleanToggle from '@/components/form/BooleanToggle';
|
|
14
|
-
import type { TenantCapacity } from '@/types/multiTenant';
|
|
15
|
-
import type { TenantRegistrationState } from '@/types/multiTenant';
|
|
16
|
-
|
|
17
|
-
interface RegistrationFormProps {
|
|
18
|
-
isInitMode?: boolean;
|
|
19
|
-
onSuccess?: (tenantId?: string) => void;
|
|
20
|
-
onCapacityFull?: () => void;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export default function RegistrationForm({
|
|
24
|
-
isInitMode = false,
|
|
25
|
-
onSuccess,
|
|
26
|
-
onCapacityFull,
|
|
27
|
-
}: RegistrationFormProps) {
|
|
28
|
-
const [capacity, setCapacity] = useState<TenantCapacity | null>(null);
|
|
29
|
-
const [loadingCapacity, setLoadingCapacity] = useState(!isInitMode);
|
|
30
|
-
const [capacityError, setCapacityError] = useState<string | null>(null);
|
|
31
|
-
|
|
32
|
-
// Modal state for tenant preparation
|
|
33
|
-
const [showPreparationModal, setShowPreparationModal] = useState(false);
|
|
34
|
-
const [activationToken, setActivationToken] = useState<string>('');
|
|
35
|
-
const [preparingTenantId, setPreparingTenantId] = useState<string>('');
|
|
36
|
-
|
|
37
|
-
// Load capacity information on mount
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
if (isInitMode) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const loadCapacity = async () => {
|
|
44
|
-
try {
|
|
45
|
-
const capacityData = await checkTenantCapacity('default');
|
|
46
|
-
setCapacity(capacityData);
|
|
47
|
-
|
|
48
|
-
// Check if at capacity
|
|
49
|
-
if (!capacityData.available) {
|
|
50
|
-
onCapacityFull?.();
|
|
51
|
-
}
|
|
52
|
-
} catch (error) {
|
|
53
|
-
setCapacityError(
|
|
54
|
-
error instanceof Error ? error.message : 'Failed to load capacity'
|
|
55
|
-
);
|
|
56
|
-
} finally {
|
|
57
|
-
setLoadingCapacity(false);
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
loadCapacity();
|
|
62
|
-
}, [onCapacityFull, isInitMode]);
|
|
63
|
-
|
|
64
|
-
const initialState: TenantRegistrationState = convertToLocalState();
|
|
65
|
-
|
|
66
|
-
const formState = useFormState({
|
|
67
|
-
initialData: initialState,
|
|
68
|
-
validator: (data) =>
|
|
69
|
-
validateTenantRegistration(data, undefined, isInitMode),
|
|
70
|
-
interceptor: tenantStateIntercept,
|
|
71
|
-
onSave: async (data) => {
|
|
72
|
-
try {
|
|
73
|
-
if (isInitMode) {
|
|
74
|
-
const api = new TractStackAPI('default');
|
|
75
|
-
const setupData = {
|
|
76
|
-
adminEmail: data.email.trim(),
|
|
77
|
-
adminPassword: data.adminPassword.trim(),
|
|
78
|
-
...(data.tursoEnabled && {
|
|
79
|
-
tursoDatabaseURL: data.tursoDatabaseURL.trim(),
|
|
80
|
-
tursoAuthToken: data.tursoAuthToken.trim(),
|
|
81
|
-
}),
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const response = await api.post(
|
|
85
|
-
'/api/v1/setup/initialize',
|
|
86
|
-
setupData
|
|
87
|
-
);
|
|
88
|
-
if (!response.success) {
|
|
89
|
-
throw new Error(response.error || 'Setup failed');
|
|
90
|
-
}
|
|
91
|
-
window.location.href = '/storykeep';
|
|
92
|
-
return data;
|
|
93
|
-
} else {
|
|
94
|
-
const backendData = convertToBackendFormat(data);
|
|
95
|
-
const result = await provisionTenant('default', backendData);
|
|
96
|
-
|
|
97
|
-
// Store the activation token and tenant ID for the modal
|
|
98
|
-
setActivationToken(result.token);
|
|
99
|
-
setPreparingTenantId(data.tenantId);
|
|
100
|
-
setShowPreparationModal(true);
|
|
101
|
-
|
|
102
|
-
return data;
|
|
103
|
-
}
|
|
104
|
-
} catch (error) {
|
|
105
|
-
console.error('Tenant provisioning failed:', error);
|
|
106
|
-
throw error;
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
const { state, updateField, errors } = formState;
|
|
112
|
-
|
|
113
|
-
// Handle activation button click in modal
|
|
114
|
-
const handleActivate = () => {
|
|
115
|
-
if (activationToken && preparingTenantId) {
|
|
116
|
-
// Check if we're in dev mode
|
|
117
|
-
const isDev = import.meta.env.DEV;
|
|
118
|
-
|
|
119
|
-
if (isDev) {
|
|
120
|
-
// In dev mode, navigate to local activation page with tenant ID param
|
|
121
|
-
window.location.href = `/sandbox/activate?token=${activationToken}&tenantId=${preparingTenantId}`;
|
|
122
|
-
} else {
|
|
123
|
-
// In production, use the full subdomain URL
|
|
124
|
-
window.location.href = `https://${preparingTenantId}.sandbox.freewebpress.com/sandbox/activate?token=${activationToken}`;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
// Handle modal close
|
|
130
|
-
const handleModalClose = () => {
|
|
131
|
-
setShowPreparationModal(false);
|
|
132
|
-
onSuccess?.(preparingTenantId);
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
if (loadingCapacity) {
|
|
136
|
-
return (
|
|
137
|
-
<div className="mx-auto max-w-2xl p-6">
|
|
138
|
-
<div className="animate-pulse">
|
|
139
|
-
<div className="mb-4 h-8 rounded bg-gray-200"></div>
|
|
140
|
-
<div className="mb-2 h-4 rounded bg-gray-200"></div>
|
|
141
|
-
<div className="mb-4 h-4 rounded bg-gray-200"></div>
|
|
142
|
-
</div>
|
|
143
|
-
</div>
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (capacityError) {
|
|
148
|
-
return (
|
|
149
|
-
<div className="mx-auto max-w-2xl p-6">
|
|
150
|
-
<div className="rounded-lg border border-red-200 bg-red-50 p-4">
|
|
151
|
-
<h3 className="mb-2 text-lg font-bold text-red-800">
|
|
152
|
-
Unable to Load Registration
|
|
153
|
-
</h3>
|
|
154
|
-
<p className="text-red-700">{capacityError}</p>
|
|
155
|
-
</div>
|
|
156
|
-
</div>
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (!capacity && !isInitMode) {
|
|
161
|
-
return (
|
|
162
|
-
<div className="mx-auto max-w-2xl p-6">
|
|
163
|
-
<div className="rounded-lg border border-yellow-200 bg-yellow-50 p-4">
|
|
164
|
-
<h3 className="mb-2 text-lg font-bold text-yellow-800">
|
|
165
|
-
Registration Currently Unavailable
|
|
166
|
-
</h3>
|
|
167
|
-
<p className="text-yellow-700">
|
|
168
|
-
We've reached our current capacity. Please check back later for
|
|
169
|
-
availability.
|
|
170
|
-
</p>
|
|
171
|
-
</div>
|
|
172
|
-
</div>
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
return (
|
|
177
|
-
<>
|
|
178
|
-
<div className="mx-auto max-w-2xl p-6" style={{ paddingBottom: '112px' }}>
|
|
179
|
-
<div className="rounded-lg bg-white p-8 shadow-lg">
|
|
180
|
-
<div className="mb-8">
|
|
181
|
-
<div className="h-16">
|
|
182
|
-
<img
|
|
183
|
-
src="/brand/logo.svg"
|
|
184
|
-
className="pointer-events-none mx-auto h-full"
|
|
185
|
-
alt="Logo"
|
|
186
|
-
/>
|
|
187
|
-
</div>
|
|
188
|
-
|
|
189
|
-
<h2 className="mb-2 mt-8 text-2xl font-bold text-gray-900">
|
|
190
|
-
{isInitMode ? `Install Tract Stack` : `Try Tract Stack`}
|
|
191
|
-
</h2>
|
|
192
|
-
{!isInitMode && (
|
|
193
|
-
<p className="text-gray-600">
|
|
194
|
-
Set up your free sandbox environment to try TractStack.
|
|
195
|
-
</p>
|
|
196
|
-
)}
|
|
197
|
-
{!isInitMode && capacity && (
|
|
198
|
-
<div className="mt-4 rounded-lg bg-orange-50 p-4">
|
|
199
|
-
<div className="flex">
|
|
200
|
-
<div className="flex-shrink-0">
|
|
201
|
-
<svg
|
|
202
|
-
className="h-5 w-5 text-orange-400"
|
|
203
|
-
viewBox="0 0 20 20"
|
|
204
|
-
fill="currentColor"
|
|
205
|
-
>
|
|
206
|
-
<path
|
|
207
|
-
fillRule="evenodd"
|
|
208
|
-
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
|
|
209
|
-
clipRule="evenodd"
|
|
210
|
-
/>
|
|
211
|
-
</svg>
|
|
212
|
-
</div>
|
|
213
|
-
<div className="ml-3">
|
|
214
|
-
<p className="text-sm text-orange-700">
|
|
215
|
-
{capacity.availableSlots} of {capacity.maxTenants} slots
|
|
216
|
-
remaining
|
|
217
|
-
</p>
|
|
218
|
-
</div>
|
|
219
|
-
</div>
|
|
220
|
-
</div>
|
|
221
|
-
)}
|
|
222
|
-
</div>
|
|
223
|
-
|
|
224
|
-
<div className="space-y-6">
|
|
225
|
-
{/* Tenant ID field - hidden in init mode */}
|
|
226
|
-
{!isInitMode && (
|
|
227
|
-
<div>
|
|
228
|
-
<label className="mb-1 block text-sm font-bold text-gray-700">
|
|
229
|
-
Tenant ID *
|
|
230
|
-
</label>
|
|
231
|
-
<StringInput
|
|
232
|
-
value={state.tenantId}
|
|
233
|
-
onChange={(value) => updateField('tenantId', value)}
|
|
234
|
-
placeholder="my-awesome-tenant"
|
|
235
|
-
error={errors.tenantId}
|
|
236
|
-
/>
|
|
237
|
-
<p className="mt-1 text-sm text-gray-500">
|
|
238
|
-
3-12 characters, lowercase letters, numbers, and dashes only
|
|
239
|
-
</p>
|
|
240
|
-
</div>
|
|
241
|
-
)}
|
|
242
|
-
|
|
243
|
-
{/* Admin Password */}
|
|
244
|
-
<div>
|
|
245
|
-
<label className="mb-1 block text-sm font-bold text-gray-700">
|
|
246
|
-
Admin Password *
|
|
247
|
-
</label>
|
|
248
|
-
<StringInput
|
|
249
|
-
value={state.adminPassword}
|
|
250
|
-
onChange={(value) => updateField('adminPassword', value)}
|
|
251
|
-
type="password"
|
|
252
|
-
placeholder="Strong password for admin access"
|
|
253
|
-
error={errors.adminPassword}
|
|
254
|
-
/>
|
|
255
|
-
<p className="mt-1 text-sm text-gray-500">Minimum 8 characters</p>
|
|
256
|
-
</div>
|
|
257
|
-
|
|
258
|
-
{/* Confirm Password */}
|
|
259
|
-
<div>
|
|
260
|
-
<label className="mb-1 block text-sm font-bold text-gray-700">
|
|
261
|
-
Confirm Password *
|
|
262
|
-
</label>
|
|
263
|
-
<StringInput
|
|
264
|
-
value={state.confirmPassword}
|
|
265
|
-
onChange={(value) => updateField('confirmPassword', value)}
|
|
266
|
-
type="password"
|
|
267
|
-
placeholder="Confirm your admin password"
|
|
268
|
-
error={errors.confirmPassword}
|
|
269
|
-
/>
|
|
270
|
-
</div>
|
|
271
|
-
|
|
272
|
-
{/* Name */}
|
|
273
|
-
<div>
|
|
274
|
-
<label className="mb-1 block text-sm font-bold text-gray-700">
|
|
275
|
-
Your Name *
|
|
276
|
-
</label>
|
|
277
|
-
<StringInput
|
|
278
|
-
value={state.name}
|
|
279
|
-
onChange={(value) => updateField('name', value)}
|
|
280
|
-
placeholder="John Doe"
|
|
281
|
-
error={errors.name}
|
|
282
|
-
/>
|
|
283
|
-
</div>
|
|
284
|
-
|
|
285
|
-
{/* Email */}
|
|
286
|
-
<div>
|
|
287
|
-
<label className="mb-1 block text-sm font-bold text-gray-700">
|
|
288
|
-
Email Address *
|
|
289
|
-
</label>
|
|
290
|
-
<StringInput
|
|
291
|
-
value={state.email}
|
|
292
|
-
onChange={(value) => updateField('email', value)}
|
|
293
|
-
type="email"
|
|
294
|
-
placeholder="susie@amazing.com"
|
|
295
|
-
error={errors.email}
|
|
296
|
-
/>
|
|
297
|
-
<p className="mt-1 text-sm text-gray-500">
|
|
298
|
-
{isInitMode
|
|
299
|
-
? `Used for password reset, etc.`
|
|
300
|
-
: `You'll receive an activation email at this address`}
|
|
301
|
-
</p>
|
|
302
|
-
</div>
|
|
303
|
-
|
|
304
|
-
{/* Database Configuration */}
|
|
305
|
-
<div className="rounded-lg border border-gray-200 p-4">
|
|
306
|
-
<div className="mb-4">
|
|
307
|
-
<BooleanToggle
|
|
308
|
-
value={state.tursoEnabled}
|
|
309
|
-
onChange={(value) => updateField('tursoEnabled', value)}
|
|
310
|
-
label="Enable Turso Database"
|
|
311
|
-
/>
|
|
312
|
-
<p className="mt-2 text-sm text-gray-500">
|
|
313
|
-
By default, your tenant will use SQLite3. Enable this option
|
|
314
|
-
to use your own Turso database instead.
|
|
315
|
-
</p>
|
|
316
|
-
</div>
|
|
317
|
-
|
|
318
|
-
{state.tursoEnabled && (
|
|
319
|
-
<div className="space-y-4 rounded-lg bg-gray-50 p-4">
|
|
320
|
-
<div>
|
|
321
|
-
<label className="mb-1 block text-sm font-bold text-gray-700">
|
|
322
|
-
Turso Database URL *
|
|
323
|
-
</label>
|
|
324
|
-
<StringInput
|
|
325
|
-
value={state.tursoDatabaseURL}
|
|
326
|
-
onChange={(value) =>
|
|
327
|
-
updateField('tursoDatabaseURL', value)
|
|
328
|
-
}
|
|
329
|
-
placeholder="libsql://your-database.turso.io"
|
|
330
|
-
error={errors.tursoDatabaseURL}
|
|
331
|
-
/>
|
|
332
|
-
<p className="mt-1 text-sm text-gray-500">
|
|
333
|
-
Must start with libsql://
|
|
334
|
-
</p>
|
|
335
|
-
</div>
|
|
336
|
-
|
|
337
|
-
<div>
|
|
338
|
-
<label className="mb-1 block text-sm font-bold text-gray-700">
|
|
339
|
-
Turso Auth Token *
|
|
340
|
-
</label>
|
|
341
|
-
<StringInput
|
|
342
|
-
value={state.tursoAuthToken}
|
|
343
|
-
onChange={(value) => updateField('tursoAuthToken', value)}
|
|
344
|
-
type="password"
|
|
345
|
-
placeholder="Your Turso auth token"
|
|
346
|
-
error={errors.tursoAuthToken}
|
|
347
|
-
/>
|
|
348
|
-
</div>
|
|
349
|
-
</div>
|
|
350
|
-
)}
|
|
351
|
-
</div>
|
|
352
|
-
</div>
|
|
353
|
-
|
|
354
|
-
<UnsavedChangesBar
|
|
355
|
-
formState={formState}
|
|
356
|
-
message={
|
|
357
|
-
isInitMode
|
|
358
|
-
? `Install Tract Stack`
|
|
359
|
-
: `Complete your tenant registration`
|
|
360
|
-
}
|
|
361
|
-
saveLabel={isInitMode ? `Install` : `Create Tenant`}
|
|
362
|
-
cancelLabel="Clear Form"
|
|
363
|
-
/>
|
|
364
|
-
</div>
|
|
365
|
-
</div>
|
|
366
|
-
|
|
367
|
-
{/* Tenant Preparation Modal */}
|
|
368
|
-
{showPreparationModal && (
|
|
369
|
-
<div className="fixed inset-0 z-50 overflow-y-auto">
|
|
370
|
-
<div className="flex min-h-screen items-center justify-center p-4">
|
|
371
|
-
<div
|
|
372
|
-
className="fixed inset-0 bg-black opacity-25"
|
|
373
|
-
onClick={handleModalClose}
|
|
374
|
-
/>
|
|
375
|
-
|
|
376
|
-
<div className="relative w-full max-w-lg rounded-lg bg-white shadow-xl">
|
|
377
|
-
<div className="p-6">
|
|
378
|
-
<div className="mb-6 text-center">
|
|
379
|
-
<div className="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
|
|
380
|
-
<svg
|
|
381
|
-
className="h-6 w-6 text-green-600"
|
|
382
|
-
fill="none"
|
|
383
|
-
viewBox="0 0 24 24"
|
|
384
|
-
strokeWidth="1.5"
|
|
385
|
-
stroke="currentColor"
|
|
386
|
-
>
|
|
387
|
-
<path
|
|
388
|
-
strokeLinecap="round"
|
|
389
|
-
strokeLinejoin="round"
|
|
390
|
-
d="M4.5 12.75l6 6 9-13.5"
|
|
391
|
-
/>
|
|
392
|
-
</svg>
|
|
393
|
-
</div>
|
|
394
|
-
<h3 className="text-xl font-bold text-gray-900">
|
|
395
|
-
Your TractStack is Being Prepared
|
|
396
|
-
</h3>
|
|
397
|
-
<p className="mt-2 text-sm text-gray-600">
|
|
398
|
-
We've successfully provisioned your tenant{' '}
|
|
399
|
-
<strong>{preparingTenantId}</strong>. Click the button below
|
|
400
|
-
to activate your sandbox environment.
|
|
401
|
-
</p>
|
|
402
|
-
</div>
|
|
403
|
-
|
|
404
|
-
<div className="mb-6 rounded-lg bg-orange-50 p-4">
|
|
405
|
-
<div className="flex">
|
|
406
|
-
<div className="flex-shrink-0">
|
|
407
|
-
<svg
|
|
408
|
-
className="h-5 w-5 text-orange-400"
|
|
409
|
-
viewBox="0 0 20 20"
|
|
410
|
-
fill="currentColor"
|
|
411
|
-
>
|
|
412
|
-
<path
|
|
413
|
-
fillRule="evenodd"
|
|
414
|
-
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
|
|
415
|
-
clipRule="evenodd"
|
|
416
|
-
/>
|
|
417
|
-
</svg>
|
|
418
|
-
</div>
|
|
419
|
-
<div className="ml-3">
|
|
420
|
-
<p className="text-sm text-orange-700">
|
|
421
|
-
You'll also receive an email with this activation link
|
|
422
|
-
at <strong>{state.email}</strong>
|
|
423
|
-
</p>
|
|
424
|
-
</div>
|
|
425
|
-
</div>
|
|
426
|
-
</div>
|
|
427
|
-
|
|
428
|
-
<div className="flex justify-end space-x-3">
|
|
429
|
-
<button
|
|
430
|
-
onClick={handleModalClose}
|
|
431
|
-
className="rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-bold text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-cyan-500"
|
|
432
|
-
>
|
|
433
|
-
I'll Use the Email Link
|
|
434
|
-
</button>
|
|
435
|
-
<button
|
|
436
|
-
onClick={handleActivate}
|
|
437
|
-
className="rounded-md border border-transparent bg-cyan-600 px-4 py-2 text-sm font-bold text-white hover:bg-cyan-700 focus:outline-none focus:ring-2 focus:ring-cyan-500"
|
|
438
|
-
>
|
|
439
|
-
Activate Now
|
|
440
|
-
</button>
|
|
441
|
-
</div>
|
|
442
|
-
</div>
|
|
443
|
-
</div>
|
|
444
|
-
</div>
|
|
445
|
-
</div>
|
|
446
|
-
)}
|
|
447
|
-
</>
|
|
448
|
-
);
|
|
449
|
-
}
|