@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
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
import { useAbility } from "@casl/react";
|
|
4
4
|
import Link from "next/link";
|
|
5
5
|
import { useParams, useRouter } from "next/navigation";
|
|
6
|
-
import { useCallback, useEffect, useState } from "react";
|
|
7
|
-
import {
|
|
6
|
+
import { startTransition, useCallback, useEffect, useState } from "react";
|
|
7
|
+
import {
|
|
8
|
+
AuthApiError,
|
|
9
|
+
type CustomerSummary,
|
|
10
|
+
} from "@simple-auth-kit/auth-client";
|
|
8
11
|
import { format } from "date-fns";
|
|
9
12
|
import { PencilIcon } from "lucide-react";
|
|
10
13
|
import { toast } from "sonner";
|
|
@@ -13,7 +16,13 @@ import { PermissionRequired } from "@/components/permission-required";
|
|
|
13
16
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
14
17
|
import { Badge } from "@/components/ui/badge";
|
|
15
18
|
import { Button, buttonVariants } from "@/components/ui/button";
|
|
16
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
Card,
|
|
21
|
+
CardContent,
|
|
22
|
+
CardDescription,
|
|
23
|
+
CardHeader,
|
|
24
|
+
CardTitle,
|
|
25
|
+
} from "@/components/ui/card";
|
|
17
26
|
import {
|
|
18
27
|
Dialog,
|
|
19
28
|
DialogContent,
|
|
@@ -23,7 +32,12 @@ import {
|
|
|
23
32
|
DialogTitle,
|
|
24
33
|
DialogTrigger,
|
|
25
34
|
} from "@/components/ui/dialog";
|
|
26
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
PERMISSIONS,
|
|
37
|
+
hasPermission,
|
|
38
|
+
missingPermissionHint,
|
|
39
|
+
type AppAbility,
|
|
40
|
+
} from "@/lib/ability";
|
|
27
41
|
import { authClient } from "@/lib/auth-client";
|
|
28
42
|
|
|
29
43
|
function apiErrorMessage(err: unknown, fallback: string): string {
|
|
@@ -82,20 +96,35 @@ export default function CustomerDetailPage() {
|
|
|
82
96
|
|
|
83
97
|
useEffect(() => {
|
|
84
98
|
if (!canRead) return;
|
|
85
|
-
void load();
|
|
99
|
+
startTransition(() => void load());
|
|
86
100
|
}, [canRead, load]);
|
|
87
101
|
|
|
88
|
-
if (!canRead)
|
|
102
|
+
if (!canRead)
|
|
103
|
+
return (
|
|
104
|
+
<PermissionRequired
|
|
105
|
+
permission={PERMISSIONS.customersRead}
|
|
106
|
+
what="Customer details"
|
|
107
|
+
/>
|
|
108
|
+
);
|
|
89
109
|
|
|
90
110
|
async function toggleActive() {
|
|
91
111
|
if (!customer) return;
|
|
92
112
|
try {
|
|
93
113
|
if (customer.isActive) await authClient.deactivateCustomer(id);
|
|
94
114
|
else await authClient.activateCustomer(id);
|
|
95
|
-
setCustomer((prev) =>
|
|
96
|
-
|
|
115
|
+
setCustomer((prev) =>
|
|
116
|
+
prev ? { ...prev, isActive: !prev.isActive } : prev,
|
|
117
|
+
);
|
|
118
|
+
toast.success(
|
|
119
|
+
customer.isActive ? "Customer deactivated." : "Customer activated.",
|
|
120
|
+
);
|
|
97
121
|
} catch (err) {
|
|
98
|
-
toast.error(
|
|
122
|
+
toast.error(
|
|
123
|
+
apiErrorMessage(
|
|
124
|
+
err,
|
|
125
|
+
"Couldn't change this customer's status. Try again.",
|
|
126
|
+
),
|
|
127
|
+
);
|
|
99
128
|
}
|
|
100
129
|
}
|
|
101
130
|
|
|
@@ -106,7 +135,9 @@ export default function CustomerDetailPage() {
|
|
|
106
135
|
toast.success("Customer deleted.");
|
|
107
136
|
router.push("/customers");
|
|
108
137
|
} catch (err) {
|
|
109
|
-
toast.error(
|
|
138
|
+
toast.error(
|
|
139
|
+
apiErrorMessage(err, "Couldn't delete this customer. Try again."),
|
|
140
|
+
);
|
|
110
141
|
setDeleting(false);
|
|
111
142
|
setDeleteOpen(false);
|
|
112
143
|
}
|
|
@@ -114,27 +145,44 @@ export default function CustomerDetailPage() {
|
|
|
114
145
|
|
|
115
146
|
return (
|
|
116
147
|
<div className="flex flex-col gap-4">
|
|
117
|
-
<Breadcrumb
|
|
148
|
+
<Breadcrumb
|
|
149
|
+
items={[
|
|
150
|
+
{ title: "Customers", href: "/customers" },
|
|
151
|
+
{ title: customer?.email ?? "Details", href: `/customers/${id}` },
|
|
152
|
+
]}
|
|
153
|
+
/>
|
|
118
154
|
|
|
119
|
-
{loading && !customer &&
|
|
155
|
+
{loading && !customer && (
|
|
156
|
+
<p className="text-sm text-muted-foreground">Loading…</p>
|
|
157
|
+
)}
|
|
120
158
|
|
|
121
159
|
{customer && (
|
|
122
160
|
<>
|
|
123
161
|
<Card>
|
|
124
162
|
<CardHeader className="flex flex-row items-center justify-between">
|
|
125
163
|
<div>
|
|
126
|
-
<CardTitle>
|
|
164
|
+
<CardTitle>
|
|
165
|
+
{customerName(customer) || customer.email}
|
|
166
|
+
</CardTitle>
|
|
127
167
|
<CardDescription>
|
|
128
|
-
Joined: {format(new Date(customer.joinedDate), "dd MMM yyyy")}
|
|
129
|
-
{
|
|
168
|
+
Joined: {format(new Date(customer.joinedDate), "dd MMM yyyy")}{" "}
|
|
169
|
+
· Created:{" "}
|
|
170
|
+
{format(new Date(customer.createdAt), "dd MMM yyyy")} ·
|
|
171
|
+
Updated: {format(new Date(customer.updatedAt), "dd MMM yyyy")}
|
|
130
172
|
</CardDescription>
|
|
131
173
|
</div>
|
|
132
174
|
<div className="flex items-center gap-1.5">
|
|
133
|
-
<Badge variant={customer.isActive ? "success" : "destructive"}>
|
|
175
|
+
<Badge variant={customer.isActive ? "success" : "destructive"}>
|
|
176
|
+
{customer.isActive ? "Active" : "Inactive"}
|
|
177
|
+
</Badge>
|
|
134
178
|
<Link
|
|
135
179
|
href={`/customers/${id}/edit`}
|
|
136
180
|
className={buttonVariants({ variant: "outline", size: "sm" })}
|
|
137
|
-
title={
|
|
181
|
+
title={
|
|
182
|
+
canManage
|
|
183
|
+
? undefined
|
|
184
|
+
: missingPermissionHint(PERMISSIONS.customersManage)
|
|
185
|
+
}
|
|
138
186
|
aria-disabled={!canManage}
|
|
139
187
|
onClick={(e) => !canManage && e.preventDefault()}
|
|
140
188
|
>
|
|
@@ -145,8 +193,16 @@ export default function CustomerDetailPage() {
|
|
|
145
193
|
</CardHeader>
|
|
146
194
|
<CardContent className="flex flex-col gap-4">
|
|
147
195
|
<Avatar className="size-16">
|
|
148
|
-
{customer.photo &&
|
|
149
|
-
|
|
196
|
+
{customer.photo && (
|
|
197
|
+
<AvatarImage
|
|
198
|
+
src={customer.photo}
|
|
199
|
+
alt=""
|
|
200
|
+
className="object-cover"
|
|
201
|
+
/>
|
|
202
|
+
)}
|
|
203
|
+
<AvatarFallback className="text-base">
|
|
204
|
+
{initialsOf(customer)}
|
|
205
|
+
</AvatarFallback>
|
|
150
206
|
</Avatar>
|
|
151
207
|
|
|
152
208
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
@@ -155,14 +211,39 @@ export default function CustomerDetailPage() {
|
|
|
155
211
|
{field("First name", customer.firstName)}
|
|
156
212
|
{field("Last name", customer.lastName)}
|
|
157
213
|
{field("Phone", customer.phone)}
|
|
158
|
-
{field(
|
|
159
|
-
|
|
160
|
-
|
|
214
|
+
{field(
|
|
215
|
+
"Gender",
|
|
216
|
+
customer.gender &&
|
|
217
|
+
customer.gender.charAt(0).toUpperCase() +
|
|
218
|
+
customer.gender.slice(1),
|
|
219
|
+
)}
|
|
220
|
+
{field(
|
|
221
|
+
"Date of birth",
|
|
222
|
+
customer.dob
|
|
223
|
+
? format(new Date(customer.dob), "dd MMM yyyy")
|
|
224
|
+
: null,
|
|
225
|
+
)}
|
|
226
|
+
{field(
|
|
227
|
+
"Joined date",
|
|
228
|
+
format(new Date(customer.joinedDate), "dd MMM yyyy"),
|
|
229
|
+
)}
|
|
161
230
|
</div>
|
|
162
231
|
|
|
163
232
|
<div className="flex flex-wrap gap-1.5">
|
|
164
|
-
<Badge
|
|
165
|
-
|
|
233
|
+
<Badge
|
|
234
|
+
variant={customer.isEmailVerified ? "success" : "outline"}
|
|
235
|
+
>
|
|
236
|
+
{customer.isEmailVerified
|
|
237
|
+
? "Email verified"
|
|
238
|
+
: "Email unverified"}
|
|
239
|
+
</Badge>
|
|
240
|
+
<Badge
|
|
241
|
+
variant={customer.isPhoneVerified ? "success" : "outline"}
|
|
242
|
+
>
|
|
243
|
+
{customer.isPhoneVerified
|
|
244
|
+
? "Phone verified"
|
|
245
|
+
: "Phone unverified"}
|
|
246
|
+
</Badge>
|
|
166
247
|
</div>
|
|
167
248
|
</CardContent>
|
|
168
249
|
</Card>
|
|
@@ -175,7 +256,11 @@ export default function CustomerDetailPage() {
|
|
|
175
256
|
<Button
|
|
176
257
|
variant={customer.isActive ? "destructive" : "outline"}
|
|
177
258
|
disabled={!canStatus}
|
|
178
|
-
title={
|
|
259
|
+
title={
|
|
260
|
+
canStatus
|
|
261
|
+
? undefined
|
|
262
|
+
: missingPermissionHint(PERMISSIONS.customersStatus)
|
|
263
|
+
}
|
|
179
264
|
onClick={() => void toggleActive()}
|
|
180
265
|
>
|
|
181
266
|
{customer.isActive ? "Deactivate" : "Activate"}
|
|
@@ -183,7 +268,15 @@ export default function CustomerDetailPage() {
|
|
|
183
268
|
|
|
184
269
|
<Dialog open={deleteOpen} onOpenChange={setDeleteOpen}>
|
|
185
270
|
<DialogTrigger asChild>
|
|
186
|
-
<Button
|
|
271
|
+
<Button
|
|
272
|
+
variant="destructive"
|
|
273
|
+
disabled={!canManage}
|
|
274
|
+
title={
|
|
275
|
+
canManage
|
|
276
|
+
? undefined
|
|
277
|
+
: missingPermissionHint(PERMISSIONS.customersManage)
|
|
278
|
+
}
|
|
279
|
+
>
|
|
187
280
|
Delete customer
|
|
188
281
|
</Button>
|
|
189
282
|
</DialogTrigger>
|
|
@@ -191,14 +284,23 @@ export default function CustomerDetailPage() {
|
|
|
191
284
|
<DialogHeader>
|
|
192
285
|
<DialogTitle>Delete {customer.email}?</DialogTitle>
|
|
193
286
|
<DialogDescription>
|
|
194
|
-
This soft-deletes the customer: they stop appearing in
|
|
287
|
+
This soft-deletes the customer: they stop appearing in
|
|
288
|
+
listings, but the row is kept for audit purposes.
|
|
195
289
|
</DialogDescription>
|
|
196
290
|
</DialogHeader>
|
|
197
291
|
<DialogFooter>
|
|
198
|
-
<Button
|
|
292
|
+
<Button
|
|
293
|
+
variant="outline"
|
|
294
|
+
onClick={() => setDeleteOpen(false)}
|
|
295
|
+
disabled={deleting}
|
|
296
|
+
>
|
|
199
297
|
Cancel
|
|
200
298
|
</Button>
|
|
201
|
-
<Button
|
|
299
|
+
<Button
|
|
300
|
+
variant="destructive"
|
|
301
|
+
onClick={() => void handleDelete()}
|
|
302
|
+
disabled={deleting}
|
|
303
|
+
>
|
|
202
304
|
{deleting ? "Deleting…" : "Delete customer"}
|
|
203
305
|
</Button>
|
|
204
306
|
</DialogFooter>
|
package/registry/admin-apps/nextjs/variants/base/src/app/(console)/languages/[id]/edit/page.tsx
CHANGED
|
@@ -3,25 +3,48 @@
|
|
|
3
3
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
4
4
|
import { useAbility } from "@casl/react";
|
|
5
5
|
import { useParams, useRouter } from "next/navigation";
|
|
6
|
-
import { useCallback, useEffect, useState } from "react";
|
|
6
|
+
import { startTransition, useCallback, useEffect, useState } from "react";
|
|
7
7
|
import { useForm } from "react-hook-form";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
AuthApiError,
|
|
10
|
+
type LanguageSummary,
|
|
11
|
+
type UpdateLanguageInput,
|
|
12
|
+
} from "@simple-auth-kit/auth-client";
|
|
9
13
|
import { toast } from "sonner";
|
|
10
14
|
import { Breadcrumb } from "@/components/breadcrumb";
|
|
11
15
|
import { FormErrorAlert } from "@/components/form-error-alert";
|
|
12
16
|
import { PermissionRequired } from "@/components/permission-required";
|
|
13
17
|
import { Button } from "@/components/ui/button";
|
|
14
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
Card,
|
|
20
|
+
CardContent,
|
|
21
|
+
CardDescription,
|
|
22
|
+
CardFooter,
|
|
23
|
+
CardHeader,
|
|
24
|
+
CardTitle,
|
|
25
|
+
} from "@/components/ui/card";
|
|
15
26
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
16
27
|
import { Combobox } from "@/components/ui/combobox";
|
|
17
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
Form,
|
|
30
|
+
FormControl,
|
|
31
|
+
FormDescription,
|
|
32
|
+
FormField,
|
|
33
|
+
FormItem,
|
|
34
|
+
FormLabel,
|
|
35
|
+
FormMessage,
|
|
36
|
+
} from "@/components/ui/form";
|
|
18
37
|
import { Heading } from "@/components/ui/heading";
|
|
19
38
|
import { Input } from "@/components/ui/input";
|
|
20
39
|
import { Separator } from "@/components/ui/separator";
|
|
21
40
|
import { PERMISSIONS, hasPermission, type AppAbility } from "@/lib/ability";
|
|
22
41
|
import { authClient } from "@/lib/auth-client";
|
|
23
42
|
import { errorMessage, errorMessages } from "@/lib/error";
|
|
24
|
-
import {
|
|
43
|
+
import {
|
|
44
|
+
DIRECTION_OPTIONS,
|
|
45
|
+
languageSchema,
|
|
46
|
+
type LanguageFormValues,
|
|
47
|
+
} from "../../language-schema";
|
|
25
48
|
|
|
26
49
|
function apiErrorMessage(err: unknown, fallback: string): string {
|
|
27
50
|
return err instanceof AuthApiError ? err.message : fallback;
|
|
@@ -50,7 +73,14 @@ export default function EditLanguagePage() {
|
|
|
50
73
|
|
|
51
74
|
const form = useForm<LanguageFormValues>({
|
|
52
75
|
resolver: zodResolver(languageSchema),
|
|
53
|
-
defaultValues: {
|
|
76
|
+
defaultValues: {
|
|
77
|
+
code: "",
|
|
78
|
+
name: "",
|
|
79
|
+
nativeName: "",
|
|
80
|
+
direction: "ltr",
|
|
81
|
+
isDefault: false,
|
|
82
|
+
isActive: true,
|
|
83
|
+
},
|
|
54
84
|
});
|
|
55
85
|
|
|
56
86
|
const load = useCallback(async () => {
|
|
@@ -70,10 +100,16 @@ export default function EditLanguagePage() {
|
|
|
70
100
|
|
|
71
101
|
useEffect(() => {
|
|
72
102
|
if (!canManage) return;
|
|
73
|
-
void load();
|
|
103
|
+
startTransition(() => void load());
|
|
74
104
|
}, [canManage, load]);
|
|
75
105
|
|
|
76
|
-
if (!canManage)
|
|
106
|
+
if (!canManage)
|
|
107
|
+
return (
|
|
108
|
+
<PermissionRequired
|
|
109
|
+
permission={PERMISSIONS.languagesManage}
|
|
110
|
+
what="Editing a language"
|
|
111
|
+
/>
|
|
112
|
+
);
|
|
77
113
|
|
|
78
114
|
async function onSubmit(values: LanguageFormValues) {
|
|
79
115
|
if (!language) return;
|
|
@@ -112,7 +148,9 @@ export default function EditLanguagePage() {
|
|
|
112
148
|
</div>
|
|
113
149
|
<Separator />
|
|
114
150
|
|
|
115
|
-
{loading && !language &&
|
|
151
|
+
{loading && !language && (
|
|
152
|
+
<p className="text-sm text-muted-foreground">Loading…</p>
|
|
153
|
+
)}
|
|
116
154
|
|
|
117
155
|
{language && (
|
|
118
156
|
<Card>
|
|
@@ -120,16 +158,25 @@ export default function EditLanguagePage() {
|
|
|
120
158
|
<CardContent>
|
|
121
159
|
<FormErrorAlert messages={formError} />
|
|
122
160
|
<Form {...form}>
|
|
123
|
-
<form
|
|
161
|
+
<form
|
|
162
|
+
onSubmit={form.handleSubmit(onSubmit)}
|
|
163
|
+
className="space-y-8 w-full"
|
|
164
|
+
>
|
|
124
165
|
<Card className="w-full">
|
|
125
166
|
<CardHeader className="border-b bg-muted/50">
|
|
126
|
-
<CardTitle className="text-2xl">
|
|
127
|
-
|
|
167
|
+
<CardTitle className="text-2xl">
|
|
168
|
+
Language Information
|
|
169
|
+
</CardTitle>
|
|
170
|
+
<CardDescription className="text-base">
|
|
171
|
+
Enter language details
|
|
172
|
+
</CardDescription>
|
|
128
173
|
</CardHeader>
|
|
129
174
|
<CardContent className="pt-6">
|
|
130
175
|
<div className="space-y-6">
|
|
131
176
|
<div className="space-y-4">
|
|
132
|
-
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">
|
|
177
|
+
<h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide">
|
|
178
|
+
Basic Information
|
|
179
|
+
</h3>
|
|
133
180
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
134
181
|
<FormField
|
|
135
182
|
control={form.control}
|
|
@@ -157,7 +204,9 @@ export default function EditLanguagePage() {
|
|
|
157
204
|
<FormItem>
|
|
158
205
|
<FormLabel>
|
|
159
206
|
Language Code
|
|
160
|
-
<span className="text-xs text-destructive dark:text-destructive-foreground">
|
|
207
|
+
<span className="text-xs text-destructive dark:text-destructive-foreground">
|
|
208
|
+
(ISO code, e.g., en, ar, bn)
|
|
209
|
+
</span>
|
|
161
210
|
</FormLabel>
|
|
162
211
|
<FormControl>
|
|
163
212
|
<Input
|
|
@@ -167,7 +216,11 @@ export default function EditLanguagePage() {
|
|
|
167
216
|
type="text"
|
|
168
217
|
maxLength={10}
|
|
169
218
|
className="bg-background border-2 focus:border-purple-500 transition-colors lowercase"
|
|
170
|
-
onChange={(e) =>
|
|
219
|
+
onChange={(e) =>
|
|
220
|
+
field.onChange(
|
|
221
|
+
e.target.value.toLowerCase(),
|
|
222
|
+
)
|
|
223
|
+
}
|
|
171
224
|
/>
|
|
172
225
|
</FormControl>
|
|
173
226
|
<FormMessage />
|
|
@@ -181,7 +234,9 @@ export default function EditLanguagePage() {
|
|
|
181
234
|
<FormItem>
|
|
182
235
|
<FormLabel>
|
|
183
236
|
Native Name
|
|
184
|
-
<span className="text-xs text-muted-foreground">
|
|
237
|
+
<span className="text-xs text-muted-foreground">
|
|
238
|
+
(e.g., العربية, বাংলা)
|
|
239
|
+
</span>
|
|
185
240
|
</FormLabel>
|
|
186
241
|
<FormControl>
|
|
187
242
|
<Input
|
|
@@ -208,7 +263,9 @@ export default function EditLanguagePage() {
|
|
|
208
263
|
options={DIRECTION_OPTIONS}
|
|
209
264
|
selected={field.value}
|
|
210
265
|
placeholder="Select text direction"
|
|
211
|
-
onChange={(option) =>
|
|
266
|
+
onChange={(option) =>
|
|
267
|
+
field.onChange(option.value)
|
|
268
|
+
}
|
|
212
269
|
showCreate={false}
|
|
213
270
|
popoverClassName="min-w-[250px]"
|
|
214
271
|
/>
|
|
@@ -230,11 +287,21 @@ export default function EditLanguagePage() {
|
|
|
230
287
|
render={({ field }) => (
|
|
231
288
|
<FormItem className="flex flex-row items-center space-x-3 space-y-0 rounded-lg border border-purple-500 bg-purple-50 dark:bg-purple-950/20 p-4">
|
|
232
289
|
<FormControl>
|
|
233
|
-
<Checkbox
|
|
290
|
+
<Checkbox
|
|
291
|
+
checked={field.value}
|
|
292
|
+
onCheckedChange={(checked) =>
|
|
293
|
+
field.onChange(checked === true)
|
|
294
|
+
}
|
|
295
|
+
/>
|
|
234
296
|
</FormControl>
|
|
235
297
|
<div className="space-y-1 leading-none">
|
|
236
|
-
<FormLabel className="text-base font-medium">
|
|
237
|
-
|
|
298
|
+
<FormLabel className="text-base font-medium">
|
|
299
|
+
Default Language
|
|
300
|
+
</FormLabel>
|
|
301
|
+
<FormDescription className="text-sm">
|
|
302
|
+
The default language is the one a new
|
|
303
|
+
deployment's locale falls back to
|
|
304
|
+
</FormDescription>
|
|
238
305
|
</div>
|
|
239
306
|
</FormItem>
|
|
240
307
|
)}
|
|
@@ -245,11 +312,21 @@ export default function EditLanguagePage() {
|
|
|
245
312
|
render={({ field }) => (
|
|
246
313
|
<FormItem className="flex flex-row items-center space-x-3 space-y-0 rounded-lg border border-purple-500 bg-purple-50 dark:bg-purple-950/20 p-4">
|
|
247
314
|
<FormControl>
|
|
248
|
-
<Checkbox
|
|
315
|
+
<Checkbox
|
|
316
|
+
checked={field.value}
|
|
317
|
+
onCheckedChange={(checked) =>
|
|
318
|
+
field.onChange(checked === true)
|
|
319
|
+
}
|
|
320
|
+
/>
|
|
249
321
|
</FormControl>
|
|
250
322
|
<div className="space-y-1 leading-none">
|
|
251
|
-
<FormLabel className="text-base font-medium">
|
|
252
|
-
|
|
323
|
+
<FormLabel className="text-base font-medium">
|
|
324
|
+
Active Status
|
|
325
|
+
</FormLabel>
|
|
326
|
+
<FormDescription className="text-sm">
|
|
327
|
+
Language will be active and available for
|
|
328
|
+
selection
|
|
329
|
+
</FormDescription>
|
|
253
330
|
</div>
|
|
254
331
|
</FormItem>
|
|
255
332
|
)}
|
|
@@ -257,10 +334,19 @@ export default function EditLanguagePage() {
|
|
|
257
334
|
</div>
|
|
258
335
|
|
|
259
336
|
<CardFooter className="flex justify-center gap-4 mt-8 pb-8">
|
|
260
|
-
<Button
|
|
337
|
+
<Button
|
|
338
|
+
type="button"
|
|
339
|
+
variant="outline"
|
|
340
|
+
onClick={() => router.push(`/languages/${id}`)}
|
|
341
|
+
disabled={saving}
|
|
342
|
+
>
|
|
261
343
|
Cancel
|
|
262
344
|
</Button>
|
|
263
|
-
<Button
|
|
345
|
+
<Button
|
|
346
|
+
disabled={saving}
|
|
347
|
+
className="bg-purple-600 hover:bg-purple-700 dark:bg-purple-600 dark:hover:bg-purple-700 min-w-32"
|
|
348
|
+
type="submit"
|
|
349
|
+
>
|
|
264
350
|
{saving ? "Updating..." : "Update Language"}
|
|
265
351
|
</Button>
|
|
266
352
|
</CardFooter>
|