@arch-cadre/panel 0.0.1

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.
Files changed (97) hide show
  1. package/dist/actions/actions.cjs +18 -0
  2. package/dist/actions/actions.d.ts +4 -0
  3. package/dist/actions/actions.mjs +10 -0
  4. package/dist/actions/index.cjs +27 -0
  5. package/dist/actions/index.d.ts +2 -0
  6. package/dist/actions/index.mjs +2 -0
  7. package/dist/actions/manager.cjs +138 -0
  8. package/dist/actions/manager.d.ts +14 -0
  9. package/dist/actions/manager.mjs +135 -0
  10. package/dist/actions/profile.cjs +169 -0
  11. package/dist/actions/profile.d.ts +8 -0
  12. package/dist/actions/profile.mjs +135 -0
  13. package/dist/actions/settings.cjs +26 -0
  14. package/dist/actions/settings.d.ts +7 -0
  15. package/dist/actions/settings.mjs +20 -0
  16. package/dist/index.cjs +29 -0
  17. package/dist/index.d.ts +3 -0
  18. package/dist/index.mjs +25 -0
  19. package/dist/intl.d.ts +9 -0
  20. package/dist/navigation.cjs +38 -0
  21. package/dist/navigation.d.ts +2 -0
  22. package/dist/navigation.mjs +39 -0
  23. package/dist/routes.cjs +71 -0
  24. package/dist/routes.d.ts +4 -0
  25. package/dist/routes.mjs +71 -0
  26. package/dist/types.cjs +1 -0
  27. package/dist/types.d.ts +13 -0
  28. package/dist/types.mjs +0 -0
  29. package/dist/ui/[...catchAll]/page.cjs +127 -0
  30. package/dist/ui/[...catchAll]/page.d.ts +13 -0
  31. package/dist/ui/[...catchAll]/page.mjs +88 -0
  32. package/dist/ui/components/app-content.cjs +49 -0
  33. package/dist/ui/components/app-content.d.ts +6 -0
  34. package/dist/ui/components/app-content.mjs +34 -0
  35. package/dist/ui/components/app-header.cjs +55 -0
  36. package/dist/ui/components/app-header.d.ts +6 -0
  37. package/dist/ui/components/app-header.mjs +45 -0
  38. package/dist/ui/components/app-sidebar.cjs +133 -0
  39. package/dist/ui/components/app-sidebar.d.ts +17 -0
  40. package/dist/ui/components/app-sidebar.mjs +142 -0
  41. package/dist/ui/components/app-user.cjs +102 -0
  42. package/dist/ui/components/app-user.d.ts +2 -0
  43. package/dist/ui/components/app-user.mjs +92 -0
  44. package/dist/ui/components/breadcrumb-slot.cjs +35 -0
  45. package/dist/ui/components/breadcrumb-slot.d.ts +2 -0
  46. package/dist/ui/components/breadcrumb-slot.mjs +31 -0
  47. package/dist/ui/components/manager/module-card.cjs +213 -0
  48. package/dist/ui/components/manager/module-card.d.ts +12 -0
  49. package/dist/ui/components/manager/module-card.mjs +197 -0
  50. package/dist/ui/components/manager/module-list.cjs +52 -0
  51. package/dist/ui/components/manager/module-list.d.ts +4 -0
  52. package/dist/ui/components/manager/module-list.mjs +16 -0
  53. package/dist/ui/components/manager/module-upload.cjs +81 -0
  54. package/dist/ui/components/manager/module-upload.d.ts +2 -0
  55. package/dist/ui/components/manager/module-upload.mjs +68 -0
  56. package/dist/ui/components/profile/components.cjs +239 -0
  57. package/dist/ui/components/profile/components.d.ts +8 -0
  58. package/dist/ui/components/profile/components.mjs +219 -0
  59. package/dist/ui/components/profile/link.cjs +25 -0
  60. package/dist/ui/components/profile/link.d.ts +1 -0
  61. package/dist/ui/components/profile/link.mjs +13 -0
  62. package/dist/ui/components/profile/page.cjs +41 -0
  63. package/dist/ui/components/profile/page.d.ts +1 -0
  64. package/dist/ui/components/profile/page.mjs +21 -0
  65. package/dist/ui/components/sidebar-slot.cjs +49 -0
  66. package/dist/ui/components/sidebar-slot.d.ts +2 -0
  67. package/dist/ui/components/sidebar-slot.mjs +33 -0
  68. package/dist/ui/dashboard/page.cjs +31 -0
  69. package/dist/ui/dashboard/page.d.ts +2 -0
  70. package/dist/ui/dashboard/page.mjs +9 -0
  71. package/dist/ui/error.cjs +50 -0
  72. package/dist/ui/error.d.ts +7 -0
  73. package/dist/ui/error.mjs +35 -0
  74. package/dist/ui/layout.cjs +48 -0
  75. package/dist/ui/layout.d.ts +4 -0
  76. package/dist/ui/layout.mjs +35 -0
  77. package/dist/ui/modules/docs/page.cjs +98 -0
  78. package/dist/ui/modules/docs/page.d.ts +6 -0
  79. package/dist/ui/modules/docs/page.mjs +46 -0
  80. package/dist/ui/modules/page.cjs +30 -0
  81. package/dist/ui/modules/page.d.ts +2 -0
  82. package/dist/ui/modules/page.mjs +10 -0
  83. package/dist/ui/page.cjs +18 -0
  84. package/dist/ui/page.d.ts +1 -0
  85. package/dist/ui/page.mjs +9 -0
  86. package/dist/ui/router.cjs +61 -0
  87. package/dist/ui/router.d.ts +5 -0
  88. package/dist/ui/router.mjs +51 -0
  89. package/dist/ui/settings/page.cjs +61 -0
  90. package/dist/ui/settings/page.d.ts +2 -0
  91. package/dist/ui/settings/page.mjs +22 -0
  92. package/dist/ui/settings-page.cjs +76 -0
  93. package/dist/ui/settings-page.d.ts +2 -0
  94. package/dist/ui/settings-page.mjs +57 -0
  95. package/locales/en/global.json +80 -0
  96. package/manifest.json +11 -0
  97. package/package.json +67 -0
