@simple-auth-kit/cli 1.3.1 → 1.4.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/README.md +6 -5
- package/package.json +1 -1
- package/registry/admin-apps/nextjs/shared/.husky/commit-msg +1 -0
- package/registry/admin-apps/nextjs/shared/.husky/pre-commit +1 -0
- package/registry/admin-apps/nextjs/shared/.lintstagedrc.json +4 -0
- package/registry/admin-apps/nextjs/shared/.prettierrc.json +3 -0
- package/registry/admin-apps/nextjs/shared/commitlint.config.js +5 -0
- package/registry/admin-apps/nextjs/shared/eslint.config.js +28 -0
- package/registry/admin-apps/nextjs/variants/base/package.json +11 -2
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/audit-log/page.tsx +72 -16
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/customers/[id]/edit/page.tsx +107 -26
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/customers/[id]/page.tsx +131 -29
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/[id]/edit/page.tsx +111 -25
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/[id]/page.tsx +97 -22
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/permissions/[id]/edit/page.tsx +129 -46
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/permissions/[id]/page.tsx +64 -14
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/roles/[id]/page.tsx +91 -22
- package/registry/admin-apps/nextjs/variants/base/src/app/(console)/users/[id]/edit/page.tsx +94 -23
- package/registry/admin-apps/nextjs/variants/workspaces/package.json +11 -2
- package/registry/admin-apps/nextjs/variants/workspaces/src/app/(console)/account/page.tsx +179 -41
- package/registry/admin-apps/react/shared/.husky/commit-msg +1 -0
- package/registry/admin-apps/react/shared/.husky/pre-commit +1 -0
- package/registry/admin-apps/react/shared/.lintstagedrc.json +4 -0
- package/registry/admin-apps/react/shared/.prettierrc.json +3 -0
- package/registry/admin-apps/react/shared/commitlint.config.js +3 -0
- package/registry/admin-apps/react/shared/eslint.config.js +38 -0
- package/registry/admin-apps/react/variants/base/package.json +15 -2
- package/registry/admin-apps/react/variants/base/src/pages/CountryDetailPage.tsx +76 -19
- package/registry/admin-apps/react/variants/base/src/pages/CustomerDetailPage.tsx +110 -26
- package/registry/admin-apps/react/variants/base/src/pages/EditCountryPage.tsx +115 -29
- package/registry/admin-apps/react/variants/base/src/pages/EditCustomerPage.tsx +123 -31
- package/registry/admin-apps/react/variants/base/src/pages/EditLanguagePage.tsx +100 -24
- package/registry/admin-apps/react/variants/base/src/pages/EditRolePage.tsx +99 -26
- package/registry/admin-apps/react/variants/base/src/pages/EditUserPage.tsx +126 -31
- package/registry/admin-apps/react/variants/base/src/pages/LanguageDetailPage.tsx +75 -19
- package/registry/admin-apps/react/variants/base/src/pages/PermissionDetailPage.tsx +58 -13
- package/registry/admin-apps/react/variants/base/src/pages/PermissionsPage.tsx +132 -29
- package/registry/admin-apps/react/variants/base/src/pages/RoleDetailPage.tsx +70 -17
- package/registry/admin-apps/react/variants/base/src/pages/RolesPage.tsx +277 -59
- package/registry/admin-apps/react/variants/workspaces/package.json +15 -2
- package/registry/combos/express-drizzle/eslint.config.js +18 -0
- package/registry/combos/express-drizzle/package.json +5 -1
- package/registry/combos/express-drizzle/shared/src/request-context.ts +3 -0
- package/registry/combos/express-prisma/eslint.config.js +18 -0
- package/registry/combos/express-prisma/package.json +5 -1
- package/registry/combos/express-prisma/shared/src/request-context.ts +3 -0
- package/registry/combos/nestjs-drizzle/eslint.config.js +18 -0
- package/registry/combos/nestjs-drizzle/package.json +5 -1
- package/registry/combos/nestjs-drizzle/shared/src/auth.controller.ts +138 -33
- package/registry/combos/nestjs-drizzle/shared/src/route-tiers.ts +71 -18
- package/registry/combos/nestjs-prisma/eslint.config.js +18 -0
- package/registry/combos/nestjs-prisma/package.json +5 -1
- package/registry/combos/nestjs-prisma/shared/src/auth.controller.ts +148 -35
- package/registry/combos/nestjs-prisma/shared/src/route-tiers.ts +72 -18
- package/registry/mobile-apps/bare-rn/shared/.husky/commit-msg +1 -0
- package/registry/mobile-apps/bare-rn/shared/.husky/pre-commit +1 -0
- package/registry/mobile-apps/bare-rn/shared/.lintstagedrc.json +4 -0
- package/registry/mobile-apps/bare-rn/shared/.prettierrc.json +5 -0
- package/registry/mobile-apps/bare-rn/shared/commitlint.config.js +3 -0
- package/registry/mobile-apps/bare-rn/shared/eslint.config.js +19 -0
- package/registry/mobile-apps/bare-rn/variants/base/package.json +17 -12
- package/registry/mobile-apps/bare-rn/variants/workspaces/package.json +17 -12
- package/registry/mobile-apps/expo/shared/.husky/commit-msg +1 -0
- package/registry/mobile-apps/expo/shared/.husky/pre-commit +1 -0
- package/registry/mobile-apps/expo/shared/.lintstagedrc.json +4 -0
- package/registry/mobile-apps/expo/shared/.prettierrc.json +5 -0
- package/registry/mobile-apps/expo/shared/commitlint.config.js +3 -0
- package/registry/mobile-apps/expo/shared/eslint.config.js +9 -0
- package/registry/mobile-apps/expo/shared/src/screens/SessionsScreen.tsx +6 -17
- package/registry/mobile-apps/expo/shared/src/store/sessionsStore.ts +35 -0
- package/registry/mobile-apps/expo/variants/base/package.json +12 -3
- package/registry/mobile-apps/expo/variants/workspaces/package.json +12 -3
- package/registry/mobile-apps/expo/variants/workspaces/src/screens/WorkspaceSelectScreen.tsx +1 -1
- package/registry.json +13 -36
- package/simple-auth-kit.ts +13 -10
- package/registry/mobile-apps/bare-rn/shared/.eslintrc.js +0 -4
- package/registry/mobile-apps/bare-rn/shared/.prettierrc.js +0 -5
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type FormEvent,
|
|
3
|
+
startTransition,
|
|
4
|
+
useCallback,
|
|
5
|
+
useEffect,
|
|
6
|
+
useState,
|
|
7
|
+
} from "react";
|
|
2
8
|
import { useNavigate, useParams } from "react-router-dom";
|
|
3
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
AuthApiError,
|
|
11
|
+
type CountrySummary,
|
|
12
|
+
type UpdateCountryInput,
|
|
13
|
+
} from "@simple-auth-kit/auth-client";
|
|
4
14
|
import { toast } from "sonner";
|
|
5
15
|
import { authClient } from "@/lib/auth-client";
|
|
6
16
|
import { errorMessage, errorMessages } from "@/lib/error";
|
|
@@ -8,7 +18,14 @@ import { Breadcrumb } from "@/components/breadcrumb";
|
|
|
8
18
|
import { FormErrorAlert } from "@/components/form-error-alert";
|
|
9
19
|
import { PhotoUpload } from "@/components/photo-upload";
|
|
10
20
|
import { Button } from "@/components/ui/button";
|
|
11
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
Card,
|
|
23
|
+
CardContent,
|
|
24
|
+
CardDescription,
|
|
25
|
+
CardFooter,
|
|
26
|
+
CardHeader,
|
|
27
|
+
CardTitle,
|
|
28
|
+
} from "@/components/ui/card";
|
|
12
29
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
13
30
|
import { Heading } from "@/components/ui/heading";
|
|
14
31
|
import { Input } from "@/components/ui/input";
|
|
@@ -34,7 +51,8 @@ function toForm(country: CountrySummary): CountryFormFields {
|
|
|
34
51
|
};
|
|
35
52
|
}
|
|
36
53
|
|
|
37
|
-
const inputClassName =
|
|
54
|
+
const inputClassName =
|
|
55
|
+
"bg-background border-2 focus:border-purple-500 transition-colors";
|
|
38
56
|
const upperInputClassName = `${inputClassName} uppercase`;
|
|
39
57
|
|
|
40
58
|
export function EditCountryPage() {
|
|
@@ -62,7 +80,7 @@ export function EditCountryPage() {
|
|
|
62
80
|
}, [id]);
|
|
63
81
|
|
|
64
82
|
useEffect(() => {
|
|
65
|
-
void load();
|
|
83
|
+
startTransition(() => void load());
|
|
66
84
|
}, [load]);
|
|
67
85
|
|
|
68
86
|
if (!id) return null;
|
|
@@ -108,7 +126,9 @@ export function EditCountryPage() {
|
|
|
108
126
|
</div>
|
|
109
127
|
<Separator />
|
|
110
128
|
|
|
111
|
-
{loading && !country &&
|
|
129
|
+
{loading && !country && (
|
|
130
|
+
<p className="text-sm text-muted-foreground">Loading…</p>
|
|
131
|
+
)}
|
|
112
132
|
|
|
113
133
|
{country && (
|
|
114
134
|
<Card>
|
|
@@ -118,13 +138,19 @@ export function EditCountryPage() {
|
|
|
118
138
|
<form onSubmit={handleSubmit} className="space-y-8 w-full">
|
|
119
139
|
<Card className="w-full">
|
|
120
140
|
<CardHeader className="border-b bg-muted/50">
|
|
121
|
-
<CardTitle className="text-2xl">
|
|
122
|
-
|
|
141
|
+
<CardTitle className="text-2xl">
|
|
142
|
+
Country Information
|
|
143
|
+
</CardTitle>
|
|
144
|
+
<CardDescription className="text-base">
|
|
145
|
+
Enter country details
|
|
146
|
+
</CardDescription>
|
|
123
147
|
</CardHeader>
|
|
124
148
|
<CardContent className="pt-6">
|
|
125
149
|
<div className="space-y-6">
|
|
126
150
|
<div className="space-y-4">
|
|
127
|
-
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">
|
|
151
|
+
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">
|
|
152
|
+
Basic Information
|
|
153
|
+
</h3>
|
|
128
154
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
129
155
|
<div className="flex flex-col gap-2">
|
|
130
156
|
<Label htmlFor="name">Country Name</Label>
|
|
@@ -134,7 +160,9 @@ export function EditCountryPage() {
|
|
|
134
160
|
placeholder="e.g., Bangladesh"
|
|
135
161
|
required
|
|
136
162
|
value={form.name}
|
|
137
|
-
onChange={(e) =>
|
|
163
|
+
onChange={(e) =>
|
|
164
|
+
setForm({ ...form, name: e.target.value })
|
|
165
|
+
}
|
|
138
166
|
type="text"
|
|
139
167
|
className={inputClassName}
|
|
140
168
|
/>
|
|
@@ -142,7 +170,9 @@ export function EditCountryPage() {
|
|
|
142
170
|
<div className="flex flex-col gap-2">
|
|
143
171
|
<Label htmlFor="code">
|
|
144
172
|
Country Code
|
|
145
|
-
<span className="text-xs text-destructive dark:text-destructive-foreground">
|
|
173
|
+
<span className="text-xs text-destructive dark:text-destructive-foreground">
|
|
174
|
+
(2-3 characters, e.g., BD, USA)
|
|
175
|
+
</span>
|
|
146
176
|
</Label>
|
|
147
177
|
<Input
|
|
148
178
|
id="code"
|
|
@@ -150,7 +180,12 @@ export function EditCountryPage() {
|
|
|
150
180
|
placeholder="e.g., BD"
|
|
151
181
|
required
|
|
152
182
|
value={form.code}
|
|
153
|
-
onChange={(e) =>
|
|
183
|
+
onChange={(e) =>
|
|
184
|
+
setForm({
|
|
185
|
+
...form,
|
|
186
|
+
code: e.target.value.toUpperCase(),
|
|
187
|
+
})
|
|
188
|
+
}
|
|
154
189
|
type="text"
|
|
155
190
|
maxLength={3}
|
|
156
191
|
className={upperInputClassName}
|
|
@@ -159,7 +194,9 @@ export function EditCountryPage() {
|
|
|
159
194
|
<div className="flex flex-col gap-2">
|
|
160
195
|
<Label htmlFor="emoji">
|
|
161
196
|
Country Emoji
|
|
162
|
-
<span className="text-xs text-muted-foreground">
|
|
197
|
+
<span className="text-xs text-muted-foreground">
|
|
198
|
+
(Stands in wherever no flag image is set)
|
|
199
|
+
</span>
|
|
163
200
|
</Label>
|
|
164
201
|
<Input
|
|
165
202
|
id="emoji"
|
|
@@ -167,7 +204,9 @@ export function EditCountryPage() {
|
|
|
167
204
|
placeholder="e.g., 🇧🇩"
|
|
168
205
|
required
|
|
169
206
|
value={form.emoji}
|
|
170
|
-
onChange={(e) =>
|
|
207
|
+
onChange={(e) =>
|
|
208
|
+
setForm({ ...form, emoji: e.target.value })
|
|
209
|
+
}
|
|
171
210
|
type="text"
|
|
172
211
|
className={inputClassName}
|
|
173
212
|
/>
|
|
@@ -175,7 +214,9 @@ export function EditCountryPage() {
|
|
|
175
214
|
<div className="flex flex-col gap-2">
|
|
176
215
|
<Label htmlFor="phoneCode">
|
|
177
216
|
Country Phone Code
|
|
178
|
-
<span className="text-xs text-destructive dark:text-destructive-foreground">
|
|
217
|
+
<span className="text-xs text-destructive dark:text-destructive-foreground">
|
|
218
|
+
(2-3 characters, e.g., +880, +60)
|
|
219
|
+
</span>
|
|
179
220
|
</Label>
|
|
180
221
|
<Input
|
|
181
222
|
id="phoneCode"
|
|
@@ -183,7 +224,9 @@ export function EditCountryPage() {
|
|
|
183
224
|
placeholder="e.g., +880"
|
|
184
225
|
required
|
|
185
226
|
value={form.phoneCode}
|
|
186
|
-
onChange={(e) =>
|
|
227
|
+
onChange={(e) =>
|
|
228
|
+
setForm({ ...form, phoneCode: e.target.value })
|
|
229
|
+
}
|
|
187
230
|
type="text"
|
|
188
231
|
className={upperInputClassName}
|
|
189
232
|
/>
|
|
@@ -191,7 +234,9 @@ export function EditCountryPage() {
|
|
|
191
234
|
<div className="flex flex-col gap-2">
|
|
192
235
|
<Label htmlFor="currency">
|
|
193
236
|
Country Currency
|
|
194
|
-
<span className="text-xs text-destructive dark:text-destructive-foreground">
|
|
237
|
+
<span className="text-xs text-destructive dark:text-destructive-foreground">
|
|
238
|
+
(e.g., USD, EUR)
|
|
239
|
+
</span>
|
|
195
240
|
</Label>
|
|
196
241
|
<Input
|
|
197
242
|
id="currency"
|
|
@@ -199,7 +244,12 @@ export function EditCountryPage() {
|
|
|
199
244
|
placeholder="e.g., USD, EUR"
|
|
200
245
|
required
|
|
201
246
|
value={form.currency}
|
|
202
|
-
onChange={(e) =>
|
|
247
|
+
onChange={(e) =>
|
|
248
|
+
setForm({
|
|
249
|
+
...form,
|
|
250
|
+
currency: e.target.value.toUpperCase(),
|
|
251
|
+
})
|
|
252
|
+
}
|
|
203
253
|
type="text"
|
|
204
254
|
maxLength={3}
|
|
205
255
|
className={upperInputClassName}
|
|
@@ -208,7 +258,9 @@ export function EditCountryPage() {
|
|
|
208
258
|
<div className="flex flex-col gap-2">
|
|
209
259
|
<Label htmlFor="currencyName">
|
|
210
260
|
Country Currency Name
|
|
211
|
-
<span className="text-xs text-destructive dark:text-destructive-foreground">
|
|
261
|
+
<span className="text-xs text-destructive dark:text-destructive-foreground">
|
|
262
|
+
(e.g., US Dollar, Euro)
|
|
263
|
+
</span>
|
|
212
264
|
</Label>
|
|
213
265
|
<Input
|
|
214
266
|
id="currencyName"
|
|
@@ -216,7 +268,9 @@ export function EditCountryPage() {
|
|
|
216
268
|
placeholder="e.g., US Dollar, Euro"
|
|
217
269
|
required
|
|
218
270
|
value={form.currencyName}
|
|
219
|
-
onChange={(e) =>
|
|
271
|
+
onChange={(e) =>
|
|
272
|
+
setForm({ ...form, currencyName: e.target.value })
|
|
273
|
+
}
|
|
220
274
|
type="text"
|
|
221
275
|
className={inputClassName}
|
|
222
276
|
/>
|
|
@@ -224,7 +278,9 @@ export function EditCountryPage() {
|
|
|
224
278
|
<div className="flex flex-col gap-2">
|
|
225
279
|
<Label htmlFor="isoCode">
|
|
226
280
|
Country ISO Code
|
|
227
|
-
<span className="text-xs text-destructive dark:text-destructive-foreground">
|
|
281
|
+
<span className="text-xs text-destructive dark:text-destructive-foreground">
|
|
282
|
+
(ISO code, e.g., BDT, USD)
|
|
283
|
+
</span>
|
|
228
284
|
</Label>
|
|
229
285
|
<Input
|
|
230
286
|
id="isoCode"
|
|
@@ -232,7 +288,12 @@ export function EditCountryPage() {
|
|
|
232
288
|
placeholder="e.g., BDT, USD"
|
|
233
289
|
required
|
|
234
290
|
value={form.isoCode}
|
|
235
|
-
onChange={(e) =>
|
|
291
|
+
onChange={(e) =>
|
|
292
|
+
setForm({
|
|
293
|
+
...form,
|
|
294
|
+
isoCode: e.target.value.toUpperCase(),
|
|
295
|
+
})
|
|
296
|
+
}
|
|
236
297
|
type="text"
|
|
237
298
|
maxLength={3}
|
|
238
299
|
className={upperInputClassName}
|
|
@@ -245,27 +306,52 @@ export function EditCountryPage() {
|
|
|
245
306
|
|
|
246
307
|
<CardHeader className="border-b bg-muted/50 mt-6">
|
|
247
308
|
<CardTitle className="text-2xl">Country Flag</CardTitle>
|
|
248
|
-
<CardDescription className="text-base">
|
|
309
|
+
<CardDescription className="text-base">
|
|
310
|
+
Upload country flag (Max size: 2MB, Formats: JPG, PNG, SVG)
|
|
311
|
+
</CardDescription>
|
|
249
312
|
</CardHeader>
|
|
250
313
|
<CardContent className="pt-6">
|
|
251
|
-
<PhotoUpload
|
|
314
|
+
<PhotoUpload
|
|
315
|
+
photo={form.flag}
|
|
316
|
+
fallback={form.emoji || "?"}
|
|
317
|
+
onChange={(next) => setForm({ ...form, flag: next })}
|
|
318
|
+
disabled={saving}
|
|
319
|
+
/>
|
|
252
320
|
</CardContent>
|
|
253
321
|
|
|
254
322
|
<div className="flex justify-center mt-6">
|
|
255
323
|
<label className="flex flex-row items-center space-x-3 rounded-lg border border-purple-500 bg-purple-50 dark:bg-purple-950/20 p-4">
|
|
256
|
-
<Checkbox
|
|
324
|
+
<Checkbox
|
|
325
|
+
checked={form.isActive}
|
|
326
|
+
onCheckedChange={(checked) =>
|
|
327
|
+
setForm({ ...form, isActive: checked === true })
|
|
328
|
+
}
|
|
329
|
+
/>
|
|
257
330
|
<span className="space-y-1 leading-none">
|
|
258
|
-
<span className="block text-base font-medium">
|
|
259
|
-
|
|
331
|
+
<span className="block text-base font-medium">
|
|
332
|
+
Active Status
|
|
333
|
+
</span>
|
|
334
|
+
<span className="block text-sm text-muted-foreground">
|
|
335
|
+
Country will be active and available for selection
|
|
336
|
+
</span>
|
|
260
337
|
</span>
|
|
261
338
|
</label>
|
|
262
339
|
</div>
|
|
263
340
|
|
|
264
341
|
<CardFooter className="flex justify-center gap-4 mt-8 pb-8">
|
|
265
|
-
<Button
|
|
342
|
+
<Button
|
|
343
|
+
type="button"
|
|
344
|
+
variant="outline"
|
|
345
|
+
onClick={() => navigate(`/countries/${id}`)}
|
|
346
|
+
disabled={saving}
|
|
347
|
+
>
|
|
266
348
|
Cancel
|
|
267
349
|
</Button>
|
|
268
|
-
<Button
|
|
350
|
+
<Button
|
|
351
|
+
disabled={saving}
|
|
352
|
+
className="bg-purple-600 hover:bg-purple-700 dark:bg-purple-600 dark:hover:bg-purple-700 min-w-32"
|
|
353
|
+
type="submit"
|
|
354
|
+
>
|
|
269
355
|
{saving ? "Updating..." : "Update Country"}
|
|
270
356
|
</Button>
|
|
271
357
|
</CardFooter>
|
|
@@ -1,6 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type FormEvent,
|
|
3
|
+
startTransition,
|
|
4
|
+
useCallback,
|
|
5
|
+
useEffect,
|
|
6
|
+
useState,
|
|
7
|
+
} from "react";
|
|
2
8
|
import { useNavigate, useParams } from "react-router-dom";
|
|
3
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
AuthApiError,
|
|
11
|
+
type CustomerSummary,
|
|
12
|
+
type UpdateCustomerInput,
|
|
13
|
+
} from "@simple-auth-kit/auth-client";
|
|
4
14
|
import { format } from "date-fns";
|
|
5
15
|
import { toast } from "sonner";
|
|
6
16
|
import { authClient } from "@/lib/auth-client";
|
|
@@ -9,7 +19,14 @@ import { Breadcrumb } from "@/components/breadcrumb";
|
|
|
9
19
|
import { FormErrorAlert } from "@/components/form-error-alert";
|
|
10
20
|
import { PhotoUpload } from "@/components/photo-upload";
|
|
11
21
|
import { Button } from "@/components/ui/button";
|
|
12
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
Card,
|
|
24
|
+
CardContent,
|
|
25
|
+
CardDescription,
|
|
26
|
+
CardFooter,
|
|
27
|
+
CardHeader,
|
|
28
|
+
CardTitle,
|
|
29
|
+
} from "@/components/ui/card";
|
|
13
30
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
14
31
|
import { Combobox } from "@/components/ui/combobox";
|
|
15
32
|
import { DatePicker } from "@/components/ui/date-picker";
|
|
@@ -17,7 +34,11 @@ import { Heading } from "@/components/ui/heading";
|
|
|
17
34
|
import { Input } from "@/components/ui/input";
|
|
18
35
|
import { Label } from "@/components/ui/label";
|
|
19
36
|
import { Separator } from "@/components/ui/separator";
|
|
20
|
-
import {
|
|
37
|
+
import {
|
|
38
|
+
emptyCustomerFields,
|
|
39
|
+
GENDER_OPTIONS,
|
|
40
|
+
type CustomerFormFields,
|
|
41
|
+
} from "./customer-schema";
|
|
21
42
|
|
|
22
43
|
function apiErrorMessage(err: unknown, fallback: string): string {
|
|
23
44
|
return err instanceof AuthApiError ? err.message : fallback;
|
|
@@ -38,7 +59,8 @@ function toForm(customer: CustomerSummary): CustomerFormFields {
|
|
|
38
59
|
};
|
|
39
60
|
}
|
|
40
61
|
|
|
41
|
-
const inputClassName =
|
|
62
|
+
const inputClassName =
|
|
63
|
+
"bg-background border-2 focus:border-purple-500 transition-colors";
|
|
42
64
|
|
|
43
65
|
export function EditCustomerPage() {
|
|
44
66
|
const { id } = useParams<{ id: string }>();
|
|
@@ -65,7 +87,7 @@ export function EditCustomerPage() {
|
|
|
65
87
|
}, [id]);
|
|
66
88
|
|
|
67
89
|
useEffect(() => {
|
|
68
|
-
void load();
|
|
90
|
+
startTransition(() => void load());
|
|
69
91
|
}, [load]);
|
|
70
92
|
|
|
71
93
|
if (!id) return null;
|
|
@@ -84,7 +106,9 @@ export function EditCustomerPage() {
|
|
|
84
106
|
phone: form.phone || null,
|
|
85
107
|
dob: form.dob ? format(form.dob, "yyyy-MM-dd") : null,
|
|
86
108
|
gender: form.gender || null,
|
|
87
|
-
joinedDate: form.joinedDate
|
|
109
|
+
joinedDate: form.joinedDate
|
|
110
|
+
? format(form.joinedDate, "yyyy-MM-dd")
|
|
111
|
+
: undefined,
|
|
88
112
|
photo: form.photo || null,
|
|
89
113
|
isActive: form.isActive,
|
|
90
114
|
};
|
|
@@ -113,7 +137,9 @@ export function EditCustomerPage() {
|
|
|
113
137
|
</div>
|
|
114
138
|
<Separator />
|
|
115
139
|
|
|
116
|
-
{loading && !customer &&
|
|
140
|
+
{loading && !customer && (
|
|
141
|
+
<p className="text-sm text-muted-foreground">Loading…</p>
|
|
142
|
+
)}
|
|
117
143
|
|
|
118
144
|
{customer && (
|
|
119
145
|
<Card>
|
|
@@ -123,13 +149,19 @@ export function EditCustomerPage() {
|
|
|
123
149
|
<form onSubmit={handleSubmit} className="space-y-8 w-full">
|
|
124
150
|
<Card className="w-full">
|
|
125
151
|
<CardHeader className="border-b bg-muted/50">
|
|
126
|
-
<CardTitle className="text-2xl">
|
|
127
|
-
|
|
152
|
+
<CardTitle className="text-2xl">
|
|
153
|
+
Customer Information
|
|
154
|
+
</CardTitle>
|
|
155
|
+
<CardDescription className="text-base">
|
|
156
|
+
Update customer's basic information
|
|
157
|
+
</CardDescription>
|
|
128
158
|
</CardHeader>
|
|
129
159
|
<CardContent className="pt-6">
|
|
130
160
|
<div className="space-y-6">
|
|
131
161
|
<div className="space-y-4">
|
|
132
|
-
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">
|
|
162
|
+
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">
|
|
163
|
+
Personal Information
|
|
164
|
+
</h3>
|
|
133
165
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
134
166
|
<div className="flex flex-col gap-2">
|
|
135
167
|
<Label htmlFor="firstName">First Name</Label>
|
|
@@ -138,7 +170,9 @@ export function EditCustomerPage() {
|
|
|
138
170
|
disabled={saving}
|
|
139
171
|
placeholder="First Name"
|
|
140
172
|
value={form.firstName}
|
|
141
|
-
onChange={(e) =>
|
|
173
|
+
onChange={(e) =>
|
|
174
|
+
setForm({ ...form, firstName: e.target.value })
|
|
175
|
+
}
|
|
142
176
|
type="text"
|
|
143
177
|
className={inputClassName}
|
|
144
178
|
/>
|
|
@@ -150,7 +184,9 @@ export function EditCustomerPage() {
|
|
|
150
184
|
disabled={saving}
|
|
151
185
|
placeholder="Last Name"
|
|
152
186
|
value={form.lastName}
|
|
153
|
-
onChange={(e) =>
|
|
187
|
+
onChange={(e) =>
|
|
188
|
+
setForm({ ...form, lastName: e.target.value })
|
|
189
|
+
}
|
|
154
190
|
type="text"
|
|
155
191
|
className={inputClassName}
|
|
156
192
|
/>
|
|
@@ -158,14 +194,18 @@ export function EditCustomerPage() {
|
|
|
158
194
|
<div className="flex flex-col gap-2">
|
|
159
195
|
<Label htmlFor="username">
|
|
160
196
|
Username
|
|
161
|
-
<span className="text-xs text-destructive dark:text-destructive-foreground">
|
|
197
|
+
<span className="text-xs text-destructive dark:text-destructive-foreground">
|
|
198
|
+
(Must be unique)
|
|
199
|
+
</span>
|
|
162
200
|
</Label>
|
|
163
201
|
<Input
|
|
164
202
|
id="username"
|
|
165
203
|
disabled={saving}
|
|
166
204
|
placeholder="Username"
|
|
167
205
|
value={form.username}
|
|
168
|
-
onChange={(e) =>
|
|
206
|
+
onChange={(e) =>
|
|
207
|
+
setForm({ ...form, username: e.target.value })
|
|
208
|
+
}
|
|
169
209
|
className={inputClassName}
|
|
170
210
|
/>
|
|
171
211
|
</div>
|
|
@@ -173,12 +213,16 @@ export function EditCustomerPage() {
|
|
|
173
213
|
</div>
|
|
174
214
|
|
|
175
215
|
<div className="space-y-4">
|
|
176
|
-
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">
|
|
216
|
+
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">
|
|
217
|
+
Contact Information
|
|
218
|
+
</h3>
|
|
177
219
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
178
220
|
<div className="flex flex-col gap-2">
|
|
179
221
|
<Label htmlFor="email">
|
|
180
222
|
Email
|
|
181
|
-
<span className="text-xs text-destructive dark:text-destructive-foreground">
|
|
223
|
+
<span className="text-xs text-destructive dark:text-destructive-foreground">
|
|
224
|
+
(Must be unique)
|
|
225
|
+
</span>
|
|
182
226
|
</Label>
|
|
183
227
|
<Input
|
|
184
228
|
id="email"
|
|
@@ -186,7 +230,9 @@ export function EditCustomerPage() {
|
|
|
186
230
|
placeholder="customer@example.com"
|
|
187
231
|
required
|
|
188
232
|
value={form.email}
|
|
189
|
-
onChange={(e) =>
|
|
233
|
+
onChange={(e) =>
|
|
234
|
+
setForm({ ...form, email: e.target.value })
|
|
235
|
+
}
|
|
190
236
|
type="email"
|
|
191
237
|
className={inputClassName}
|
|
192
238
|
/>
|
|
@@ -194,14 +240,18 @@ export function EditCustomerPage() {
|
|
|
194
240
|
<div className="flex flex-col gap-2">
|
|
195
241
|
<Label htmlFor="phone">
|
|
196
242
|
Phone
|
|
197
|
-
<span className="text-xs text-muted-foreground">
|
|
243
|
+
<span className="text-xs text-muted-foreground">
|
|
244
|
+
(With country code)
|
|
245
|
+
</span>
|
|
198
246
|
</Label>
|
|
199
247
|
<Input
|
|
200
248
|
id="phone"
|
|
201
249
|
type="text"
|
|
202
250
|
disabled={saving}
|
|
203
251
|
value={form.phone}
|
|
204
|
-
onChange={(e) =>
|
|
252
|
+
onChange={(e) =>
|
|
253
|
+
setForm({ ...form, phone: e.target.value })
|
|
254
|
+
}
|
|
205
255
|
placeholder="+1234567890"
|
|
206
256
|
className={inputClassName}
|
|
207
257
|
/>
|
|
@@ -210,15 +260,29 @@ export function EditCustomerPage() {
|
|
|
210
260
|
</div>
|
|
211
261
|
|
|
212
262
|
<div className="space-y-4">
|
|
213
|
-
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">
|
|
263
|
+
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">
|
|
264
|
+
Additional Details
|
|
265
|
+
</h3>
|
|
214
266
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
215
267
|
<div className="flex flex-col gap-2 p-2">
|
|
216
268
|
<Label>Joined Date</Label>
|
|
217
|
-
<DatePicker
|
|
269
|
+
<DatePicker
|
|
270
|
+
placeholder="Joined Date"
|
|
271
|
+
value={form.joinedDate}
|
|
272
|
+
onChange={(date) =>
|
|
273
|
+
setForm({ ...form, joinedDate: date })
|
|
274
|
+
}
|
|
275
|
+
displayFormat="dd-MM-yyyy"
|
|
276
|
+
/>
|
|
218
277
|
</div>
|
|
219
278
|
<div className="flex flex-col gap-2 p-2">
|
|
220
279
|
<Label>Date of birth</Label>
|
|
221
|
-
<DatePicker
|
|
280
|
+
<DatePicker
|
|
281
|
+
placeholder="Date of birth"
|
|
282
|
+
value={form.dob}
|
|
283
|
+
onChange={(date) => setForm({ ...form, dob: date })}
|
|
284
|
+
displayFormat="dd-MM-yyyy"
|
|
285
|
+
/>
|
|
222
286
|
</div>
|
|
223
287
|
<div className="flex flex-col gap-2">
|
|
224
288
|
<Label>Gender</Label>
|
|
@@ -226,7 +290,9 @@ export function EditCustomerPage() {
|
|
|
226
290
|
options={GENDER_OPTIONS}
|
|
227
291
|
selected={form.gender}
|
|
228
292
|
placeholder="Select Gender"
|
|
229
|
-
onChange={(option) =>
|
|
293
|
+
onChange={(option) =>
|
|
294
|
+
setForm({ ...form, gender: option.value })
|
|
295
|
+
}
|
|
230
296
|
showCreate={false}
|
|
231
297
|
popoverClassName="min-w-[200px]"
|
|
232
298
|
/>
|
|
@@ -238,27 +304,53 @@ export function EditCustomerPage() {
|
|
|
238
304
|
|
|
239
305
|
<CardHeader className="border-b bg-muted/50 mt-6">
|
|
240
306
|
<CardTitle className="text-2xl">Profile Photo</CardTitle>
|
|
241
|
-
<CardDescription className="text-base">
|
|
307
|
+
<CardDescription className="text-base">
|
|
308
|
+
Upload customer's profile picture (Max size: 2MB,
|
|
309
|
+
Formats: JPG, PNG)
|
|
310
|
+
</CardDescription>
|
|
242
311
|
</CardHeader>
|
|
243
312
|
<CardContent className="pt-6">
|
|
244
|
-
<PhotoUpload
|
|
313
|
+
<PhotoUpload
|
|
314
|
+
photo={form.photo}
|
|
315
|
+
fallback="?"
|
|
316
|
+
onChange={(next) => setForm({ ...form, photo: next })}
|
|
317
|
+
disabled={saving}
|
|
318
|
+
/>
|
|
245
319
|
</CardContent>
|
|
246
320
|
|
|
247
321
|
<div className="flex justify-center mt-6">
|
|
248
322
|
<label className="flex flex-row items-center space-x-3 rounded-lg border border-purple-500 bg-purple-50 dark:bg-purple-950/20 p-4">
|
|
249
|
-
<Checkbox
|
|
323
|
+
<Checkbox
|
|
324
|
+
checked={form.isActive}
|
|
325
|
+
onCheckedChange={(checked) =>
|
|
326
|
+
setForm({ ...form, isActive: checked === true })
|
|
327
|
+
}
|
|
328
|
+
/>
|
|
250
329
|
<span className="space-y-1 leading-none">
|
|
251
|
-
<span className="block text-base font-medium">
|
|
252
|
-
|
|
330
|
+
<span className="block text-base font-medium">
|
|
331
|
+
Active Status
|
|
332
|
+
</span>
|
|
333
|
+
<span className="block text-sm text-muted-foreground">
|
|
334
|
+
Customer will be active and available for selection
|
|
335
|
+
</span>
|
|
253
336
|
</span>
|
|
254
337
|
</label>
|
|
255
338
|
</div>
|
|
256
339
|
|
|
257
340
|
<CardFooter className="flex justify-center gap-4 mt-8 pb-8">
|
|
258
|
-
<Button
|
|
341
|
+
<Button
|
|
342
|
+
type="button"
|
|
343
|
+
variant="outline"
|
|
344
|
+
onClick={() => navigate(`/customers/${id}`)}
|
|
345
|
+
disabled={saving}
|
|
346
|
+
>
|
|
259
347
|
Cancel
|
|
260
348
|
</Button>
|
|
261
|
-
<Button
|
|
349
|
+
<Button
|
|
350
|
+
disabled={saving}
|
|
351
|
+
className="bg-purple-600 hover:bg-purple-700 dark:bg-purple-600 dark:hover:bg-purple-700 min-w-32"
|
|
352
|
+
type="submit"
|
|
353
|
+
>
|
|
262
354
|
{saving ? "Updating..." : "Update Customer"}
|
|
263
355
|
</Button>
|
|
264
356
|
</CardFooter>
|