@doswiftly/cli 0.1.23 → 0.2.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.
- package/dist/commands/check.js +2 -2
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +34 -7
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts +13 -0
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +155 -63
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +3 -4
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +271 -166
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/sdk.d.ts +1 -1
- package/dist/commands/sdk.js +3 -3
- package/dist/commands/sdk.js.map +1 -1
- package/dist/commands/template.d.ts.map +1 -1
- package/dist/commands/template.js +4 -31
- package/dist/commands/template.js.map +1 -1
- package/dist/commands/verify.js +5 -5
- package/dist/commands/verify.js.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/i18n.d.ts +12 -0
- package/dist/lib/i18n.d.ts.map +1 -1
- package/dist/lib/i18n.js +24 -0
- package/dist/lib/i18n.js.map +1 -1
- package/dist/lib/proxy-server.d.ts +22 -6
- package/dist/lib/proxy-server.d.ts.map +1 -1
- package/dist/lib/proxy-server.js +174 -75
- package/dist/lib/proxy-server.js.map +1 -1
- package/package.json +1 -1
- package/dist/commands/types.d.ts +0 -5
- package/dist/commands/types.d.ts.map +0 -1
- package/dist/commands/types.js +0 -82
- package/dist/commands/types.js.map +0 -1
- package/templates/storefront-minimal/.env.example +0 -10
- package/templates/storefront-minimal/.github/workflows/build-template.yml +0 -119
- package/templates/storefront-minimal/app/globals.css +0 -18
- package/templates/storefront-minimal/app/layout.tsx +0 -26
- package/templates/storefront-minimal/app/page.tsx +0 -93
- package/templates/storefront-minimal/lib/graphql-client.ts +0 -23
- package/templates/storefront-minimal/next.config.ts +0 -15
- package/templates/storefront-minimal/open-next.config.ts +0 -3
- package/templates/storefront-minimal/package.json +0 -30
- package/templates/storefront-minimal/postcss.config.mjs +0 -5
- package/templates/storefront-minimal/tailwind.config.ts +0 -14
- package/templates/storefront-minimal/tsconfig.json +0 -27
- package/templates/storefront-minimal/wrangler.toml +0 -24
- package/templates/storefront-nextjs/.env.example +0 -68
- package/templates/storefront-nextjs/.github/workflows/build-template.yml +0 -119
- package/templates/storefront-nextjs/README.md +0 -524
- package/templates/storefront-nextjs/app/account/orders/page.tsx +0 -216
- package/templates/storefront-nextjs/app/account/page.tsx +0 -167
- package/templates/storefront-nextjs/app/auth/login/page.tsx +0 -135
- package/templates/storefront-nextjs/app/auth/register/page.tsx +0 -212
- package/templates/storefront-nextjs/app/cart/page.tsx +0 -263
- package/templates/storefront-nextjs/app/categories/[slug]/page.tsx +0 -200
- package/templates/storefront-nextjs/app/categories/page.tsx +0 -58
- package/templates/storefront-nextjs/app/checkout/page.tsx +0 -351
- package/templates/storefront-nextjs/app/collections/[slug]/page.tsx +0 -158
- package/templates/storefront-nextjs/app/collections/page.tsx +0 -61
- package/templates/storefront-nextjs/app/globals.css +0 -98
- package/templates/storefront-nextjs/app/layout.tsx +0 -39
- package/templates/storefront-nextjs/app/page.tsx +0 -136
- package/templates/storefront-nextjs/app/products/[slug]/page.tsx +0 -119
- package/templates/storefront-nextjs/app/products/page.tsx +0 -107
- package/templates/storefront-nextjs/app/search/page.tsx +0 -127
- package/templates/storefront-nextjs/components/auth/auth-guard.tsx +0 -94
- package/templates/storefront-nextjs/components/commerce/add-to-cart-button.tsx +0 -77
- package/templates/storefront-nextjs/components/commerce/cart-icon.tsx +0 -29
- package/templates/storefront-nextjs/components/commerce/currency-selector.tsx +0 -217
- package/templates/storefront-nextjs/components/commerce/pagination.tsx +0 -62
- package/templates/storefront-nextjs/components/commerce/product-actions.tsx +0 -135
- package/templates/storefront-nextjs/components/commerce/product-filters.tsx +0 -109
- package/templates/storefront-nextjs/components/commerce/product-price.tsx +0 -375
- package/templates/storefront-nextjs/components/commerce/search-input.tsx +0 -178
- package/templates/storefront-nextjs/components/commerce/sort-select.tsx +0 -64
- package/templates/storefront-nextjs/components/commerce/variant-selector.tsx +0 -210
- package/templates/storefront-nextjs/components/layout/footer.tsx +0 -107
- package/templates/storefront-nextjs/components/layout/header.tsx +0 -104
- package/templates/storefront-nextjs/components/providers.tsx +0 -62
- package/templates/storefront-nextjs/lib/auth/routes.ts +0 -52
- package/templates/storefront-nextjs/lib/currency.tsx +0 -140
- package/templates/storefront-nextjs/lib/format.ts +0 -159
- package/templates/storefront-nextjs/lib/graphql-queries.ts +0 -629
- package/templates/storefront-nextjs/lib/hooks.ts +0 -30
- package/templates/storefront-nextjs/middleware.ts +0 -80
- package/templates/storefront-nextjs/next.config.ts +0 -37
- package/templates/storefront-nextjs/open-next.config.ts +0 -3
- package/templates/storefront-nextjs/package.dev.json +0 -30
- package/templates/storefront-nextjs/package.json +0 -32
- package/templates/storefront-nextjs/package.json.template +0 -32
- package/templates/storefront-nextjs/postcss.config.mjs +0 -8
- package/templates/storefront-nextjs/tailwind.config.ts +0 -111
- package/templates/storefront-nextjs/tsconfig.json +0 -27
- package/templates/storefront-nextjs/wrangler.toml +0 -24
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import Link from "next/link";
|
|
2
|
-
import { useCategories } from "@doswiftly/storefront-sdk/graphql/server";
|
|
3
|
-
|
|
4
|
-
export default async function CategoriesPage() {
|
|
5
|
-
// Fetch all root categories
|
|
6
|
-
const { categories } = await useCategories();
|
|
7
|
-
|
|
8
|
-
return (
|
|
9
|
-
<div className="container mx-auto px-4 py-8">
|
|
10
|
-
{/* Page Header */}
|
|
11
|
-
<div className="mb-8">
|
|
12
|
-
<h1 className="text-3xl font-bold text-gray-900">Categories</h1>
|
|
13
|
-
<p className="mt-2 text-gray-600">Browse products by category</p>
|
|
14
|
-
</div>
|
|
15
|
-
|
|
16
|
-
{/* Categories Grid */}
|
|
17
|
-
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4 lg:gap-6">
|
|
18
|
-
{categories.length === 0 ? (
|
|
19
|
-
<div className="col-span-full py-12 text-center text-gray-500">
|
|
20
|
-
No categories found
|
|
21
|
-
</div>
|
|
22
|
-
) : (
|
|
23
|
-
categories.map((category) => (
|
|
24
|
-
<Link
|
|
25
|
-
key={category.id}
|
|
26
|
-
href={`/categories/${category.slug}`}
|
|
27
|
-
className="group relative overflow-hidden rounded-lg"
|
|
28
|
-
>
|
|
29
|
-
<div className="aspect-square bg-gray-100">
|
|
30
|
-
{category.image?.url ? (
|
|
31
|
-
<img
|
|
32
|
-
src={category.image.url}
|
|
33
|
-
alt={category.image.altText || category.name}
|
|
34
|
-
className="h-full w-full object-cover transition group-hover:scale-105"
|
|
35
|
-
/>
|
|
36
|
-
) : (
|
|
37
|
-
<div className="flex h-full items-center justify-center bg-gradient-to-br from-gray-100 to-gray-200">
|
|
38
|
-
<span className="text-4xl text-gray-400">🏷️</span>
|
|
39
|
-
</div>
|
|
40
|
-
)}
|
|
41
|
-
</div>
|
|
42
|
-
<div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent" />
|
|
43
|
-
<div className="absolute bottom-0 left-0 right-0 p-4">
|
|
44
|
-
<h2 className="font-bold text-white">{category.name}</h2>
|
|
45
|
-
{category.productCount > 0 && (
|
|
46
|
-
<p className="text-sm text-white/80">
|
|
47
|
-
{category.productCount}{" "}
|
|
48
|
-
{category.productCount === 1 ? "product" : "products"}
|
|
49
|
-
</p>
|
|
50
|
-
)}
|
|
51
|
-
</div>
|
|
52
|
-
</Link>
|
|
53
|
-
))
|
|
54
|
-
)}
|
|
55
|
-
</div>
|
|
56
|
-
</div>
|
|
57
|
-
);
|
|
58
|
-
}
|
|
@@ -1,351 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useState } from 'react';
|
|
4
|
-
import Link from 'next/link';
|
|
5
|
-
|
|
6
|
-
interface CheckoutForm {
|
|
7
|
-
email: string;
|
|
8
|
-
firstName: string;
|
|
9
|
-
lastName: string;
|
|
10
|
-
address: string;
|
|
11
|
-
city: string;
|
|
12
|
-
postalCode: string;
|
|
13
|
-
country: string;
|
|
14
|
-
phone: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export default function CheckoutPage() {
|
|
18
|
-
const [step, setStep] = useState<'shipping' | 'payment' | 'confirmation'>('shipping');
|
|
19
|
-
const [form, setForm] = useState<CheckoutForm>({
|
|
20
|
-
email: '',
|
|
21
|
-
firstName: '',
|
|
22
|
-
lastName: '',
|
|
23
|
-
address: '',
|
|
24
|
-
city: '',
|
|
25
|
-
postalCode: '',
|
|
26
|
-
country: 'PL',
|
|
27
|
-
phone: '',
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
|
|
31
|
-
const { name, value } = e.target;
|
|
32
|
-
setForm((prev) => ({ ...prev, [name]: value }));
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const handleSubmit = async (e: React.FormEvent) => {
|
|
36
|
-
e.preventDefault();
|
|
37
|
-
if (step === 'shipping') {
|
|
38
|
-
setStep('payment');
|
|
39
|
-
} else if (step === 'payment') {
|
|
40
|
-
// Process payment
|
|
41
|
-
setStep('confirmation');
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
// Order summary (placeholder)
|
|
46
|
-
const orderSummary = {
|
|
47
|
-
subtotal: 9999,
|
|
48
|
-
shipping: 999,
|
|
49
|
-
total: 10998,
|
|
50
|
-
items: 1,
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
if (step === 'confirmation') {
|
|
54
|
-
return (
|
|
55
|
-
<div className="container mx-auto px-4 py-16 text-center">
|
|
56
|
-
<div className="mx-auto max-w-md">
|
|
57
|
-
<div className="mb-6 text-6xl">✅</div>
|
|
58
|
-
<h1 className="mb-4 text-3xl font-bold text-gray-900">
|
|
59
|
-
Order Confirmed!
|
|
60
|
-
</h1>
|
|
61
|
-
<p className="mb-8 text-gray-600">
|
|
62
|
-
Thank you for your order. We've sent a confirmation email to{' '}
|
|
63
|
-
<strong>{form.email}</strong>.
|
|
64
|
-
</p>
|
|
65
|
-
<p className="mb-8 text-sm text-gray-500">
|
|
66
|
-
Order #: ORD-{Date.now().toString(36).toUpperCase()}
|
|
67
|
-
</p>
|
|
68
|
-
<Link href="/products" className="btn btn-primary">
|
|
69
|
-
Continue Shopping
|
|
70
|
-
</Link>
|
|
71
|
-
</div>
|
|
72
|
-
</div>
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return (
|
|
77
|
-
<div className="container mx-auto px-4 py-8">
|
|
78
|
-
<h1 className="mb-8 text-3xl font-bold text-gray-900">Checkout</h1>
|
|
79
|
-
|
|
80
|
-
{/* Progress Steps */}
|
|
81
|
-
<div className="mb-8 flex items-center justify-center gap-4">
|
|
82
|
-
<div className={`flex items-center gap-2 ${step === 'shipping' ? 'text-primary' : 'text-gray-400'}`}>
|
|
83
|
-
<span className="flex h-8 w-8 items-center justify-center rounded-full border-2 border-current">
|
|
84
|
-
1
|
|
85
|
-
</span>
|
|
86
|
-
<span className="font-medium">Shipping</span>
|
|
87
|
-
</div>
|
|
88
|
-
<div className="h-px w-12 bg-gray-300" />
|
|
89
|
-
<div className={`flex items-center gap-2 ${step === 'payment' ? 'text-primary' : 'text-gray-400'}`}>
|
|
90
|
-
<span className="flex h-8 w-8 items-center justify-center rounded-full border-2 border-current">
|
|
91
|
-
2
|
|
92
|
-
</span>
|
|
93
|
-
<span className="font-medium">Payment</span>
|
|
94
|
-
</div>
|
|
95
|
-
</div>
|
|
96
|
-
|
|
97
|
-
<div className="grid gap-8 lg:grid-cols-3">
|
|
98
|
-
{/* Form */}
|
|
99
|
-
<div className="lg:col-span-2">
|
|
100
|
-
<form onSubmit={handleSubmit} className="card">
|
|
101
|
-
{step === 'shipping' && (
|
|
102
|
-
<>
|
|
103
|
-
<h2 className="mb-6 text-lg font-semibold text-gray-900">
|
|
104
|
-
Shipping Information
|
|
105
|
-
</h2>
|
|
106
|
-
|
|
107
|
-
<div className="space-y-4">
|
|
108
|
-
<div>
|
|
109
|
-
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
110
|
-
Email
|
|
111
|
-
</label>
|
|
112
|
-
<input
|
|
113
|
-
type="email"
|
|
114
|
-
name="email"
|
|
115
|
-
value={form.email}
|
|
116
|
-
onChange={handleInputChange}
|
|
117
|
-
className="input"
|
|
118
|
-
required
|
|
119
|
-
/>
|
|
120
|
-
</div>
|
|
121
|
-
|
|
122
|
-
<div className="grid gap-4 sm:grid-cols-2">
|
|
123
|
-
<div>
|
|
124
|
-
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
125
|
-
First Name
|
|
126
|
-
</label>
|
|
127
|
-
<input
|
|
128
|
-
type="text"
|
|
129
|
-
name="firstName"
|
|
130
|
-
value={form.firstName}
|
|
131
|
-
onChange={handleInputChange}
|
|
132
|
-
className="input"
|
|
133
|
-
required
|
|
134
|
-
/>
|
|
135
|
-
</div>
|
|
136
|
-
<div>
|
|
137
|
-
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
138
|
-
Last Name
|
|
139
|
-
</label>
|
|
140
|
-
<input
|
|
141
|
-
type="text"
|
|
142
|
-
name="lastName"
|
|
143
|
-
value={form.lastName}
|
|
144
|
-
onChange={handleInputChange}
|
|
145
|
-
className="input"
|
|
146
|
-
required
|
|
147
|
-
/>
|
|
148
|
-
</div>
|
|
149
|
-
</div>
|
|
150
|
-
|
|
151
|
-
<div>
|
|
152
|
-
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
153
|
-
Address
|
|
154
|
-
</label>
|
|
155
|
-
<input
|
|
156
|
-
type="text"
|
|
157
|
-
name="address"
|
|
158
|
-
value={form.address}
|
|
159
|
-
onChange={handleInputChange}
|
|
160
|
-
className="input"
|
|
161
|
-
required
|
|
162
|
-
/>
|
|
163
|
-
</div>
|
|
164
|
-
|
|
165
|
-
<div className="grid gap-4 sm:grid-cols-3">
|
|
166
|
-
<div>
|
|
167
|
-
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
168
|
-
City
|
|
169
|
-
</label>
|
|
170
|
-
<input
|
|
171
|
-
type="text"
|
|
172
|
-
name="city"
|
|
173
|
-
value={form.city}
|
|
174
|
-
onChange={handleInputChange}
|
|
175
|
-
className="input"
|
|
176
|
-
required
|
|
177
|
-
/>
|
|
178
|
-
</div>
|
|
179
|
-
<div>
|
|
180
|
-
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
181
|
-
Postal Code
|
|
182
|
-
</label>
|
|
183
|
-
<input
|
|
184
|
-
type="text"
|
|
185
|
-
name="postalCode"
|
|
186
|
-
value={form.postalCode}
|
|
187
|
-
onChange={handleInputChange}
|
|
188
|
-
className="input"
|
|
189
|
-
required
|
|
190
|
-
/>
|
|
191
|
-
</div>
|
|
192
|
-
<div>
|
|
193
|
-
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
194
|
-
Country
|
|
195
|
-
</label>
|
|
196
|
-
<select
|
|
197
|
-
name="country"
|
|
198
|
-
value={form.country}
|
|
199
|
-
onChange={handleInputChange}
|
|
200
|
-
className="input"
|
|
201
|
-
required
|
|
202
|
-
>
|
|
203
|
-
<option value="PL">Poland</option>
|
|
204
|
-
<option value="DE">Germany</option>
|
|
205
|
-
<option value="GB">United Kingdom</option>
|
|
206
|
-
<option value="US">United States</option>
|
|
207
|
-
</select>
|
|
208
|
-
</div>
|
|
209
|
-
</div>
|
|
210
|
-
|
|
211
|
-
<div>
|
|
212
|
-
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
213
|
-
Phone
|
|
214
|
-
</label>
|
|
215
|
-
<input
|
|
216
|
-
type="tel"
|
|
217
|
-
name="phone"
|
|
218
|
-
value={form.phone}
|
|
219
|
-
onChange={handleInputChange}
|
|
220
|
-
className="input"
|
|
221
|
-
/>
|
|
222
|
-
</div>
|
|
223
|
-
</div>
|
|
224
|
-
|
|
225
|
-
<button type="submit" className="btn btn-primary mt-6 w-full">
|
|
226
|
-
Continue to Payment
|
|
227
|
-
</button>
|
|
228
|
-
</>
|
|
229
|
-
)}
|
|
230
|
-
|
|
231
|
-
{step === 'payment' && (
|
|
232
|
-
<>
|
|
233
|
-
<h2 className="mb-6 text-lg font-semibold text-gray-900">
|
|
234
|
-
Payment Method
|
|
235
|
-
</h2>
|
|
236
|
-
|
|
237
|
-
<div className="space-y-4">
|
|
238
|
-
<div className="rounded-lg border border-primary bg-primary/5 p-4">
|
|
239
|
-
<label className="flex items-center gap-3">
|
|
240
|
-
<input
|
|
241
|
-
type="radio"
|
|
242
|
-
name="paymentMethod"
|
|
243
|
-
value="card"
|
|
244
|
-
defaultChecked
|
|
245
|
-
className="h-4 w-4 text-primary"
|
|
246
|
-
/>
|
|
247
|
-
<span className="font-medium">Credit/Debit Card</span>
|
|
248
|
-
</label>
|
|
249
|
-
<div className="mt-4 space-y-4 pl-7">
|
|
250
|
-
<div>
|
|
251
|
-
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
252
|
-
Card Number
|
|
253
|
-
</label>
|
|
254
|
-
<input
|
|
255
|
-
type="text"
|
|
256
|
-
placeholder="1234 5678 9012 3456"
|
|
257
|
-
className="input"
|
|
258
|
-
/>
|
|
259
|
-
</div>
|
|
260
|
-
<div className="grid gap-4 sm:grid-cols-2">
|
|
261
|
-
<div>
|
|
262
|
-
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
263
|
-
Expiry Date
|
|
264
|
-
</label>
|
|
265
|
-
<input
|
|
266
|
-
type="text"
|
|
267
|
-
placeholder="MM/YY"
|
|
268
|
-
className="input"
|
|
269
|
-
/>
|
|
270
|
-
</div>
|
|
271
|
-
<div>
|
|
272
|
-
<label className="mb-1 block text-sm font-medium text-gray-700">
|
|
273
|
-
CVV
|
|
274
|
-
</label>
|
|
275
|
-
<input
|
|
276
|
-
type="text"
|
|
277
|
-
placeholder="123"
|
|
278
|
-
className="input"
|
|
279
|
-
/>
|
|
280
|
-
</div>
|
|
281
|
-
</div>
|
|
282
|
-
</div>
|
|
283
|
-
</div>
|
|
284
|
-
|
|
285
|
-
<div className="rounded-lg border border-gray-200 p-4">
|
|
286
|
-
<label className="flex items-center gap-3">
|
|
287
|
-
<input
|
|
288
|
-
type="radio"
|
|
289
|
-
name="paymentMethod"
|
|
290
|
-
value="blik"
|
|
291
|
-
className="h-4 w-4 text-primary"
|
|
292
|
-
/>
|
|
293
|
-
<span className="font-medium">BLIK</span>
|
|
294
|
-
</label>
|
|
295
|
-
</div>
|
|
296
|
-
</div>
|
|
297
|
-
|
|
298
|
-
<div className="mt-6 flex gap-4">
|
|
299
|
-
<button
|
|
300
|
-
type="button"
|
|
301
|
-
onClick={() => setStep('shipping')}
|
|
302
|
-
className="btn btn-outline flex-1"
|
|
303
|
-
>
|
|
304
|
-
Back
|
|
305
|
-
</button>
|
|
306
|
-
<button type="submit" className="btn btn-primary flex-1">
|
|
307
|
-
Place Order
|
|
308
|
-
</button>
|
|
309
|
-
</div>
|
|
310
|
-
</>
|
|
311
|
-
)}
|
|
312
|
-
</form>
|
|
313
|
-
</div>
|
|
314
|
-
|
|
315
|
-
{/* Order Summary */}
|
|
316
|
-
<div className="lg:col-span-1">
|
|
317
|
-
<div className="card sticky top-24">
|
|
318
|
-
<h2 className="mb-4 text-lg font-semibold text-gray-900">
|
|
319
|
-
Order Summary
|
|
320
|
-
</h2>
|
|
321
|
-
|
|
322
|
-
<div className="space-y-3 text-sm">
|
|
323
|
-
<div className="flex justify-between">
|
|
324
|
-
<span className="text-gray-600">
|
|
325
|
-
Items ({orderSummary.items})
|
|
326
|
-
</span>
|
|
327
|
-
<span className="text-gray-900">
|
|
328
|
-
${(orderSummary.subtotal / 100).toFixed(2)}
|
|
329
|
-
</span>
|
|
330
|
-
</div>
|
|
331
|
-
<div className="flex justify-between">
|
|
332
|
-
<span className="text-gray-600">Shipping</span>
|
|
333
|
-
<span className="text-gray-900">
|
|
334
|
-
${(orderSummary.shipping / 100).toFixed(2)}
|
|
335
|
-
</span>
|
|
336
|
-
</div>
|
|
337
|
-
<div className="border-t border-gray-200 pt-3">
|
|
338
|
-
<div className="flex justify-between font-semibold">
|
|
339
|
-
<span className="text-gray-900">Total</span>
|
|
340
|
-
<span className="text-primary">
|
|
341
|
-
${(orderSummary.total / 100).toFixed(2)}
|
|
342
|
-
</span>
|
|
343
|
-
</div>
|
|
344
|
-
</div>
|
|
345
|
-
</div>
|
|
346
|
-
</div>
|
|
347
|
-
</div>
|
|
348
|
-
</div>
|
|
349
|
-
</div>
|
|
350
|
-
);
|
|
351
|
-
}
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import Link from "next/link";
|
|
2
|
-
import { notFound } from "next/navigation";
|
|
3
|
-
import {
|
|
4
|
-
useCollection,
|
|
5
|
-
useProducts,
|
|
6
|
-
} from "@doswiftly/storefront-sdk/graphql/server";
|
|
7
|
-
import { Pagination } from "@/components/commerce/pagination";
|
|
8
|
-
|
|
9
|
-
interface CollectionPageProps {
|
|
10
|
-
params: Promise<{ slug: string }>;
|
|
11
|
-
searchParams: Promise<{ after?: string; before?: string }>;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export default async function CollectionPage({
|
|
15
|
-
params,
|
|
16
|
-
searchParams,
|
|
17
|
-
}: CollectionPageProps) {
|
|
18
|
-
const { slug } = await params;
|
|
19
|
-
const { after, before } = await searchParams;
|
|
20
|
-
|
|
21
|
-
// Fetch collection details using handle
|
|
22
|
-
const collectionData = await useCollection({ handle: slug });
|
|
23
|
-
const collection = collectionData.collection;
|
|
24
|
-
|
|
25
|
-
if (!collection) {
|
|
26
|
-
notFound();
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Fetch products for this collection using query filter
|
|
30
|
-
const { products, pageInfo } = await useProducts({
|
|
31
|
-
first: 12,
|
|
32
|
-
after,
|
|
33
|
-
query: `collection:${collection.handle}`, // Filter by collection handle
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
return (
|
|
37
|
-
<div className="container mx-auto px-4 py-8">
|
|
38
|
-
{/* Breadcrumb */}
|
|
39
|
-
<nav className="mb-4 text-sm">
|
|
40
|
-
<ol className="flex items-center gap-2">
|
|
41
|
-
<li>
|
|
42
|
-
<Link href="/" className="text-gray-500 hover:text-gray-700">
|
|
43
|
-
Home
|
|
44
|
-
</Link>
|
|
45
|
-
</li>
|
|
46
|
-
<li className="text-gray-400">/</li>
|
|
47
|
-
<li>
|
|
48
|
-
<Link
|
|
49
|
-
href="/collections"
|
|
50
|
-
className="text-gray-500 hover:text-gray-700"
|
|
51
|
-
>
|
|
52
|
-
Collections
|
|
53
|
-
</Link>
|
|
54
|
-
</li>
|
|
55
|
-
<li className="text-gray-400">/</li>
|
|
56
|
-
<li className="text-gray-900">{collection.title}</li>
|
|
57
|
-
</ol>
|
|
58
|
-
</nav>
|
|
59
|
-
|
|
60
|
-
{/* Collection Header */}
|
|
61
|
-
<div className="mb-8">
|
|
62
|
-
{collection.image?.url && (
|
|
63
|
-
<div className="relative mb-6 aspect-[21/9] overflow-hidden rounded-lg">
|
|
64
|
-
<img
|
|
65
|
-
src={collection.image.url}
|
|
66
|
-
alt={collection.image.altText || collection.title}
|
|
67
|
-
className="h-full w-full object-cover"
|
|
68
|
-
/>
|
|
69
|
-
<div className="absolute inset-0 bg-black/30" />
|
|
70
|
-
<div className="absolute inset-0 flex flex-col items-center justify-center text-white">
|
|
71
|
-
<h1 className="text-4xl font-bold">{collection.title}</h1>
|
|
72
|
-
{collection.description && (
|
|
73
|
-
<p className="mt-2 max-w-2xl text-center text-lg text-white/90">
|
|
74
|
-
{collection.description}
|
|
75
|
-
</p>
|
|
76
|
-
)}
|
|
77
|
-
</div>
|
|
78
|
-
</div>
|
|
79
|
-
)}
|
|
80
|
-
{!collection.image?.url && (
|
|
81
|
-
<div className="mb-6">
|
|
82
|
-
<h1 className="text-3xl font-bold text-gray-900">
|
|
83
|
-
{collection.title}
|
|
84
|
-
</h1>
|
|
85
|
-
{collection.description && (
|
|
86
|
-
<p className="mt-2 text-gray-600">{collection.description}</p>
|
|
87
|
-
)}
|
|
88
|
-
</div>
|
|
89
|
-
)}
|
|
90
|
-
</div>
|
|
91
|
-
|
|
92
|
-
{/* Products Grid */}
|
|
93
|
-
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4 lg:gap-6">
|
|
94
|
-
{products.length === 0 ? (
|
|
95
|
-
<div className="col-span-full py-12 text-center text-gray-500">
|
|
96
|
-
No products in this collection
|
|
97
|
-
</div>
|
|
98
|
-
) : (
|
|
99
|
-
products.map((product) => {
|
|
100
|
-
const price = product.priceRange?.minVariantPrice;
|
|
101
|
-
const compareAtPrice = product.compareAtPriceRange?.minVariantPrice;
|
|
102
|
-
const hasDiscount =
|
|
103
|
-
compareAtPrice &&
|
|
104
|
-
price &&
|
|
105
|
-
parseFloat(compareAtPrice.amount) > parseFloat(price.amount);
|
|
106
|
-
|
|
107
|
-
return (
|
|
108
|
-
<Link
|
|
109
|
-
key={product.id}
|
|
110
|
-
href={`/products/${product.handle}`}
|
|
111
|
-
className="group"
|
|
112
|
-
>
|
|
113
|
-
<div className="relative aspect-square overflow-hidden rounded-lg bg-gray-100">
|
|
114
|
-
{product.featuredImage?.url ? (
|
|
115
|
-
<img
|
|
116
|
-
src={product.featuredImage.url}
|
|
117
|
-
alt={product.featuredImage.altText || product.title}
|
|
118
|
-
className="h-full w-full object-cover transition group-hover:scale-105"
|
|
119
|
-
/>
|
|
120
|
-
) : (
|
|
121
|
-
<div className="flex h-full items-center justify-center">
|
|
122
|
-
<span className="text-4xl text-gray-400">📷</span>
|
|
123
|
-
</div>
|
|
124
|
-
)}
|
|
125
|
-
</div>
|
|
126
|
-
<div className="mt-3">
|
|
127
|
-
<h3 className="text-sm font-medium text-gray-900 group-hover:text-gray-700">
|
|
128
|
-
{product.title}
|
|
129
|
-
</h3>
|
|
130
|
-
{price && (
|
|
131
|
-
<div className="mt-1 flex items-center gap-2">
|
|
132
|
-
<span className="text-sm font-medium text-gray-900">
|
|
133
|
-
{price.currencyCode} {price.amount}
|
|
134
|
-
</span>
|
|
135
|
-
{hasDiscount && compareAtPrice && (
|
|
136
|
-
<span className="text-sm text-gray-500 line-through">
|
|
137
|
-
{compareAtPrice.currencyCode} {compareAtPrice.amount}
|
|
138
|
-
</span>
|
|
139
|
-
)}
|
|
140
|
-
</div>
|
|
141
|
-
)}
|
|
142
|
-
</div>
|
|
143
|
-
</Link>
|
|
144
|
-
);
|
|
145
|
-
})
|
|
146
|
-
)}
|
|
147
|
-
</div>
|
|
148
|
-
|
|
149
|
-
{/* Pagination */}
|
|
150
|
-
<Pagination
|
|
151
|
-
hasMore={pageInfo.hasNextPage}
|
|
152
|
-
endCursor={pageInfo.endCursor}
|
|
153
|
-
currentCursor={after}
|
|
154
|
-
totalShown={products.length}
|
|
155
|
-
/>
|
|
156
|
-
</div>
|
|
157
|
-
);
|
|
158
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import Link from "next/link";
|
|
2
|
-
import { useCollections } from "@doswiftly/storefront-sdk/graphql/server";
|
|
3
|
-
|
|
4
|
-
export default async function CollectionsPage() {
|
|
5
|
-
// Fetch all collections
|
|
6
|
-
const { collections } = await useCollections({ first: 50 });
|
|
7
|
-
|
|
8
|
-
return (
|
|
9
|
-
<div className="container mx-auto px-4 py-8">
|
|
10
|
-
{/* Page Header */}
|
|
11
|
-
<div className="mb-8">
|
|
12
|
-
<h1 className="text-3xl font-bold text-gray-900">Collections</h1>
|
|
13
|
-
<p className="mt-2 text-gray-600">
|
|
14
|
-
Browse our curated product collections
|
|
15
|
-
</p>
|
|
16
|
-
</div>
|
|
17
|
-
|
|
18
|
-
{/* Collections Grid */}
|
|
19
|
-
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
|
20
|
-
{collections.length === 0 ? (
|
|
21
|
-
<div className="col-span-full py-12 text-center text-gray-500">
|
|
22
|
-
No collections found
|
|
23
|
-
</div>
|
|
24
|
-
) : (
|
|
25
|
-
collections.map((collection) => (
|
|
26
|
-
<Link
|
|
27
|
-
key={collection.id}
|
|
28
|
-
href={`/collections/${collection.handle}`}
|
|
29
|
-
className="group relative overflow-hidden rounded-lg"
|
|
30
|
-
>
|
|
31
|
-
<div className="aspect-[4/3] bg-gray-100">
|
|
32
|
-
{collection.image?.url ? (
|
|
33
|
-
<img
|
|
34
|
-
src={collection.image.url}
|
|
35
|
-
alt={collection.image.altText || collection.title}
|
|
36
|
-
className="h-full w-full object-cover transition group-hover:scale-105"
|
|
37
|
-
/>
|
|
38
|
-
) : (
|
|
39
|
-
<div className="flex h-full items-center justify-center bg-gradient-to-br from-gray-100 to-gray-200">
|
|
40
|
-
<span className="text-4xl text-gray-400">📦</span>
|
|
41
|
-
</div>
|
|
42
|
-
)}
|
|
43
|
-
</div>
|
|
44
|
-
<div className="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent" />
|
|
45
|
-
<div className="absolute bottom-0 left-0 right-0 p-6">
|
|
46
|
-
<h2 className="text-xl font-bold text-white">
|
|
47
|
-
{collection.title}
|
|
48
|
-
</h2>
|
|
49
|
-
{collection.description && (
|
|
50
|
-
<p className="mt-1 line-clamp-2 text-sm text-white/80">
|
|
51
|
-
{collection.description}
|
|
52
|
-
</p>
|
|
53
|
-
)}
|
|
54
|
-
</div>
|
|
55
|
-
</Link>
|
|
56
|
-
))
|
|
57
|
-
)}
|
|
58
|
-
</div>
|
|
59
|
-
</div>
|
|
60
|
-
);
|
|
61
|
-
}
|