@carrierllc/mcp 0.2.18 → 0.2.20
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 +22 -1
- package/dist/chunk-NHQDO2E7.js +461 -0
- package/dist/chunk-NHQDO2E7.js.map +1 -0
- package/dist/cli.js +877 -148
- package/dist/cli.js.map +1 -1
- package/dist/index.js +148 -297
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/plugin/carrier/commands/storefront.md +24 -12
- package/plugin/carrier/skills/carrier-operations/SKILL.md +4 -1
- package/templates/storefront/{next.config.ts → next.config.mjs} +2 -6
- package/templates/storefront/package-lock.json +12722 -0
- package/templates/storefront/package.json +3 -3
- package/templates/storefront/public/brand/logo.svg +13 -0
- package/templates/storefront/src/app/activate/[orderId]/ActivateClient.tsx +12 -12
- package/templates/storefront/src/app/checkout/[templateId]/CheckoutClient.tsx +5 -5
- package/templates/storefront/src/app/checkout/success/CheckoutSuccessClient.tsx +29 -29
- package/templates/storefront/src/app/checkout/success/page.tsx +1 -1
- package/templates/storefront/src/app/contact/page.tsx +33 -25
- package/templates/storefront/src/app/dashboard/page.tsx +7 -4
- package/templates/storefront/src/app/globals.css +72 -82
- package/templates/storefront/src/app/help/page.tsx +22 -17
- package/templates/storefront/src/app/layout.tsx +15 -4
- package/templates/storefront/src/app/page.tsx +16 -7
- package/templates/storefront/src/app/shop/ShopClient.tsx +7 -3
- package/templates/storefront/src/app/sign-in/[[...sign-in]]/page.tsx +1 -1
- package/templates/storefront/src/app/sign-up/[[...sign-up]]/StorefrontSignUpClient.tsx +16 -16
- package/templates/storefront/src/app/sign-up/[[...sign-up]]/page.tsx +1 -1
- package/templates/storefront/src/brand.config.ts +3 -3
- package/templates/storefront/src/components/faq/FaqSection.tsx +23 -26
- package/templates/storefront/src/components/footer/StorefrontFooter.tsx +37 -22
- package/templates/storefront/src/components/landing/FinalCTA.tsx +28 -0
- package/templates/storefront/src/components/landing/HeroSection.tsx +57 -24
- package/templates/storefront/src/components/landing/HowItWorks.tsx +44 -0
- package/templates/storefront/src/components/landing/PlanCard.tsx +58 -28
- package/templates/storefront/src/components/nav/StorefrontNav.tsx +59 -24
- package/templates/storefront/src/components/theme/BrandStyles.tsx +8 -0
- package/templates/storefront/src/components/theme/ThemeToggle.tsx +27 -0
- package/templates/storefront/src/components/theme/clerk-appearance.ts +33 -35
- package/templates/storefront/src/components/theme/theme-provider.tsx +25 -32
- package/templates/storefront/src/components/theme/theme-script.tsx +4 -7
- package/templates/storefront/src/lib/checkout-order-claim.ts +16 -2
- package/templates/storefront/src/lib/complete-email-sign-up.ts +3 -3
- package/templates/storefront/src/lib/verify-checkout-session.ts +19 -2
- package/templates/storefront/src/vendor/ui/brand.ts +6 -6
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"clsx": "^2.1.1",
|
|
18
18
|
"framer-motion": "^11.15.0",
|
|
19
19
|
"geist": "^1.3.1",
|
|
20
|
-
"next": "^
|
|
20
|
+
"next": "^16.3.0",
|
|
21
21
|
"react": "^19.0.0",
|
|
22
22
|
"react-dom": "^19.0.0",
|
|
23
23
|
"tailwind-merge": "^2.6.0"
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"@types/node": "^22.0.0",
|
|
30
30
|
"@types/react": "^19.0.0",
|
|
31
31
|
"@types/react-dom": "^19.0.0",
|
|
32
|
-
"eslint": "^
|
|
33
|
-
"eslint-config-next": "^
|
|
32
|
+
"eslint": "^10.8.1",
|
|
33
|
+
"eslint-config-next": "^16.3.0",
|
|
34
34
|
"tailwindcss": "^4.1.4",
|
|
35
35
|
"typescript": "^5.8.0",
|
|
36
36
|
"wrangler": "^4.94.0"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512" role="img" aria-label="Carrier logo">
|
|
3
|
+
<defs>
|
|
4
|
+
<linearGradient id="lgcf797g" x1="0" y1="0" x2="1" y2="1">
|
|
5
|
+
<stop offset="0%" stop-color="#ff9c75"/>
|
|
6
|
+
<stop offset="55%" stop-color="#FF6B35"/>
|
|
7
|
+
<stop offset="100%" stop-color="#984625"/>
|
|
8
|
+
</linearGradient>
|
|
9
|
+
</defs>
|
|
10
|
+
<rect width="512" height="512" rx="96" fill="#FAFAF7"/>
|
|
11
|
+
<path d="M256 64 C320 64 400 96 424 176 C448 256 424 352 344 416 C280 464 232 464 168 416 C88 352 64 256 88 176 C112 96 192 64 256 64 Z" fill="url(#lgcf797g)"/>
|
|
12
|
+
<text x="256" y="300" text-anchor="middle" font-family="Georgia, 'Times New Roman', serif" font-size="220" font-weight="700" fill="#FFF8EB">C</text>
|
|
13
|
+
</svg>
|
|
@@ -51,34 +51,34 @@ export function ActivateClient({ orderId }: Props) {
|
|
|
51
51
|
<div className="inline-flex items-center justify-center size-14 rounded-full bg-emerald-500/10 border border-emerald-500/30 mb-4">
|
|
52
52
|
<span className="text-2xl">✓</span>
|
|
53
53
|
</div>
|
|
54
|
-
<h1 className="text-2xl font-bold text-
|
|
55
|
-
<p className="mt-2 text-sm text-
|
|
54
|
+
<h1 className="text-2xl font-bold text-sand-900 dark:text-sand-50">Your eSIM is ready</h1>
|
|
55
|
+
<p className="mt-2 text-sm text-sand-500">Order: {orderId}</p>
|
|
56
56
|
</div>
|
|
57
57
|
|
|
58
58
|
{/* QR Code placeholder */}
|
|
59
|
-
<div className="rounded-2xl border border-
|
|
60
|
-
<p className="text-
|
|
59
|
+
<div className="rounded-2xl border border-sand-200 dark:border-sand-800 bg-white dark:bg-sand-900 p-8 text-center mb-8">
|
|
60
|
+
<p className="text-sand-600 dark:text-sand-400 text-sm mb-2">
|
|
61
61
|
Your QR code will appear here once your order is confirmed.
|
|
62
62
|
</p>
|
|
63
|
-
<p className="text-
|
|
63
|
+
<p className="text-sand-400 text-xs">
|
|
64
64
|
This usually takes less than a minute. Refresh if needed.
|
|
65
65
|
</p>
|
|
66
66
|
</div>
|
|
67
67
|
|
|
68
68
|
{/* Optional phone / WhatsApp collection */}
|
|
69
|
-
<div className="rounded-2xl border border-
|
|
69
|
+
<div className="rounded-2xl border border-sand-200 dark:border-sand-800 bg-white dark:bg-sand-900 p-6">
|
|
70
70
|
<div className="flex items-start gap-3 mb-4">
|
|
71
71
|
<div className="shrink-0 mt-0.5">
|
|
72
|
-
<span className="inline-flex items-center justify-center size-5 rounded-full bg-white/10 text-[10px] text-
|
|
72
|
+
<span className="inline-flex items-center justify-center size-5 rounded-full bg-white/10 text-[10px] text-sand-500">
|
|
73
73
|
?
|
|
74
74
|
</span>
|
|
75
75
|
</div>
|
|
76
76
|
<div>
|
|
77
|
-
<p className="text-sm font-medium text-
|
|
77
|
+
<p className="text-sm font-medium text-sand-900 dark:text-sand-50">
|
|
78
78
|
Get eSIM updates via WhatsApp{" "}
|
|
79
|
-
<span className="text-
|
|
79
|
+
<span className="text-sand-400 font-normal">(optional)</span>
|
|
80
80
|
</p>
|
|
81
|
-
<p className="text-xs text-
|
|
81
|
+
<p className="text-xs text-sand-500 mt-0.5">
|
|
82
82
|
We'll send your QR code and activation status. You can skip this.
|
|
83
83
|
</p>
|
|
84
84
|
</div>
|
|
@@ -97,7 +97,7 @@ export function ActivateClient({ orderId }: Props) {
|
|
|
97
97
|
value={phone}
|
|
98
98
|
onChange={(e) => setPhone(e.target.value)}
|
|
99
99
|
placeholder="+1 555 000 0000"
|
|
100
|
-
className="flex-1 rounded-lg border border-
|
|
100
|
+
className="flex-1 rounded-lg border border-sand-200 dark:border-sand-800 bg-sand-50 dark:bg-sand-950 px-3 py-2 text-sm text-sand-900 dark:text-sand-50 placeholder:text-sand-400 focus:border-[var(--brand-accent)] focus:outline-none"
|
|
101
101
|
/>
|
|
102
102
|
<button
|
|
103
103
|
type="submit"
|
|
@@ -113,7 +113,7 @@ export function ActivateClient({ orderId }: Props) {
|
|
|
113
113
|
<button
|
|
114
114
|
type="button"
|
|
115
115
|
onClick={() => setSubmitted(true)}
|
|
116
|
-
className="w-full text-center text-xs text-
|
|
116
|
+
className="w-full text-center text-xs text-sand-400 hover:text-sand-500 py-1"
|
|
117
117
|
>
|
|
118
118
|
Skip for now
|
|
119
119
|
</button>
|
|
@@ -86,11 +86,11 @@ export function CheckoutClient({ plan, session }: Props) {
|
|
|
86
86
|
if (checkoutError) {
|
|
87
87
|
return (
|
|
88
88
|
<div className="mx-auto max-w-lg px-4 py-32 text-center">
|
|
89
|
-
<p className="text-
|
|
90
|
-
<p className="text-sm text-
|
|
89
|
+
<p className="text-sand-700 dark:text-sand-200">{checkoutError}</p>
|
|
90
|
+
<p className="text-sm text-sand-400 mt-2">{plan.name}</p>
|
|
91
91
|
<a
|
|
92
92
|
href="/shop"
|
|
93
|
-
className="mt-6 inline-block rounded-full border border-
|
|
93
|
+
className="mt-6 inline-block rounded-full border border-sand-300 dark:border-sand-700 px-6 py-2 text-sm text-sand-900 dark:text-sand-50 hover:bg-white/10 transition-colors"
|
|
94
94
|
>
|
|
95
95
|
← Back to shop
|
|
96
96
|
</a>
|
|
@@ -101,10 +101,10 @@ export function CheckoutClient({ plan, session }: Props) {
|
|
|
101
101
|
return (
|
|
102
102
|
<div className="mx-auto max-w-lg px-4 py-32 text-center">
|
|
103
103
|
<div className="h-8 w-8 rounded-full border-2 border-[var(--brand-accent)] border-t-transparent animate-spin mx-auto mb-4" />
|
|
104
|
-
<p className="text-
|
|
104
|
+
<p className="text-sand-600 dark:text-sand-400">
|
|
105
105
|
{loading ? "Preparing your checkout…" : "Redirecting to payment…"}
|
|
106
106
|
</p>
|
|
107
|
-
<p className="text-sm text-
|
|
107
|
+
<p className="text-sm text-sand-400 mt-2">{plan.name}</p>
|
|
108
108
|
</div>
|
|
109
109
|
);
|
|
110
110
|
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* The orderId is verified server-side before this component renders.
|
|
12
12
|
*/
|
|
13
13
|
import { useState, FormEvent, useEffect, useCallback } from "react";
|
|
14
|
-
import { useSignUp, useSignIn } from "@clerk/nextjs
|
|
14
|
+
import { useSignUp, useSignIn } from "@clerk/nextjs";
|
|
15
15
|
import { useClerk, useAuth } from "@clerk/nextjs";
|
|
16
16
|
import { useRouter } from "next/navigation";
|
|
17
17
|
import { completeEmailSignUpAfterVerify } from "@/lib/complete-email-sign-up";
|
|
@@ -166,7 +166,7 @@ export function CheckoutSuccessClient({
|
|
|
166
166
|
<div className="flex min-h-[calc(100vh-4rem)] items-center justify-center px-4">
|
|
167
167
|
<div className="text-center space-y-3">
|
|
168
168
|
<div className="mx-auto size-8 animate-spin rounded-full border-2 border-[var(--brand-accent)] border-t-transparent" />
|
|
169
|
-
<p className="text-sm text-
|
|
169
|
+
<p className="text-sm text-sand-600 dark:text-sand-400">
|
|
170
170
|
{step === "redirecting" ? "Activating your eSIM…" : "Loading…"}
|
|
171
171
|
</p>
|
|
172
172
|
</div>
|
|
@@ -181,8 +181,8 @@ export function CheckoutSuccessClient({
|
|
|
181
181
|
<div className="rounded-2xl border border-emerald-500/30 bg-emerald-500/10 px-6 py-4">
|
|
182
182
|
<p className="text-lg font-semibold text-emerald-300">✓ Payment successful!</p>
|
|
183
183
|
</div>
|
|
184
|
-
<div className="rounded-2xl border border-
|
|
185
|
-
<p className="text-sm text-
|
|
184
|
+
<div className="rounded-2xl border border-sand-200 dark:border-sand-800 bg-white dark:bg-sand-900 p-8 backdrop-blur space-y-4">
|
|
185
|
+
<p className="text-sm text-sand-700 dark:text-sand-200">We could not link this purchase to your account.</p>
|
|
186
186
|
{error && (
|
|
187
187
|
<p className="rounded-lg border border-red-500/30 bg-red-500/10 px-3 py-2 text-xs text-red-300">
|
|
188
188
|
{error}
|
|
@@ -213,17 +213,17 @@ export function CheckoutSuccessClient({
|
|
|
213
213
|
</div>
|
|
214
214
|
|
|
215
215
|
{/* Auth card */}
|
|
216
|
-
<div className="rounded-2xl border border-
|
|
216
|
+
<div className="rounded-2xl border border-sand-200 dark:border-sand-800 bg-white dark:bg-sand-900 p-8 backdrop-blur">
|
|
217
217
|
{/* Mode toggle */}
|
|
218
218
|
{step === "credentials" && (
|
|
219
|
-
<div className="flex rounded-lg border border-
|
|
219
|
+
<div className="flex rounded-lg border border-sand-200 dark:border-sand-800 p-1 mb-6">
|
|
220
220
|
<button
|
|
221
221
|
type="button"
|
|
222
222
|
onClick={() => { setMode("signup"); setError(null); }}
|
|
223
223
|
className={`flex-1 rounded-md py-1.5 text-sm font-medium transition-colors ${
|
|
224
224
|
mode === "signup"
|
|
225
|
-
? "bg-[var(--brand-accent)] text-
|
|
226
|
-
: "text-
|
|
225
|
+
? "bg-[var(--brand-accent)] text-sand-900 dark:text-sand-50"
|
|
226
|
+
: "text-sand-500 hover:text-sand-600 dark:text-sand-300"
|
|
227
227
|
}`}
|
|
228
228
|
>
|
|
229
229
|
Create account
|
|
@@ -233,8 +233,8 @@ export function CheckoutSuccessClient({
|
|
|
233
233
|
onClick={() => { setMode("signin"); setError(null); }}
|
|
234
234
|
className={`flex-1 rounded-md py-1.5 text-sm font-medium transition-colors ${
|
|
235
235
|
mode === "signin"
|
|
236
|
-
? "bg-[var(--brand-accent)] text-
|
|
237
|
-
: "text-
|
|
236
|
+
? "bg-[var(--brand-accent)] text-sand-900 dark:text-sand-50"
|
|
237
|
+
: "text-sand-500 hover:text-sand-600 dark:text-sand-300"
|
|
238
238
|
}`}
|
|
239
239
|
>
|
|
240
240
|
Sign in
|
|
@@ -245,15 +245,15 @@ export function CheckoutSuccessClient({
|
|
|
245
245
|
{/* Sign-up: credentials step */}
|
|
246
246
|
{step === "credentials" && mode === "signup" && (
|
|
247
247
|
<>
|
|
248
|
-
<h2 className="text-base font-semibold text-
|
|
249
|
-
<p className="text-xs text-
|
|
248
|
+
<h2 className="text-base font-semibold text-sand-900 dark:text-sand-50 mb-1">Create your account</h2>
|
|
249
|
+
<p className="text-xs text-sand-500 mb-5">
|
|
250
250
|
{emailLocked
|
|
251
251
|
? "Use the email from your checkout to create your account."
|
|
252
252
|
: "Email and password — no phone required."}
|
|
253
253
|
</p>
|
|
254
254
|
<form onSubmit={handleSignUp} className="space-y-4">
|
|
255
255
|
<div>
|
|
256
|
-
<label className="block text-xs font-medium text-
|
|
256
|
+
<label className="block text-xs font-medium text-sand-600 dark:text-sand-400 mb-1" htmlFor="su-email">
|
|
257
257
|
Email address
|
|
258
258
|
</label>
|
|
259
259
|
<input
|
|
@@ -264,12 +264,12 @@ export function CheckoutSuccessClient({
|
|
|
264
264
|
readOnly={emailLocked}
|
|
265
265
|
value={email}
|
|
266
266
|
onChange={(e) => setEmail(e.target.value)}
|
|
267
|
-
className="w-full rounded-lg border border-
|
|
267
|
+
className="w-full rounded-lg border border-sand-200 dark:border-sand-800 bg-sand-50 dark:bg-sand-950 px-3 py-2 text-sm text-sand-900 dark:text-sand-50 placeholder:text-sand-400 focus:border-[var(--brand-accent)] focus:outline-none"
|
|
268
268
|
placeholder="you@example.com"
|
|
269
269
|
/>
|
|
270
270
|
</div>
|
|
271
271
|
<div>
|
|
272
|
-
<label className="block text-xs font-medium text-
|
|
272
|
+
<label className="block text-xs font-medium text-sand-600 dark:text-sand-400 mb-1" htmlFor="su-password">
|
|
273
273
|
Password
|
|
274
274
|
</label>
|
|
275
275
|
<input
|
|
@@ -280,7 +280,7 @@ export function CheckoutSuccessClient({
|
|
|
280
280
|
minLength={8}
|
|
281
281
|
value={password}
|
|
282
282
|
onChange={(e) => setPassword(e.target.value)}
|
|
283
|
-
className="w-full rounded-lg border border-
|
|
283
|
+
className="w-full rounded-lg border border-sand-200 dark:border-sand-800 bg-sand-50 dark:bg-sand-950 px-3 py-2 text-sm text-sand-900 dark:text-sand-50 placeholder:text-sand-400 focus:border-[var(--brand-accent)] focus:outline-none"
|
|
284
284
|
placeholder="Min. 8 characters"
|
|
285
285
|
/>
|
|
286
286
|
</div>
|
|
@@ -303,15 +303,15 @@ export function CheckoutSuccessClient({
|
|
|
303
303
|
{/* Sign-in: credentials step */}
|
|
304
304
|
{step === "credentials" && mode === "signin" && (
|
|
305
305
|
<>
|
|
306
|
-
<h2 className="text-base font-semibold text-
|
|
307
|
-
<p className="text-xs text-
|
|
306
|
+
<h2 className="text-base font-semibold text-sand-900 dark:text-sand-50 mb-1">Sign in</h2>
|
|
307
|
+
<p className="text-xs text-sand-500 mb-5">
|
|
308
308
|
{emailLocked
|
|
309
309
|
? "Sign in with the email from your checkout to activate your eSIM."
|
|
310
310
|
: "Sign in to activate your eSIM."}
|
|
311
311
|
</p>
|
|
312
312
|
<form onSubmit={handleSignIn} className="space-y-4">
|
|
313
313
|
<div>
|
|
314
|
-
<label className="block text-xs font-medium text-
|
|
314
|
+
<label className="block text-xs font-medium text-sand-600 dark:text-sand-400 mb-1" htmlFor="si-email">
|
|
315
315
|
Email address
|
|
316
316
|
</label>
|
|
317
317
|
<input
|
|
@@ -322,12 +322,12 @@ export function CheckoutSuccessClient({
|
|
|
322
322
|
readOnly={emailLocked}
|
|
323
323
|
value={email}
|
|
324
324
|
onChange={(e) => setEmail(e.target.value)}
|
|
325
|
-
className="w-full rounded-lg border border-
|
|
325
|
+
className="w-full rounded-lg border border-sand-200 dark:border-sand-800 bg-sand-50 dark:bg-sand-950 px-3 py-2 text-sm text-sand-900 dark:text-sand-50 placeholder:text-sand-400 focus:border-[var(--brand-accent)] focus:outline-none"
|
|
326
326
|
placeholder="you@example.com"
|
|
327
327
|
/>
|
|
328
328
|
</div>
|
|
329
329
|
<div>
|
|
330
|
-
<label className="block text-xs font-medium text-
|
|
330
|
+
<label className="block text-xs font-medium text-sand-600 dark:text-sand-400 mb-1" htmlFor="si-password">
|
|
331
331
|
Password
|
|
332
332
|
</label>
|
|
333
333
|
<input
|
|
@@ -337,7 +337,7 @@ export function CheckoutSuccessClient({
|
|
|
337
337
|
required
|
|
338
338
|
value={password}
|
|
339
339
|
onChange={(e) => setPassword(e.target.value)}
|
|
340
|
-
className="w-full rounded-lg border border-
|
|
340
|
+
className="w-full rounded-lg border border-sand-200 dark:border-sand-800 bg-sand-50 dark:bg-sand-950 px-3 py-2 text-sm text-sand-900 dark:text-sand-50 placeholder:text-sand-400 focus:border-[var(--brand-accent)] focus:outline-none"
|
|
341
341
|
placeholder="Your password"
|
|
342
342
|
/>
|
|
343
343
|
</div>
|
|
@@ -360,13 +360,13 @@ export function CheckoutSuccessClient({
|
|
|
360
360
|
{/* Email verification step */}
|
|
361
361
|
{step === "verify" && (
|
|
362
362
|
<>
|
|
363
|
-
<h2 className="text-base font-semibold text-
|
|
364
|
-
<p className="text-xs text-
|
|
365
|
-
We sent a 6-digit code to <span className="text-
|
|
363
|
+
<h2 className="text-base font-semibold text-sand-900 dark:text-sand-50 mb-1">Check your email</h2>
|
|
364
|
+
<p className="text-xs text-sand-500 mb-5">
|
|
365
|
+
We sent a 6-digit code to <span className="text-sand-600 dark:text-sand-300">{email}</span>.
|
|
366
366
|
</p>
|
|
367
367
|
<form onSubmit={handleVerify} className="space-y-4">
|
|
368
368
|
<div>
|
|
369
|
-
<label className="block text-xs font-medium text-
|
|
369
|
+
<label className="block text-xs font-medium text-sand-600 dark:text-sand-400 mb-1" htmlFor="verify-code">
|
|
370
370
|
Verification code
|
|
371
371
|
</label>
|
|
372
372
|
<input
|
|
@@ -378,7 +378,7 @@ export function CheckoutSuccessClient({
|
|
|
378
378
|
maxLength={6}
|
|
379
379
|
value={code}
|
|
380
380
|
onChange={(e) => setCode(e.target.value.replace(/\D/g, ""))}
|
|
381
|
-
className="w-full rounded-lg border border-
|
|
381
|
+
className="w-full rounded-lg border border-sand-200 dark:border-sand-800 bg-sand-50 dark:bg-sand-950 px-3 py-2 text-center text-lg tracking-[0.4em] text-sand-900 dark:text-sand-50 placeholder:text-sand-400 focus:border-[var(--brand-accent)] focus:outline-none"
|
|
382
382
|
placeholder="000000"
|
|
383
383
|
/>
|
|
384
384
|
</div>
|
|
@@ -398,7 +398,7 @@ export function CheckoutSuccessClient({
|
|
|
398
398
|
<button
|
|
399
399
|
type="button"
|
|
400
400
|
onClick={() => { setStep("credentials"); setError(null); setCode(""); }}
|
|
401
|
-
className="mt-4 w-full text-center text-xs text-
|
|
401
|
+
className="mt-4 w-full text-center text-xs text-sand-400 hover:text-sand-500"
|
|
402
402
|
>
|
|
403
403
|
← Back
|
|
404
404
|
</button>
|
|
@@ -406,7 +406,7 @@ export function CheckoutSuccessClient({
|
|
|
406
406
|
)}
|
|
407
407
|
</div>
|
|
408
408
|
|
|
409
|
-
<p className="text-center text-xs text-
|
|
409
|
+
<p className="text-center text-xs text-sand-400">
|
|
410
410
|
Your payment is confirmed. Account setup is required to receive your eSIM QR code.
|
|
411
411
|
</p>
|
|
412
412
|
</div>
|
|
@@ -43,7 +43,7 @@ export default async function CheckoutSuccessPage({ searchParams }: Props) {
|
|
|
43
43
|
<div className="flex min-h-[calc(100vh-4rem)] items-center justify-center px-4">
|
|
44
44
|
<div className="text-center space-y-3">
|
|
45
45
|
<div className="mx-auto size-8 animate-spin rounded-full border-2 border-[var(--brand-accent)] border-t-transparent" />
|
|
46
|
-
<p className="text-sm text-
|
|
46
|
+
<p className="text-sm text-sand-600 dark:text-sand-400">Loading…</p>
|
|
47
47
|
</div>
|
|
48
48
|
</div>
|
|
49
49
|
}
|
|
@@ -1,34 +1,42 @@
|
|
|
1
|
+
import Link from "next/link";
|
|
1
2
|
import brand from "@/brand.config";
|
|
3
|
+
import { buildSupportLink } from "@/vendor/ui/brand";
|
|
2
4
|
|
|
3
5
|
export const metadata = { title: "Contact" };
|
|
4
6
|
|
|
5
7
|
export default function ContactPage() {
|
|
6
8
|
return (
|
|
7
|
-
<div className="mx-auto max-w-2xl px-4 py-
|
|
8
|
-
<h1 className="text-3xl font-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
9
|
+
<div className="mx-auto max-w-2xl px-4 py-16">
|
|
10
|
+
<h1 className="text-3xl font-semibold tracking-tight text-sand-900 dark:text-sand-50">
|
|
11
|
+
Get in touch
|
|
12
|
+
</h1>
|
|
13
|
+
<p className="mt-2 mb-10 text-sand-600 dark:text-sand-400">
|
|
14
|
+
Real humans. 24/7. Pick the channel you already have open.
|
|
15
|
+
</p>
|
|
16
|
+
<div className="space-y-3">
|
|
17
|
+
<Link
|
|
18
|
+
href={buildSupportLink("whatsapp", { message: `Hi ${brand.name}!` })}
|
|
19
|
+
target="_blank"
|
|
20
|
+
rel="noopener noreferrer"
|
|
21
|
+
className="flex items-center justify-between rounded-2xl border border-sand-200 bg-white px-5 py-4 text-sand-900 dark:border-sand-800 dark:bg-sand-900 dark:text-sand-50"
|
|
22
|
+
>
|
|
23
|
+
<span className="font-medium">WhatsApp</span>
|
|
24
|
+
<span className="text-sm text-sand-500">24/7</span>
|
|
25
|
+
</Link>
|
|
26
|
+
<Link
|
|
27
|
+
href={buildSupportLink("sms")}
|
|
28
|
+
className="flex items-center justify-between rounded-2xl border border-sand-200 bg-white px-5 py-4 text-sand-900 dark:border-sand-800 dark:bg-sand-900 dark:text-sand-50"
|
|
29
|
+
>
|
|
30
|
+
<span className="font-medium">SMS</span>
|
|
31
|
+
<span className="text-sm text-sand-500">{brand.supportWhatsapp}</span>
|
|
32
|
+
</Link>
|
|
33
|
+
<a
|
|
34
|
+
href={`mailto:${brand.supportEmail}`}
|
|
35
|
+
className="flex items-center justify-between rounded-2xl border border-sand-200 bg-white px-5 py-4 text-sand-900 dark:border-sand-800 dark:bg-sand-900 dark:text-sand-50"
|
|
36
|
+
>
|
|
37
|
+
<span className="font-medium">Email</span>
|
|
38
|
+
<span className="text-sm text-sand-500">{brand.supportEmail}</span>
|
|
39
|
+
</a>
|
|
32
40
|
</div>
|
|
33
41
|
</div>
|
|
34
42
|
);
|
|
@@ -11,12 +11,15 @@ export default async function DashboardPage() {
|
|
|
11
11
|
|
|
12
12
|
return (
|
|
13
13
|
<div className="mx-auto max-w-4xl px-4 py-16">
|
|
14
|
-
<h1 className="text-3xl font-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
<h1 className="mb-8 text-3xl font-semibold tracking-tight text-sand-900 dark:text-sand-50">
|
|
15
|
+
Your eSIMs
|
|
16
|
+
</h1>
|
|
17
|
+
<div className="rounded-2xl border border-sand-200 bg-white p-12 text-center dark:border-sand-800 dark:bg-sand-900">
|
|
18
|
+
<p className="text-sand-500">No active eSIMs yet.</p>
|
|
17
19
|
<a
|
|
18
20
|
href="/shop"
|
|
19
|
-
className="mt-4 inline-block rounded-full
|
|
21
|
+
className="mt-4 inline-block rounded-full px-6 py-2 text-sm font-semibold text-white transition-opacity hover:opacity-90"
|
|
22
|
+
style={{ background: "var(--brand-accent)" }}
|
|
20
23
|
>
|
|
21
24
|
Browse plans
|
|
22
25
|
</a>
|
|
@@ -1,99 +1,89 @@
|
|
|
1
1
|
@import "tailwindcss";
|
|
2
2
|
|
|
3
|
-
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
|
|
3
|
+
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
|
|
4
4
|
|
|
5
5
|
@theme {
|
|
6
|
-
|
|
7
|
-
--color-
|
|
8
|
-
--color-
|
|
9
|
-
--color-
|
|
10
|
-
--color-brand-accent-light: var(--brand-accent-light);
|
|
11
|
-
--color-brand-text: var(--brand-text);
|
|
12
|
-
--color-brand-text-secondary: var(--brand-text-secondary);
|
|
6
|
+
--color-leaf-50: #F0FDF4;
|
|
7
|
+
--color-leaf-500: #22C55E;
|
|
8
|
+
--color-leaf-600: #16A34A;
|
|
9
|
+
--color-leaf-700: #15803D;
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
--color-
|
|
16
|
-
--color-
|
|
17
|
-
--color-
|
|
18
|
-
--color-
|
|
19
|
-
--color-
|
|
20
|
-
--color-
|
|
21
|
-
--color-
|
|
22
|
-
--color-
|
|
23
|
-
--color-
|
|
24
|
-
--color-mango-900: #5e1e0c;
|
|
25
|
-
--color-mango-950: #3a1107;
|
|
11
|
+
--color-sand-50: #FAFAF7;
|
|
12
|
+
--color-sand-100: #F4F2EC;
|
|
13
|
+
--color-sand-200: #E8E4D8;
|
|
14
|
+
--color-sand-400: #A8A28E;
|
|
15
|
+
--color-sand-500: #78725F;
|
|
16
|
+
--color-sand-600: #5C5644;
|
|
17
|
+
--color-sand-700: #3F3B30;
|
|
18
|
+
--color-sand-800: #2C291F;
|
|
19
|
+
--color-sand-900: #1A1812;
|
|
20
|
+
--color-sand-950: #0F0E0A;
|
|
26
21
|
|
|
27
|
-
|
|
28
|
-
--
|
|
22
|
+
--font-sans: var(--font-geist-sans, "Geist", "Inter", system-ui, sans-serif);
|
|
23
|
+
--font-display: var(--font-geist-sans, "Geist", system-ui, sans-serif);
|
|
24
|
+
--font-mono: var(--font-geist-mono, ui-monospace, monospace);
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
--
|
|
32
|
-
--
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
--radius-sm: 0.25rem;
|
|
36
|
-
--radius-md: 0.5rem;
|
|
37
|
-
--radius-lg: 0.75rem;
|
|
38
|
-
--radius-xl: 1rem;
|
|
39
|
-
--radius-2xl: 1.5rem;
|
|
26
|
+
--radius-sm: 6px;
|
|
27
|
+
--radius-md: 10px;
|
|
28
|
+
--radius-lg: 14px;
|
|
29
|
+
--radius-xl: 20px;
|
|
30
|
+
--radius-2xl: 28px;
|
|
40
31
|
--radius-full: 9999px;
|
|
41
32
|
}
|
|
42
33
|
|
|
43
|
-
|
|
44
|
-
:root {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
--brand-text-secondary: #C9CCD6;
|
|
51
|
-
}
|
|
34
|
+
@layer base {
|
|
35
|
+
:root {
|
|
36
|
+
color-scheme: light;
|
|
37
|
+
--brand-accent: #FF6B35;
|
|
38
|
+
--brand-accent-dark: #D9461C;
|
|
39
|
+
--brand-accent-light: #FFB088;
|
|
40
|
+
}
|
|
52
41
|
|
|
53
|
-
[data-theme="
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
--brand-text-secondary: #6B7280;
|
|
57
|
-
}
|
|
42
|
+
html[data-theme="dark"] {
|
|
43
|
+
color-scheme: dark;
|
|
44
|
+
}
|
|
58
45
|
|
|
59
|
-
|
|
60
|
-
*,
|
|
61
|
-
*::before,
|
|
62
|
-
*::after {
|
|
63
|
-
box-sizing: border-box;
|
|
64
|
-
margin: 0;
|
|
65
|
-
padding: 0;
|
|
66
|
-
}
|
|
46
|
+
* { box-sizing: border-box; }
|
|
67
47
|
|
|
68
|
-
html {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
48
|
+
html {
|
|
49
|
+
scroll-behavior: smooth;
|
|
50
|
+
-webkit-font-smoothing: antialiased;
|
|
51
|
+
-moz-osx-font-smoothing: grayscale;
|
|
52
|
+
background-color: var(--color-sand-50);
|
|
53
|
+
scrollbar-gutter: stable;
|
|
54
|
+
}
|
|
72
55
|
|
|
73
|
-
body {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
56
|
+
body {
|
|
57
|
+
background-color: var(--color-sand-50);
|
|
58
|
+
color: var(--color-sand-900);
|
|
59
|
+
font-family: var(--font-sans);
|
|
60
|
+
text-rendering: optimizeLegibility;
|
|
61
|
+
transition: background-color 0.3s ease, color 0.3s ease;
|
|
62
|
+
}
|
|
81
63
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
color: var(--brand-text);
|
|
86
|
-
}
|
|
64
|
+
html[data-theme="dark"] {
|
|
65
|
+
background-color: var(--color-sand-950);
|
|
66
|
+
}
|
|
87
67
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
68
|
+
html[data-theme="dark"] body {
|
|
69
|
+
background-color: var(--color-sand-950);
|
|
70
|
+
color: var(--color-sand-50);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
::selection {
|
|
74
|
+
background-color: color-mix(in srgb, var(--brand-accent) 28%, transparent);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
:focus-visible {
|
|
78
|
+
outline: 2px solid var(--brand-accent);
|
|
79
|
+
outline-offset: 2px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@media (prefers-reduced-motion: reduce) {
|
|
83
|
+
*, *::before, *::after {
|
|
84
|
+
animation-duration: 0.01ms !important;
|
|
85
|
+
animation-iteration-count: 1 !important;
|
|
86
|
+
transition-duration: 0.01ms !important;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
99
89
|
}
|