@@ -0,0 +1,2 @@
1
+ import * as React from "react";
2
+ export declare function ModuleUpload(): React.JSX.Element;
@@ -0,0 +1,68 @@
1
+ "use client";
2
+ import { useTranslation } from "@arch-cadre/intl";
3
+ import { Button } from "@arch-cadre/ui/components/button";
4
+ import { Loader2, Lock, Upload } from "lucide-react";
5
+ import * as React from "react";
6
+ import { useEffect, useRef, useState } from "react";
7
+ import { toast } from "sonner";
8
+ import {
9
+ checkDiskWriteAccess,
10
+ uploadModuleAction
11
+ } from "../../../actions/manager.mjs";
12
+ export function ModuleUpload() {
13
+ const [isUploading, setIsUploading] = useState(false);
14
+ const [canWrite, setCanWrite] = useState(null);
15
+ const fileInputRef = useRef(null);
16
+ const { t } = useTranslation();
17
+ useEffect(() => {
18
+ checkDiskWriteAccess().then((res) => setCanWrite(res.canWrite));
19
+ }, []);
20
+ const handleUpload = async (e) => {
21
+ const file = e.target.files?.[0];
22
+ if (!file) return;
23
+ if (!file.name.endsWith(".zip")) {
24
+ toast.error(t("Invalid file"));
25
+ return;
26
+ }
27
+ const formData = new FormData();
28
+ formData.append("file", file);
29
+ setIsUploading(true);
30
+ const id = toast.loading(t("Uploading..."));
31
+ try {
32
+ const result = await uploadModuleAction(formData);
33
+ if (result.success) {
34
+ toast.success(t("Upload successful"), { id });
35
+ if (fileInputRef.current) fileInputRef.current.value = "";
36
+ } else {
37
+ toast.error(result.error || t("Upload error"), { id });
38
+ }
39
+ } catch (_error) {
40
+ toast.error(t("error_occurred"), { id });
41
+ } finally {
42
+ setIsUploading(false);
43
+ }
44
+ };
45
+ return /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ React.createElement(
46
+ "input",
47
+ {
48
+ type: "file",
49
+ accept: ".zip",
50
+ className: "hidden",
51
+ ref: fileInputRef,
52
+ onChange: handleUpload,
53
+ disabled: isUploading
54
+ }
55
+ ), /* @__PURE__ */ React.createElement(
56
+ Button,
57
+ {
58
+ onClick: () => fileInputRef.current?.click(),
59
+ disabled: isUploading || canWrite === false,
60
+ variant: "outline",
61
+ size: "sm",
62
+ className: "gap-2 w-full sm:w-auto",
63
+ title: canWrite === false ? t("No write access") : void 0
64
+ },
65
+ isUploading ? /* @__PURE__ */ React.createElement(Loader2, { className: "size-4 animate-spin" }) : canWrite === false ? /* @__PURE__ */ React.createElement(Lock, { className: "size-4 text-destructive" }) : /* @__PURE__ */ React.createElement(Upload, { className: "size-4" }),
66
+ canWrite === false ? t("Upload disabled") : t("Upload module")
67
+ ));
68
+ }
@@ -0,0 +1,239 @@
1
+ "use strict";
2
+ "use client";
3
+
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.UpdateEmailForm = UpdateEmailForm;
8
+ exports.UpdatePasswordForm = UpdatePasswordForm;
9
+ exports.UpdateProfileForm = UpdateProfileForm;
10
+ var _intl = require("@arch-cadre/intl");
11
+ var _ui = require("@arch-cadre/ui");
12
+ var _avatar = require("@arch-cadre/ui/components/avatar");
13
+ var _button = require("@arch-cadre/ui/components/button");
14
+ var _input = require("@arch-cadre/ui/components/input");
15
+ var _label = require("@arch-cadre/ui/components/label");
16
+ var _react = require("react");
17
+ var _profile = require("../../../actions/profile.cjs");
18
+ function UpdateProfileForm({
19
+ user
20
+ }) {
21
+ const {
22
+ t
23
+ } = (0, _intl.useTranslation)();
24
+ const [name, setName] = (0, _react.useState)(user.name);
25
+ const [isPending, startTransition] = (0, _react.useTransition)();
26
+ const [image, setImage] = (0, _react.useState)(user.image);
27
+ const [avatar, setAvatar] = (0, _react.useState)(void 0);
28
+ const fileInputRef = (0, _react.useRef)(null);
29
+ const handleFileChange = e => {
30
+ const file = e.target.files?.[0];
31
+ if (file) {
32
+ setAvatar(file);
33
+ const reader = new FileReader();
34
+ reader.onload = event => {
35
+ setImage(event.target?.result);
36
+ };
37
+ reader.readAsDataURL(file);
38
+ }
39
+ };
40
+ const triggerFileInput = () => {
41
+ fileInputRef.current?.click();
42
+ };
43
+ const handleSubmit = async e => {
44
+ e.preventDefault();
45
+ if (!name.trim()) {
46
+ _ui.toast.error(t("Name is required"));
47
+ return;
48
+ }
49
+ startTransition(async () => {
50
+ const result = await (0, _profile.updateProfileAction)(name.trim(), avatar);
51
+ if (result.error) {
52
+ _ui.toast.error(result.error);
53
+ } else {
54
+ _ui.toast.success(result.message);
55
+ }
56
+ });
57
+ };
58
+ return /* @__PURE__ */React.createElement("form", {
59
+ onSubmit: handleSubmit,
60
+ className: "space-y-4"
61
+ }, /* @__PURE__ */React.createElement("div", {
62
+ className: "grid grid-cols-12"
63
+ }, /* @__PURE__ */React.createElement("div", {
64
+ className: "col-span-3 flex items-center flex-col"
65
+ }, /* @__PURE__ */React.createElement("div", {
66
+ className: "relative mb-2"
67
+ }, /* @__PURE__ */React.createElement(_avatar.Avatar, {
68
+ className: "h-24 w-24 border-2 border-muted"
69
+ }, /* @__PURE__ */React.createElement(_avatar.AvatarImage, {
70
+ src: image || void 0,
71
+ alt: name
72
+ }), /* @__PURE__ */React.createElement(_avatar.AvatarFallback, {
73
+ className: "text-3xl"
74
+ }, name.charAt(0))), /* @__PURE__ */React.createElement(_button.Button, {
75
+ type: "button",
76
+ variant: "ghost",
77
+ size: "icon",
78
+ className: "absolute -top-0.5 -right-0.5 bg-accent rounded-full border-[3px] border-background h-8 w-8 hover:bg-accent",
79
+ onClick: () => {
80
+ if (image) {
81
+ setImage(null);
82
+ if (fileInputRef.current) {
83
+ fileInputRef.current.value = "";
84
+ }
85
+ } else {
86
+ triggerFileInput();
87
+ }
88
+ }
89
+ }, image ? /* @__PURE__ */React.createElement(_ui.Icon, {
90
+ icon: "solar:trash-bin-trash-broken",
91
+ className: "h-4 w-4 text-muted-foreground"
92
+ }) : /* @__PURE__ */React.createElement(_ui.Icon, {
93
+ icon: "solar:add-circle-broken",
94
+ className: "h-3 w-3 text-muted-foreground"
95
+ }), /* @__PURE__ */React.createElement("span", {
96
+ className: "sr-only"
97
+ }, image ? t("Remove image") : t("Upload image")))), /* @__PURE__ */React.createElement("p", {
98
+ className: "text-center font-medium"
99
+ }, t("Upload Image")), /* @__PURE__ */React.createElement("p", {
100
+ className: "text-center text-sm text-muted-foreground"
101
+ }, t("Max file size: 1MB")), /* @__PURE__ */React.createElement("input", {
102
+ type: "file",
103
+ ref: fileInputRef,
104
+ onChange: handleFileChange,
105
+ accept: "image/*",
106
+ className: "hidden"
107
+ })), /* @__PURE__ */React.createElement("div", {
108
+ className: "col-span-9 space-y-4"
109
+ }, /* @__PURE__ */React.createElement("div", {
110
+ className: "space-y-2"
111
+ }, /* @__PURE__ */React.createElement(_label.Label, {
112
+ htmlFor: "name"
113
+ }, t("Display Name")), /* @__PURE__ */React.createElement(_input.Input, {
114
+ id: "name",
115
+ value: name,
116
+ onChange: e => setName(e.target.value),
117
+ placeholder: t("Your name"),
118
+ disabled: isPending
119
+ })))), /* @__PURE__ */React.createElement(_button.Button, {
120
+ type: "submit",
121
+ disabled: isPending
122
+ }, isPending ? /* @__PURE__ */React.createElement(React.Fragment, null, /* @__PURE__ */React.createElement(_ui.Icon, {
123
+ icon: "solar:refresh-broken",
124
+ className: "h-4 w-4 mr-2 animate-spin"
125
+ }), t("Saving...")) : /* @__PURE__ */React.createElement(React.Fragment, null, /* @__PURE__ */React.createElement(_ui.Icon, {
126
+ icon: "solar:diskette-broken",
127
+ className: "h-4 w-4 mr-2"
128
+ }), t("Save Changes"))));
129
+ }
130
+ const initialUpdatePasswordState = {
131
+ message: "",
132
+ error: false
133
+ };
134
+ function UpdatePasswordForm() {
135
+ const [state, action, isPending] = (0, _react.useActionState)(_profile.updatePasswordAction, initialUpdatePasswordState);
136
+ const {
137
+ t
138
+ } = (0, _intl.useTranslation)();
139
+ const [currentPassword, setCurrentPassword] = (0, _react.useState)("");
140
+ const [newPassword, setNewPassword] = (0, _react.useState)("");
141
+ const [confirmPassword, setConfirmPassword] = (0, _react.useState)("");
142
+ return /* @__PURE__ */React.createElement("form", {
143
+ action,
144
+ className: "space-y-4"
145
+ }, /* @__PURE__ */React.createElement("div", {
146
+ className: "space-y-2"
147
+ }, /* @__PURE__ */React.createElement(_label.Label, {
148
+ htmlFor: "current-password"
149
+ }, t("Current Password")), /* @__PURE__ */React.createElement(_input.Input, {
150
+ type: "password",
151
+ id: "current-password",
152
+ name: "password",
153
+ value: currentPassword,
154
+ onChange: e => setCurrentPassword(e.target.value),
155
+ autoComplete: "current-password",
156
+ placeholder: t("Enter current password"),
157
+ required: true,
158
+ disabled: isPending
159
+ })), /* @__PURE__ */React.createElement("div", {
160
+ className: "space-y-2"
161
+ }, /* @__PURE__ */React.createElement(_label.Label, {
162
+ htmlFor: "new-password"
163
+ }, t("New Password")), /* @__PURE__ */React.createElement(_input.Input, {
164
+ type: "password",
165
+ id: "new-password",
166
+ name: "new_password",
167
+ value: newPassword,
168
+ onChange: e => setNewPassword(e.target.value),
169
+ autoComplete: "new-password",
170
+ placeholder: t("Enter new password"),
171
+ required: true,
172
+ disabled: isPending
173
+ })), /* @__PURE__ */React.createElement("div", {
174
+ className: "space-y-2"
175
+ }, /* @__PURE__ */React.createElement(_label.Label, {
176
+ htmlFor: "confirm-password"
177
+ }, t("Confirm New Password")), /* @__PURE__ */React.createElement(_input.Input, {
178
+ type: "password",
179
+ id: "confirm-password",
180
+ value: confirmPassword,
181
+ onChange: e => setConfirmPassword(e.target.value),
182
+ autoComplete: "new-password",
183
+ placeholder: t("Confirm new password"),
184
+ required: true,
185
+ disabled: isPending
186
+ })), state.message && /* @__PURE__ */React.createElement("p", {
187
+ className: (0, _ui.cn)("text-sm", state.error ? "text-destructive" : "text-green-600")
188
+ }, state.message), /* @__PURE__ */React.createElement(_button.Button, {
189
+ type: "submit",
190
+ disabled: isPending
191
+ }, isPending ? /* @__PURE__ */React.createElement(React.Fragment, null, /* @__PURE__ */React.createElement(_ui.Icon, {
192
+ icon: "solar:refresh-broken",
193
+ className: "h-4 w-4 mr-2 animate-spin"
194
+ }), t("Updating...")) : /* @__PURE__ */React.createElement(React.Fragment, null, /* @__PURE__ */React.createElement(_ui.Icon, {
195
+ icon: "solar:key-broken",
196
+ className: "h-4 w-4 mr-2"
197
+ }), t("Update Password"))));
198
+ }
199
+ const initialUpdateEmailState = {
200
+ message: "",
201
+ error: false
202
+ };
203
+ function UpdateEmailForm({
204
+ user
205
+ }) {
206
+ const [state, action, isPending] = (0, _react.useActionState)(_profile.updateEmailAction, initialUpdateEmailState);
207
+ const {
208
+ t
209
+ } = (0, _intl.useTranslation)();
210
+ const [email, setEmail] = (0, _react.useState)(user.email);
211
+ return /* @__PURE__ */React.createElement("form", {
212
+ action,
213
+ className: "space-y-4"
214
+ }, /* @__PURE__ */React.createElement("div", {
215
+ className: "space-y-2"
216
+ }, /* @__PURE__ */React.createElement(_label.Label, {
217
+ htmlFor: "new-email"
218
+ }, t("New Email Address")), /* @__PURE__ */React.createElement(_input.Input, {
219
+ type: "email",
220
+ id: "new-email",
221
+ name: "email",
222
+ value: email,
223
+ onChange: e => setEmail(e.target.value),
224
+ placeholder: t("Enter new email address"),
225
+ required: true,
226
+ disabled: isPending
227
+ })), state.message && /* @__PURE__ */React.createElement("p", {
228
+ className: (0, _ui.cn)("text-sm", state.error ? "text-destructive" : "text-green-600")
229
+ }, state.message), /* @__PURE__ */React.createElement(_button.Button, {
230
+ type: "submit",
231
+ disabled: isPending
232
+ }, isPending ? /* @__PURE__ */React.createElement(React.Fragment, null, /* @__PURE__ */React.createElement(_ui.Icon, {
233
+ icon: "solar:refresh-broken",
234
+ className: "h-4 w-4 mr-2 animate-spin"
235
+ }), t("Updating...")) : /* @__PURE__ */React.createElement(React.Fragment, null, /* @__PURE__ */React.createElement(_ui.Icon, {
236
+ icon: "solar:letter-broken",
237
+ className: "h-4 w-4 mr-2"
238
+ }), t("Update Email"))));
239
+ }
@@ -0,0 +1,8 @@
1
+ import type { FullUser } from "@arch-cadre/core";
2
+ export declare function UpdateProfileForm({ user }: {
3
+ user: FullUser;
4
+ }): import("react").JSX.Element;
5
+ export declare function UpdatePasswordForm(): import("react").JSX.Element;
6
+ export declare function UpdateEmailForm({ user }: {
7
+ user: FullUser;
8
+ }): import("react").JSX.Element;
@@ -0,0 +1,219 @@
1
+ "use client";
2
+ import { useTranslation } from "@arch-cadre/intl";
3
+ import { cn, Icon, toast } from "@arch-cadre/ui";
4
+ import {
5
+ Avatar,
6
+ AvatarFallback,
7
+ AvatarImage
8
+ } from "@arch-cadre/ui/components/avatar";
9
+ import { Button } from "@arch-cadre/ui/components/button";
10
+ import { Input } from "@arch-cadre/ui/components/input";
11
+ import { Label } from "@arch-cadre/ui/components/label";
12
+ import { useActionState, useRef, useState, useTransition } from "react";
13
+ import {
14
+ updateEmailAction,
15
+ updatePasswordAction,
16
+ updateProfileAction
17
+ } from "../../../actions/profile.mjs";
18
+ export function UpdateProfileForm({ user }) {
19
+ const { t } = useTranslation();
20
+ const [name, setName] = useState(user.name);
21
+ const [isPending, startTransition] = useTransition();
22
+ const [image, setImage] = useState(user.image);
23
+ const [avatar, setAvatar] = useState(void 0);
24
+ const fileInputRef = useRef(null);
25
+ const handleFileChange = (e) => {
26
+ const file = e.target.files?.[0];
27
+ if (file) {
28
+ setAvatar(file);
29
+ const reader = new FileReader();
30
+ reader.onload = (event) => {
31
+ setImage(event.target?.result);
32
+ };
33
+ reader.readAsDataURL(file);
34
+ }
35
+ };
36
+ const triggerFileInput = () => {
37
+ fileInputRef.current?.click();
38
+ };
39
+ const handleSubmit = async (e) => {
40
+ e.preventDefault();
41
+ if (!name.trim()) {
42
+ toast.error(t("Name is required"));
43
+ return;
44
+ }
45
+ startTransition(async () => {
46
+ const result = await updateProfileAction(name.trim(), avatar);
47
+ if (result.error) {
48
+ toast.error(result.error);
49
+ } else {
50
+ toast.success(result.message);
51
+ }
52
+ });
53
+ };
54
+ return /* @__PURE__ */ React.createElement("form", { onSubmit: handleSubmit, className: "space-y-4" }, /* @__PURE__ */ React.createElement("div", { className: "grid grid-cols-12" }, /* @__PURE__ */ React.createElement("div", { className: "col-span-3 flex items-center flex-col" }, /* @__PURE__ */ React.createElement("div", { className: "relative mb-2" }, /* @__PURE__ */ React.createElement(Avatar, { className: "h-24 w-24 border-2 border-muted" }, /* @__PURE__ */ React.createElement(AvatarImage, { src: image || void 0, alt: name }), /* @__PURE__ */ React.createElement(AvatarFallback, { className: "text-3xl" }, name.charAt(0))), /* @__PURE__ */ React.createElement(
55
+ Button,
56
+ {
57
+ type: "button",
58
+ variant: "ghost",
59
+ size: "icon",
60
+ className: "absolute -top-0.5 -right-0.5 bg-accent rounded-full border-[3px] border-background h-8 w-8 hover:bg-accent",
61
+ onClick: () => {
62
+ if (image) {
63
+ setImage(null);
64
+ if (fileInputRef.current) {
65
+ fileInputRef.current.value = "";
66
+ }
67
+ } else {
68
+ triggerFileInput();
69
+ }
70
+ }
71
+ },
72
+ image ? /* @__PURE__ */ React.createElement(
73
+ Icon,
74
+ {
75
+ icon: "solar:trash-bin-trash-broken",
76
+ className: "h-4 w-4 text-muted-foreground"
77
+ }
78
+ ) : /* @__PURE__ */ React.createElement(
79
+ Icon,
80
+ {
81
+ icon: "solar:add-circle-broken",
82
+ className: "h-3 w-3 text-muted-foreground"
83
+ }
84
+ ),
85
+ /* @__PURE__ */ React.createElement("span", { className: "sr-only" }, image ? t("Remove image") : t("Upload image"))
86
+ )), /* @__PURE__ */ React.createElement("p", { className: "text-center font-medium" }, t("Upload Image")), /* @__PURE__ */ React.createElement("p", { className: "text-center text-sm text-muted-foreground" }, t("Max file size: 1MB")), /* @__PURE__ */ React.createElement(
87
+ "input",
88
+ {
89
+ type: "file",
90
+ ref: fileInputRef,
91
+ onChange: handleFileChange,
92
+ accept: "image/*",
93
+ className: "hidden"
94
+ }
95
+ )), /* @__PURE__ */ React.createElement("div", { className: "col-span-9 space-y-4" }, /* @__PURE__ */ React.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React.createElement(Label, { htmlFor: "name" }, t("Display Name")), /* @__PURE__ */ React.createElement(
96
+ Input,
97
+ {
98
+ id: "name",
99
+ value: name,
100
+ onChange: (e) => setName(e.target.value),
101
+ placeholder: t("Your name"),
102
+ disabled: isPending
103
+ }
104
+ )))), /* @__PURE__ */ React.createElement(Button, { type: "submit", disabled: isPending }, isPending ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
105
+ Icon,
106
+ {
107
+ icon: "solar:refresh-broken",
108
+ className: "h-4 w-4 mr-2 animate-spin"
109
+ }
110
+ ), t("Saving...")) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Icon, { icon: "solar:diskette-broken", className: "h-4 w-4 mr-2" }), t("Save Changes"))));
111
+ }
112
+ const initialUpdatePasswordState = {
113
+ message: "",
114
+ error: false
115
+ };
116
+ export function UpdatePasswordForm() {
117
+ const [state, action, isPending] = useActionState(
118
+ updatePasswordAction,
119
+ initialUpdatePasswordState
120
+ );
121
+ const { t } = useTranslation();
122
+ const [currentPassword, setCurrentPassword] = useState("");
123
+ const [newPassword, setNewPassword] = useState("");
124
+ const [confirmPassword, setConfirmPassword] = useState("");
125
+ return /* @__PURE__ */ React.createElement("form", { action, className: "space-y-4" }, /* @__PURE__ */ React.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React.createElement(Label, { htmlFor: "current-password" }, t("Current Password")), /* @__PURE__ */ React.createElement(
126
+ Input,
127
+ {
128
+ type: "password",
129
+ id: "current-password",
130
+ name: "password",
131
+ value: currentPassword,
132
+ onChange: (e) => setCurrentPassword(e.target.value),
133
+ autoComplete: "current-password",
134
+ placeholder: t("Enter current password"),
135
+ required: true,
136
+ disabled: isPending
137
+ }
138
+ )), /* @__PURE__ */ React.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React.createElement(Label, { htmlFor: "new-password" }, t("New Password")), /* @__PURE__ */ React.createElement(
139
+ Input,
140
+ {
141
+ type: "password",
142
+ id: "new-password",
143
+ name: "new_password",
144
+ value: newPassword,
145
+ onChange: (e) => setNewPassword(e.target.value),
146
+ autoComplete: "new-password",
147
+ placeholder: t("Enter new password"),
148
+ required: true,
149
+ disabled: isPending
150
+ }
151
+ )), /* @__PURE__ */ React.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React.createElement(Label, { htmlFor: "confirm-password" }, t("Confirm New Password")), /* @__PURE__ */ React.createElement(
152
+ Input,
153
+ {
154
+ type: "password",
155
+ id: "confirm-password",
156
+ value: confirmPassword,
157
+ onChange: (e) => setConfirmPassword(e.target.value),
158
+ autoComplete: "new-password",
159
+ placeholder: t("Confirm new password"),
160
+ required: true,
161
+ disabled: isPending
162
+ }
163
+ )), state.message && /* @__PURE__ */ React.createElement(
164
+ "p",
165
+ {
166
+ className: cn(
167
+ "text-sm",
168
+ state.error ? "text-destructive" : "text-green-600"
169
+ )
170
+ },
171
+ state.message
172
+ ), /* @__PURE__ */ React.createElement(Button, { type: "submit", disabled: isPending }, isPending ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
173
+ Icon,
174
+ {
175
+ icon: "solar:refresh-broken",
176
+ className: "h-4 w-4 mr-2 animate-spin"
177
+ }
178
+ ), t("Updating...")) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Icon, { icon: "solar:key-broken", className: "h-4 w-4 mr-2" }), t("Update Password"))));
179
+ }
180
+ const initialUpdateEmailState = {
181
+ message: "",
182
+ error: false
183
+ };
184
+ export function UpdateEmailForm({ user }) {
185
+ const [state, action, isPending] = useActionState(
186
+ updateEmailAction,
187
+ initialUpdateEmailState
188
+ );
189
+ const { t } = useTranslation();
190
+ const [email, setEmail] = useState(user.email);
191
+ return /* @__PURE__ */ React.createElement("form", { action, className: "space-y-4" }, /* @__PURE__ */ React.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React.createElement(Label, { htmlFor: "new-email" }, t("New Email Address")), /* @__PURE__ */ React.createElement(
192
+ Input,
193
+ {
194
+ type: "email",
195
+ id: "new-email",
196
+ name: "email",
197
+ value: email,
198
+ onChange: (e) => setEmail(e.target.value),
199
+ placeholder: t("Enter new email address"),
200
+ required: true,
201
+ disabled: isPending
202
+ }
203
+ )), state.message && /* @__PURE__ */ React.createElement(
204
+ "p",
205
+ {
206
+ className: cn(
207
+ "text-sm",
208
+ state.error ? "text-destructive" : "text-green-600"
209
+ )
210
+ },
211
+ state.message
212
+ ), /* @__PURE__ */ React.createElement(Button, { type: "submit", disabled: isPending }, isPending ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
213
+ Icon,
214
+ {
215
+ icon: "solar:refresh-broken",
216
+ className: "h-4 w-4 mr-2 animate-spin"
217
+ }
218
+ ), t("Updating...")) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Icon, { icon: "solar:letter-broken", className: "h-4 w-4 mr-2" }), t("Update Email"))));
219
+ }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ "use client";
3
+
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.UserDropdownLink = UserDropdownLink;
8
+ var _intl = require("@arch-cadre/intl");
9
+ var _ui = require("@arch-cadre/ui");
10
+ var _dropdownMenu = require("@arch-cadre/ui/components/dropdown-menu");
11
+ var _link = _interopRequireDefault(require("next/link"));
12
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
+ function UserDropdownLink() {
14
+ const root = "/kryo";
15
+ const {
16
+ t
17
+ } = (0, _intl.useTranslation)();
18
+ return /* @__PURE__ */React.createElement(React.Fragment, null, /* @__PURE__ */React.createElement(_dropdownMenu.DropdownMenuGroup, null, /* @__PURE__ */React.createElement(_link.default, {
19
+ href: `${root}/profile`
20
+ }, /* @__PURE__ */React.createElement(_dropdownMenu.DropdownMenuItem, {
21
+ className: "cursor-pointer "
22
+ }, /* @__PURE__ */React.createElement(_ui.Icon, {
23
+ icon: "solar:user-circle-broken"
24
+ }), t("Profile Settings")))));
25
+ }
@@ -0,0 +1 @@
1
+ export declare function UserDropdownLink(): import("react").JSX.Element;
@@ -0,0 +1,13 @@
1
+ "use client";
2
+ import { useTranslation } from "@arch-cadre/intl";
3
+ import { Icon } from "@arch-cadre/ui";
4
+ import {
5
+ DropdownMenuGroup,
6
+ DropdownMenuItem
7
+ } from "@arch-cadre/ui/components/dropdown-menu";
8
+ import Link from "next/link";
9
+ export function UserDropdownLink() {
10
+ const root = "/kryo";
11
+ const { t } = useTranslation();
12
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(DropdownMenuGroup, null, /* @__PURE__ */ React.createElement(Link, { href: `${root}/profile` }, /* @__PURE__ */ React.createElement(DropdownMenuItem, { className: "cursor-pointer " }, /* @__PURE__ */ React.createElement(Icon, { icon: "solar:user-circle-broken" }), t("Profile Settings")))));
13
+ }
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ module.exports = ProfileSettingsPage;
7
+ var _server = require("@arch-cadre/core/server");
8
+ var _server2 = require("@arch-cadre/intl/server");
9
+ var _modules = require("@arch-cadre/modules");
10
+ var _navigation = require("next/navigation");
11
+ var _components = require("./components.cjs");
12
+ async function ProfileSettingsPage() {
13
+ const {
14
+ session,
15
+ user
16
+ } = await (0, _server.getCurrentSession)();
17
+ const {
18
+ t
19
+ } = await (0, _server2.getTranslation)();
20
+ if (session === null || user === null) {
21
+ return (0, _navigation.redirect)("/signin");
22
+ }
23
+ const security = await (0, _server.checkSecurity)(session, user);
24
+ if (!security.satisfied && security.redirect) {
25
+ return (0, _navigation.redirect)(security.redirect);
26
+ }
27
+ return /* @__PURE__ */React.createElement("div", {
28
+ className: "space-y-6 max-w-2xl"
29
+ }, /* @__PURE__ */React.createElement("div", null, /* @__PURE__ */React.createElement("h2", {
30
+ className: "text-2xl font-bold"
31
+ }, t("Profile Settings")), /* @__PURE__ */React.createElement("p", {
32
+ className: "text-muted-foreground"
33
+ }, t("Manage your account settings and profile information."))), /* @__PURE__ */React.createElement(_components.UpdateProfileForm, {
34
+ user
35
+ }), /* @__PURE__ */React.createElement(_components.UpdateEmailForm, {
36
+ user
37
+ }), /* @__PURE__ */React.createElement(_components.UpdatePasswordForm, null), /* @__PURE__ */React.createElement(_modules.ExtensionPoint, {
38
+ module: "user-profile",
39
+ point: "settings:extra-sections"
40
+ }));
41
+ }
@@ -0,0 +1 @@
1
+ export default function ProfileSettingsPage(): Promise<import("react").JSX.Element>;
@@ -0,0 +1,21 @@
1
+ import { checkSecurity, getCurrentSession } from "@arch-cadre/core/server";
2
+ import { getTranslation } from "@arch-cadre/intl/server";
3
+ import { ExtensionPoint } from "@arch-cadre/modules";
4
+ import { redirect } from "next/navigation";
5
+ import {
6
+ UpdateEmailForm,
7
+ UpdatePasswordForm,
8
+ UpdateProfileForm
9
+ } from "./components.mjs";
10
+ export default async function ProfileSettingsPage() {
11
+ const { session, user } = await getCurrentSession();
12
+ const { t } = await getTranslation();
13
+ if (session === null || user === null) {
14
+ return redirect("/signin");
15
+ }
16
+ const security = await checkSecurity(session, user);
17
+ if (!security.satisfied && security.redirect) {
18
+ return redirect(security.redirect);
19
+ }
20
+ return /* @__PURE__ */ React.createElement("div", { className: "space-y-6 max-w-2xl" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", { className: "text-2xl font-bold" }, t("Profile Settings")), /* @__PURE__ */ React.createElement("p", { className: "text-muted-foreground" }, t("Manage your account settings and profile information."))), /* @__PURE__ */ React.createElement(UpdateProfileForm, { user }), /* @__PURE__ */ React.createElement(UpdateEmailForm, { user }), /* @__PURE__ */ React.createElement(UpdatePasswordForm, null), /* @__PURE__ */ React.createElement(ExtensionPoint, { module: "user-profile", point: "settings:extra-sections" }));
21
+ }