@pelatform/starter.shared 0.1.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 +5 -0
- package/dist/index.d.ts +166 -0
- package/dist/index.js +993 -0
- package/dist/style.css +1 -0
- package/package.json +71 -0
package/README.md
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ComponentProps, ReactNode, PropsWithChildren } from 'react';
|
|
3
|
+
import { Avatar, buttonVariants, Dialog, Card, Button, Input } from 'pelatform-ui/default';
|
|
4
|
+
import { User, Workspace, ApiKey } from '@pelatform/starter.utils';
|
|
5
|
+
import { LucideIcon } from 'lucide-react';
|
|
6
|
+
import { LanguageSwitcherProps } from 'pelatform-ui/components';
|
|
7
|
+
export { Logo as LogoDefault } from 'pelatform-ui/components';
|
|
8
|
+
|
|
9
|
+
type AvatarClassNames = {
|
|
10
|
+
base?: string;
|
|
11
|
+
fallback?: string;
|
|
12
|
+
fallbackIcon?: string;
|
|
13
|
+
image?: string;
|
|
14
|
+
skeleton?: string;
|
|
15
|
+
};
|
|
16
|
+
interface AvatarProps extends ComponentProps<typeof Avatar> {
|
|
17
|
+
className?: string;
|
|
18
|
+
classNames?: AvatarClassNames;
|
|
19
|
+
image?: string;
|
|
20
|
+
isPending?: boolean;
|
|
21
|
+
size?: NonNullable<Parameters<typeof buttonVariants>[0]>["size"] | null | undefined;
|
|
22
|
+
user?: Partial<User> | null;
|
|
23
|
+
workspace?: Partial<Workspace> | null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type DialogClassNames = {
|
|
27
|
+
content?: string;
|
|
28
|
+
header?: string;
|
|
29
|
+
footer?: string;
|
|
30
|
+
};
|
|
31
|
+
interface DialogComponentProps extends ComponentProps<typeof Dialog> {
|
|
32
|
+
className?: string;
|
|
33
|
+
children?: ReactNode;
|
|
34
|
+
classNames?: CardClassNames;
|
|
35
|
+
title?: string;
|
|
36
|
+
description?: string;
|
|
37
|
+
disableFooter?: boolean;
|
|
38
|
+
cancelButton?: boolean;
|
|
39
|
+
cancelButtonDisabled?: boolean;
|
|
40
|
+
button?: ReactNode;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type CardClassNames = {
|
|
44
|
+
base?: string;
|
|
45
|
+
cell?: string;
|
|
46
|
+
content?: string;
|
|
47
|
+
header?: string;
|
|
48
|
+
footer?: string;
|
|
49
|
+
grid?: string;
|
|
50
|
+
skeleton?: string;
|
|
51
|
+
title?: string;
|
|
52
|
+
description?: string;
|
|
53
|
+
instructions?: string;
|
|
54
|
+
error?: string;
|
|
55
|
+
label?: string;
|
|
56
|
+
input?: string;
|
|
57
|
+
checkbox?: string;
|
|
58
|
+
icon?: string;
|
|
59
|
+
button?: string;
|
|
60
|
+
primaryButton?: string;
|
|
61
|
+
secondaryButton?: string;
|
|
62
|
+
outlineButton?: string;
|
|
63
|
+
destructiveButton?: string;
|
|
64
|
+
avatar?: AvatarClassNames;
|
|
65
|
+
dialog?: DialogClassNames;
|
|
66
|
+
};
|
|
67
|
+
interface CardComponentProps extends Omit<ComponentProps<typeof Card>, "title" | "variant"> {
|
|
68
|
+
className?: string;
|
|
69
|
+
children?: ReactNode;
|
|
70
|
+
classNames?: CardClassNames;
|
|
71
|
+
title?: ReactNode;
|
|
72
|
+
description?: ReactNode;
|
|
73
|
+
instructions?: ReactNode;
|
|
74
|
+
actionLabel?: ReactNode;
|
|
75
|
+
action?: () => Promise<unknown> | unknown;
|
|
76
|
+
disabled?: boolean;
|
|
77
|
+
isDestructive?: boolean;
|
|
78
|
+
isPending?: boolean;
|
|
79
|
+
isSubmitting?: boolean;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
declare function CardComponent({ children, className, classNames, title, description, instructions, actionLabel, action, disabled, isDestructive, isPending, isSubmitting, ...props }: CardComponentProps): react_jsx_runtime.JSX.Element;
|
|
83
|
+
declare function CardHeaderComponent({ className, classNames, title, description, isPending, }: CardComponentProps): react_jsx_runtime.JSX.Element;
|
|
84
|
+
declare function CardFooterComponent({ className, classNames, instructions, actionLabel, action, disabled, isDestructive, isPending, isSubmitting, }: CardComponentProps): react_jsx_runtime.JSX.Element;
|
|
85
|
+
declare function CardActionComponent({ classNames, actionLabel, onClick, disabled, isDestructive, isSubmitting, ...props }: CardComponentProps & ComponentProps<typeof Button>): react_jsx_runtime.JSX.Element;
|
|
86
|
+
|
|
87
|
+
declare function DialogComponent({ children, classNames, onOpenChange, title, description, disableFooter, cancelButton, cancelButtonDisabled, button, ...props }: DialogComponentProps): react_jsx_runtime.JSX.Element;
|
|
88
|
+
declare function DialogFooterComponent({ className, classNames, onOpenChange, cancelButton, cancelButtonDisabled, button, }: DialogComponentProps): react_jsx_runtime.JSX.Element;
|
|
89
|
+
|
|
90
|
+
declare function SkeletonViewComponent({ classNames }: {
|
|
91
|
+
classNames?: CardClassNames;
|
|
92
|
+
}): react_jsx_runtime.JSX.Element;
|
|
93
|
+
declare function SkeletonInputComponent({ classNames }: {
|
|
94
|
+
classNames?: CardClassNames;
|
|
95
|
+
}): react_jsx_runtime.JSX.Element;
|
|
96
|
+
|
|
97
|
+
declare function UserAvatar({ className, classNames, image, isPending, size, user, ...props }: AvatarProps): react_jsx_runtime.JSX.Element;
|
|
98
|
+
declare function WorkspaceLogo({ className, classNames, image, isPending, size, workspace, ...props }: AvatarProps): react_jsx_runtime.JSX.Element;
|
|
99
|
+
|
|
100
|
+
declare function DisplayIdCard({ className, classNames, isPending, id, title, description, ...props }: CardComponentProps & {
|
|
101
|
+
id: string | undefined;
|
|
102
|
+
title: string;
|
|
103
|
+
description: string;
|
|
104
|
+
}): react_jsx_runtime.JSX.Element;
|
|
105
|
+
|
|
106
|
+
type EmptyStateProps = PropsWithChildren<{
|
|
107
|
+
className?: string;
|
|
108
|
+
icon?: LucideIcon;
|
|
109
|
+
title: string;
|
|
110
|
+
description?: ReactNode;
|
|
111
|
+
learnMore?: string;
|
|
112
|
+
learnMoreText?: string;
|
|
113
|
+
}>;
|
|
114
|
+
declare function EmptyState({ className, icon: Icon, title, description, learnMore, learnMoreText, children, }: EmptyStateProps): react_jsx_runtime.JSX.Element;
|
|
115
|
+
|
|
116
|
+
declare function LanguageSwitcher({ className, type, variant, size, showNames, showFlags, }: Omit<LanguageSwitcherProps, "currentLocale" | "locales" | "onLocaleChange">): react_jsx_runtime.JSX.Element;
|
|
117
|
+
|
|
118
|
+
interface LogoProps {
|
|
119
|
+
className?: string;
|
|
120
|
+
href?: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
declare function LogoWithName({ className }: LogoProps): react_jsx_runtime.JSX.Element;
|
|
124
|
+
declare function LogoWithHref({ className, href }: LogoProps): react_jsx_runtime.JSX.Element;
|
|
125
|
+
|
|
126
|
+
declare function OTPInputGroup({ otpSeparators }: {
|
|
127
|
+
otpSeparators?: 0 | 1 | 2;
|
|
128
|
+
}): react_jsx_runtime.JSX.Element;
|
|
129
|
+
|
|
130
|
+
declare function PasswordInput({ className, variant, enableToggle, onChange, ...props }: ComponentProps<typeof Input> & {
|
|
131
|
+
enableToggle?: boolean;
|
|
132
|
+
}): react_jsx_runtime.JSX.Element;
|
|
133
|
+
|
|
134
|
+
declare function SignedInHint({ linkHref }: {
|
|
135
|
+
linkHref?: string;
|
|
136
|
+
}): react_jsx_runtime.JSX.Element;
|
|
137
|
+
|
|
138
|
+
declare function UserMenu({ hiddenSwitcher }: {
|
|
139
|
+
hiddenSwitcher?: boolean;
|
|
140
|
+
}): react_jsx_runtime.JSX.Element;
|
|
141
|
+
|
|
142
|
+
type ViewClassNames = {
|
|
143
|
+
base?: string;
|
|
144
|
+
content?: string;
|
|
145
|
+
title?: string;
|
|
146
|
+
subtitle?: string;
|
|
147
|
+
skeleton?: string;
|
|
148
|
+
icon?: string;
|
|
149
|
+
avatar?: AvatarClassNames;
|
|
150
|
+
};
|
|
151
|
+
interface ViewProps {
|
|
152
|
+
className?: string;
|
|
153
|
+
classNames?: ViewClassNames;
|
|
154
|
+
isPending?: boolean;
|
|
155
|
+
size?: NonNullable<Parameters<typeof buttonVariants>[0]>["size"] | null | undefined;
|
|
156
|
+
user?: Partial<User> | null;
|
|
157
|
+
workspace?: Partial<Workspace> | null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
declare function UserView({ className, classNames, isPending, size, user }: ViewProps): react_jsx_runtime.JSX.Element;
|
|
161
|
+
declare function ApiKeyView({ className, classNames, apiKey }: ViewProps & {
|
|
162
|
+
apiKey: ApiKey;
|
|
163
|
+
}): react_jsx_runtime.JSX.Element;
|
|
164
|
+
declare function WorkspaceView({ className, classNames, isPending, size, workspace }: ViewProps): react_jsx_runtime.JSX.Element;
|
|
165
|
+
|
|
166
|
+
export { ApiKeyView, type AvatarClassNames, type AvatarProps, CardActionComponent, type CardClassNames, CardComponent, type CardComponentProps, CardFooterComponent, CardHeaderComponent, type DialogClassNames, DialogComponent, type DialogComponentProps, DialogFooterComponent, DisplayIdCard, EmptyState, type EmptyStateProps, LanguageSwitcher, LogoWithHref, LogoWithName, OTPInputGroup, PasswordInput, SignedInHint, SkeletonInputComponent, SkeletonViewComponent, UserAvatar, UserMenu, UserView, type ViewClassNames, type ViewProps, WorkspaceLogo, WorkspaceView };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,993 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/components/utils/card.tsx
|
|
4
|
+
import { useFormState } from "react-hook-form";
|
|
5
|
+
import { cn } from "pelatform-ui";
|
|
6
|
+
import { Button, Card, CardContent, CardFooter, Skeleton, Spinner } from "pelatform-ui/default";
|
|
7
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
function CardComponent({
|
|
9
|
+
children,
|
|
10
|
+
className,
|
|
11
|
+
classNames,
|
|
12
|
+
title,
|
|
13
|
+
description,
|
|
14
|
+
instructions,
|
|
15
|
+
actionLabel,
|
|
16
|
+
action,
|
|
17
|
+
disabled,
|
|
18
|
+
isDestructive,
|
|
19
|
+
isPending,
|
|
20
|
+
isSubmitting,
|
|
21
|
+
...props
|
|
22
|
+
}) {
|
|
23
|
+
return /* @__PURE__ */ jsxs(
|
|
24
|
+
Card,
|
|
25
|
+
{
|
|
26
|
+
className: cn(
|
|
27
|
+
"w-full overflow-hidden",
|
|
28
|
+
isDestructive && "border-destructive/70",
|
|
29
|
+
className,
|
|
30
|
+
classNames?.base
|
|
31
|
+
),
|
|
32
|
+
...props,
|
|
33
|
+
children: [
|
|
34
|
+
/* @__PURE__ */ jsxs(CardContent, { className: cn("space-y-6 p-5 sm:p-10", classNames?.content), children: [
|
|
35
|
+
/* @__PURE__ */ jsx(
|
|
36
|
+
CardHeaderComponent,
|
|
37
|
+
{
|
|
38
|
+
classNames,
|
|
39
|
+
description,
|
|
40
|
+
title,
|
|
41
|
+
isPending
|
|
42
|
+
}
|
|
43
|
+
),
|
|
44
|
+
children
|
|
45
|
+
] }),
|
|
46
|
+
/* @__PURE__ */ jsx(
|
|
47
|
+
CardFooterComponent,
|
|
48
|
+
{
|
|
49
|
+
classNames,
|
|
50
|
+
instructions,
|
|
51
|
+
actionLabel,
|
|
52
|
+
action,
|
|
53
|
+
disabled,
|
|
54
|
+
isDestructive,
|
|
55
|
+
isPending,
|
|
56
|
+
isSubmitting
|
|
57
|
+
}
|
|
58
|
+
)
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
function CardHeaderComponent({
|
|
64
|
+
className,
|
|
65
|
+
classNames,
|
|
66
|
+
title,
|
|
67
|
+
description,
|
|
68
|
+
isPending
|
|
69
|
+
}) {
|
|
70
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col space-y-2", className, classNames?.header), children: isPending ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
71
|
+
/* @__PURE__ */ jsx(Skeleton, { className: cn("h-7 w-1/3", classNames?.skeleton) }),
|
|
72
|
+
description && /* @__PURE__ */ jsx(Skeleton, { className: cn("h-5 w-2/3", classNames?.skeleton) })
|
|
73
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
74
|
+
/* @__PURE__ */ jsx("h2", { className: cn("font-medium text-xl", classNames?.title), children: title }),
|
|
75
|
+
description && /* @__PURE__ */ jsx("p", { className: cn("text-muted-foreground text-sm", classNames?.description), children: description })
|
|
76
|
+
] }) });
|
|
77
|
+
}
|
|
78
|
+
function CardFooterComponent({
|
|
79
|
+
className,
|
|
80
|
+
classNames,
|
|
81
|
+
instructions,
|
|
82
|
+
actionLabel,
|
|
83
|
+
action,
|
|
84
|
+
disabled,
|
|
85
|
+
isDestructive,
|
|
86
|
+
isPending,
|
|
87
|
+
isSubmitting
|
|
88
|
+
}) {
|
|
89
|
+
return /* @__PURE__ */ jsx(
|
|
90
|
+
CardFooter,
|
|
91
|
+
{
|
|
92
|
+
className: cn(
|
|
93
|
+
"flex items-center justify-between space-x-4 bg-muted p-3 sm:px-10",
|
|
94
|
+
isDestructive && "border-destructive/70",
|
|
95
|
+
className,
|
|
96
|
+
classNames?.footer
|
|
97
|
+
),
|
|
98
|
+
children: isPending ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
99
|
+
instructions && /* @__PURE__ */ jsx(
|
|
100
|
+
Skeleton,
|
|
101
|
+
{
|
|
102
|
+
className: cn(
|
|
103
|
+
"h-4 w-48 max-w-full bg-muted-foreground/10 md:h-5 md:w-60",
|
|
104
|
+
classNames?.skeleton
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
),
|
|
108
|
+
actionLabel && /* @__PURE__ */ jsx(
|
|
109
|
+
Skeleton,
|
|
110
|
+
{
|
|
111
|
+
className: cn("h-8 w-20 bg-muted-foreground/10 md:ms-auto", classNames?.skeleton)
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
115
|
+
instructions && /* @__PURE__ */ jsx(
|
|
116
|
+
"div",
|
|
117
|
+
{
|
|
118
|
+
className: cn("text-muted-foreground text-xs md:text-sm", classNames?.instructions),
|
|
119
|
+
children: instructions
|
|
120
|
+
}
|
|
121
|
+
),
|
|
122
|
+
actionLabel && /* @__PURE__ */ jsx(
|
|
123
|
+
CardActionComponent,
|
|
124
|
+
{
|
|
125
|
+
classNames,
|
|
126
|
+
actionLabel,
|
|
127
|
+
onClick: action,
|
|
128
|
+
disabled,
|
|
129
|
+
isDestructive,
|
|
130
|
+
isSubmitting
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
] })
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
function CardActionComponent({
|
|
138
|
+
classNames,
|
|
139
|
+
actionLabel,
|
|
140
|
+
onClick,
|
|
141
|
+
disabled,
|
|
142
|
+
isDestructive,
|
|
143
|
+
isSubmitting,
|
|
144
|
+
...props
|
|
145
|
+
}) {
|
|
146
|
+
if (!onClick) {
|
|
147
|
+
const formState = useFormState();
|
|
148
|
+
isSubmitting = formState.isSubmitting;
|
|
149
|
+
}
|
|
150
|
+
return /* @__PURE__ */ jsxs(
|
|
151
|
+
Button,
|
|
152
|
+
{
|
|
153
|
+
type: onClick ? "button" : "submit",
|
|
154
|
+
variant: isDestructive ? "destructive" : "primary",
|
|
155
|
+
size: "sm",
|
|
156
|
+
className: cn(
|
|
157
|
+
"ms-auto",
|
|
158
|
+
isSubmitting || disabled ? "pointer-events-auto! cursor-not-allowed" : "",
|
|
159
|
+
classNames?.button,
|
|
160
|
+
isDestructive ? classNames?.destructiveButton : classNames?.primaryButton
|
|
161
|
+
),
|
|
162
|
+
onClick,
|
|
163
|
+
disabled: isSubmitting || disabled,
|
|
164
|
+
...props,
|
|
165
|
+
children: [
|
|
166
|
+
isSubmitting && /* @__PURE__ */ jsx(Spinner, {}),
|
|
167
|
+
actionLabel
|
|
168
|
+
]
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/components/utils/dialog.tsx
|
|
174
|
+
import { useTranslations } from "next-intl";
|
|
175
|
+
import { cn as cn2 } from "pelatform-ui";
|
|
176
|
+
import {
|
|
177
|
+
Button as Button2,
|
|
178
|
+
Dialog,
|
|
179
|
+
DialogContent,
|
|
180
|
+
DialogDescription,
|
|
181
|
+
DialogFooter,
|
|
182
|
+
DialogHeader,
|
|
183
|
+
DialogTitle
|
|
184
|
+
} from "pelatform-ui/default";
|
|
185
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
186
|
+
function DialogComponent({
|
|
187
|
+
children,
|
|
188
|
+
classNames,
|
|
189
|
+
onOpenChange,
|
|
190
|
+
title,
|
|
191
|
+
description,
|
|
192
|
+
disableFooter,
|
|
193
|
+
cancelButton,
|
|
194
|
+
cancelButtonDisabled,
|
|
195
|
+
button,
|
|
196
|
+
...props
|
|
197
|
+
}) {
|
|
198
|
+
return /* @__PURE__ */ jsx2(Dialog, { onOpenChange, ...props, children: /* @__PURE__ */ jsxs2(
|
|
199
|
+
DialogContent,
|
|
200
|
+
{
|
|
201
|
+
onOpenAutoFocus: (e) => e.preventDefault(),
|
|
202
|
+
className: classNames?.dialog?.content,
|
|
203
|
+
children: [
|
|
204
|
+
/* @__PURE__ */ jsxs2(DialogHeader, { className: cn2("space-y-2", classNames?.header), children: [
|
|
205
|
+
/* @__PURE__ */ jsx2(DialogTitle, { className: classNames?.title, children: title }),
|
|
206
|
+
description && /* @__PURE__ */ jsx2(DialogDescription, { className: classNames?.description, children: description })
|
|
207
|
+
] }),
|
|
208
|
+
children,
|
|
209
|
+
!disableFooter && /* @__PURE__ */ jsx2(
|
|
210
|
+
DialogFooterComponent,
|
|
211
|
+
{
|
|
212
|
+
classNames,
|
|
213
|
+
onOpenChange,
|
|
214
|
+
cancelButton,
|
|
215
|
+
cancelButtonDisabled,
|
|
216
|
+
button
|
|
217
|
+
}
|
|
218
|
+
)
|
|
219
|
+
]
|
|
220
|
+
}
|
|
221
|
+
) });
|
|
222
|
+
}
|
|
223
|
+
function DialogFooterComponent({
|
|
224
|
+
className,
|
|
225
|
+
classNames,
|
|
226
|
+
onOpenChange,
|
|
227
|
+
cancelButton,
|
|
228
|
+
cancelButtonDisabled,
|
|
229
|
+
button
|
|
230
|
+
}) {
|
|
231
|
+
const t = useTranslations();
|
|
232
|
+
return /* @__PURE__ */ jsxs2(DialogFooter, { className: cn2(className, classNames?.dialog?.footer), children: [
|
|
233
|
+
cancelButton && /* @__PURE__ */ jsx2(
|
|
234
|
+
Button2,
|
|
235
|
+
{
|
|
236
|
+
type: "button",
|
|
237
|
+
variant: "secondary",
|
|
238
|
+
className: cn2(classNames?.button, classNames?.secondaryButton),
|
|
239
|
+
onClick: () => onOpenChange?.(false),
|
|
240
|
+
disabled: cancelButtonDisabled,
|
|
241
|
+
children: t("common.actions.cancel")
|
|
242
|
+
}
|
|
243
|
+
),
|
|
244
|
+
button
|
|
245
|
+
] });
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// src/components/utils/skeleton.tsx
|
|
249
|
+
import { cn as cn3 } from "pelatform-ui";
|
|
250
|
+
import { Card as Card2, Skeleton as Skeleton2 } from "pelatform-ui/default";
|
|
251
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
252
|
+
function SkeletonViewComponent({ classNames }) {
|
|
253
|
+
return /* @__PURE__ */ jsxs3(Card2, { className: cn3("flex-row items-center gap-3 px-4 py-3", classNames?.cell), children: [
|
|
254
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
|
|
255
|
+
/* @__PURE__ */ jsx3(Skeleton2, { className: cn3("size-5 rounded-full", classNames?.skeleton) }),
|
|
256
|
+
/* @__PURE__ */ jsx3("div", { children: /* @__PURE__ */ jsx3(Skeleton2, { className: cn3("h-4 w-32", classNames?.skeleton) }) })
|
|
257
|
+
] }),
|
|
258
|
+
/* @__PURE__ */ jsx3(Skeleton2, { className: cn3("ms-auto size-8 w-16", classNames?.skeleton) })
|
|
259
|
+
] });
|
|
260
|
+
}
|
|
261
|
+
function SkeletonInputComponent({ classNames }) {
|
|
262
|
+
return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-1.5", children: [
|
|
263
|
+
/* @__PURE__ */ jsx3(Skeleton2, { className: cn3("h-4 w-32", classNames?.skeleton) }),
|
|
264
|
+
/* @__PURE__ */ jsx3(Skeleton2, { className: cn3("h-9 w-full", classNames?.skeleton) })
|
|
265
|
+
] });
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// src/components/avatar.tsx
|
|
269
|
+
import { BuildingIcon, UserRoundIcon } from "lucide-react";
|
|
270
|
+
import { useTranslations as useTranslations2 } from "next-intl";
|
|
271
|
+
import { getSizeAvatar, getUserName } from "@pelatform/starter.utils";
|
|
272
|
+
import { cn as cn4 } from "pelatform-ui";
|
|
273
|
+
import { getInitials } from "pelatform-ui/components";
|
|
274
|
+
import { Avatar, AvatarFallback, AvatarImage, Skeleton as Skeleton3 } from "pelatform-ui/default";
|
|
275
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
276
|
+
function UserAvatar({
|
|
277
|
+
className,
|
|
278
|
+
classNames,
|
|
279
|
+
image,
|
|
280
|
+
isPending,
|
|
281
|
+
size,
|
|
282
|
+
user,
|
|
283
|
+
...props
|
|
284
|
+
}) {
|
|
285
|
+
const t = useTranslations2();
|
|
286
|
+
const name = getUserName(user);
|
|
287
|
+
const src = user?.image;
|
|
288
|
+
if (isPending) {
|
|
289
|
+
return /* @__PURE__ */ jsx4(
|
|
290
|
+
Skeleton3,
|
|
291
|
+
{
|
|
292
|
+
className: cn4(
|
|
293
|
+
"shrink-0 rounded-full",
|
|
294
|
+
getSizeAvatar(size),
|
|
295
|
+
className,
|
|
296
|
+
classNames?.base,
|
|
297
|
+
classNames?.skeleton
|
|
298
|
+
)
|
|
299
|
+
}
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
return /* @__PURE__ */ jsxs4(
|
|
303
|
+
Avatar,
|
|
304
|
+
{
|
|
305
|
+
className: cn4("rounded-full bg-accent", getSizeAvatar(size), className, classNames?.base),
|
|
306
|
+
...props,
|
|
307
|
+
children: [
|
|
308
|
+
/* @__PURE__ */ jsx4(
|
|
309
|
+
AvatarImage,
|
|
310
|
+
{
|
|
311
|
+
src: image || src || void 0,
|
|
312
|
+
alt: name || t("ui.navigation.user"),
|
|
313
|
+
className: classNames?.image
|
|
314
|
+
}
|
|
315
|
+
),
|
|
316
|
+
/* @__PURE__ */ jsx4(
|
|
317
|
+
AvatarFallback,
|
|
318
|
+
{
|
|
319
|
+
className: cn4("text-foreground uppercase", classNames?.fallback),
|
|
320
|
+
delayMs: src ? 600 : void 0,
|
|
321
|
+
children: getInitials(name, 2) || /* @__PURE__ */ jsx4(UserRoundIcon, { className: cn4("size-[50%]", classNames?.fallbackIcon) })
|
|
322
|
+
}
|
|
323
|
+
)
|
|
324
|
+
]
|
|
325
|
+
}
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
function WorkspaceLogo({
|
|
329
|
+
className,
|
|
330
|
+
classNames,
|
|
331
|
+
image,
|
|
332
|
+
isPending,
|
|
333
|
+
size,
|
|
334
|
+
workspace,
|
|
335
|
+
...props
|
|
336
|
+
}) {
|
|
337
|
+
const t = useTranslations2();
|
|
338
|
+
const name = workspace?.name;
|
|
339
|
+
const src = workspace?.logo;
|
|
340
|
+
if (isPending) {
|
|
341
|
+
return /* @__PURE__ */ jsx4(
|
|
342
|
+
Skeleton3,
|
|
343
|
+
{
|
|
344
|
+
className: cn4(
|
|
345
|
+
"shrink-0 rounded-full",
|
|
346
|
+
getSizeAvatar(size),
|
|
347
|
+
className,
|
|
348
|
+
classNames?.base,
|
|
349
|
+
classNames?.skeleton
|
|
350
|
+
)
|
|
351
|
+
}
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
return /* @__PURE__ */ jsxs4(
|
|
355
|
+
Avatar,
|
|
356
|
+
{
|
|
357
|
+
className: cn4("rounded-full bg-accent", getSizeAvatar(size), className, classNames?.base),
|
|
358
|
+
...props,
|
|
359
|
+
children: [
|
|
360
|
+
/* @__PURE__ */ jsx4(
|
|
361
|
+
AvatarImage,
|
|
362
|
+
{
|
|
363
|
+
src: image || src || void 0,
|
|
364
|
+
alt: name || t("ui.navigation.workspace"),
|
|
365
|
+
className: classNames?.image
|
|
366
|
+
}
|
|
367
|
+
),
|
|
368
|
+
/* @__PURE__ */ jsx4(
|
|
369
|
+
AvatarFallback,
|
|
370
|
+
{
|
|
371
|
+
className: cn4("text-foreground", classNames?.fallback),
|
|
372
|
+
delayMs: src ? 600 : void 0,
|
|
373
|
+
children: /* @__PURE__ */ jsx4(BuildingIcon, { className: cn4("size-[50%]", classNames?.fallbackIcon) })
|
|
374
|
+
}
|
|
375
|
+
)
|
|
376
|
+
]
|
|
377
|
+
}
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// src/components/display-id.tsx
|
|
382
|
+
import { useRef } from "react";
|
|
383
|
+
import { CheckIcon, CopyIcon } from "lucide-react";
|
|
384
|
+
import { useTranslations as useTranslations3 } from "next-intl";
|
|
385
|
+
import { cn as cn5 } from "pelatform-ui";
|
|
386
|
+
import {
|
|
387
|
+
Button as Button3,
|
|
388
|
+
Input,
|
|
389
|
+
Skeleton as Skeleton4,
|
|
390
|
+
Tooltip,
|
|
391
|
+
TooltipContent,
|
|
392
|
+
TooltipProvider,
|
|
393
|
+
TooltipTrigger
|
|
394
|
+
} from "pelatform-ui/default";
|
|
395
|
+
import { useCopyToClipboard } from "pelatform-ui/hooks";
|
|
396
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
397
|
+
function DisplayIdCard({
|
|
398
|
+
className,
|
|
399
|
+
classNames,
|
|
400
|
+
isPending,
|
|
401
|
+
id,
|
|
402
|
+
title,
|
|
403
|
+
description,
|
|
404
|
+
...props
|
|
405
|
+
}) {
|
|
406
|
+
const t = useTranslations3();
|
|
407
|
+
const { copy, copied } = useCopyToClipboard();
|
|
408
|
+
const inputRef = useRef(null);
|
|
409
|
+
const handleCopy = () => {
|
|
410
|
+
if (inputRef.current) {
|
|
411
|
+
copy(inputRef.current.value);
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
return /* @__PURE__ */ jsx5(
|
|
415
|
+
CardComponent,
|
|
416
|
+
{
|
|
417
|
+
className,
|
|
418
|
+
classNames,
|
|
419
|
+
title,
|
|
420
|
+
description,
|
|
421
|
+
isPending,
|
|
422
|
+
...props,
|
|
423
|
+
children: isPending ? /* @__PURE__ */ jsx5(Skeleton4, { className: cn5("h-11.5 w-full max-w-md", classNames?.skeleton) }) : /* @__PURE__ */ jsxs5(
|
|
424
|
+
"div",
|
|
425
|
+
{
|
|
426
|
+
className: cn5(
|
|
427
|
+
"flex w-full max-w-md items-center justify-between rounded-md border p-2",
|
|
428
|
+
classNames?.grid
|
|
429
|
+
),
|
|
430
|
+
children: [
|
|
431
|
+
/* @__PURE__ */ jsx5(Input, { value: id, ref: inputRef, disabled: true, className: "border-none! bg-transparent!" }),
|
|
432
|
+
/* @__PURE__ */ jsx5(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs5(Tooltip, { children: [
|
|
433
|
+
/* @__PURE__ */ jsx5(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx5(Button3, { variant: "dim", onClick: handleCopy, disabled: copied, children: copied ? /* @__PURE__ */ jsx5(CheckIcon, { className: cn5("stroke-green-600", classNames?.icon) }) : /* @__PURE__ */ jsx5(CopyIcon, { className: classNames?.icon }) }) }),
|
|
434
|
+
/* @__PURE__ */ jsx5(TooltipContent, { className: "px-2 py-1 text-xs", children: t("common.actions.copy") })
|
|
435
|
+
] }) })
|
|
436
|
+
]
|
|
437
|
+
}
|
|
438
|
+
)
|
|
439
|
+
}
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// src/components/empty-state.tsx
|
|
444
|
+
import Link from "next/link";
|
|
445
|
+
import { useTranslations as useTranslations4 } from "next-intl";
|
|
446
|
+
import { cn as cn6 } from "pelatform-ui";
|
|
447
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
448
|
+
function EmptyState({
|
|
449
|
+
className,
|
|
450
|
+
icon: Icon,
|
|
451
|
+
title,
|
|
452
|
+
description,
|
|
453
|
+
learnMore,
|
|
454
|
+
learnMoreText,
|
|
455
|
+
children
|
|
456
|
+
}) {
|
|
457
|
+
const t = useTranslations4();
|
|
458
|
+
return /* @__PURE__ */ jsxs6("div", { className: cn6("flex flex-col items-center justify-center gap-y-4", className), children: [
|
|
459
|
+
Icon && /* @__PURE__ */ jsx6("div", { className: "flex size-14 items-center justify-center rounded-2xl border bg-muted", children: /* @__PURE__ */ jsx6(Icon, { className: "size-6 text-foreground" }) }),
|
|
460
|
+
/* @__PURE__ */ jsx6("p", { className: "text-center font-medium text-base text-foreground", children: title }),
|
|
461
|
+
description && /* @__PURE__ */ jsxs6("p", { className: "max-w-sm text-balance text-center text-muted-foreground text-sm", children: [
|
|
462
|
+
description,
|
|
463
|
+
" ",
|
|
464
|
+
learnMore && /* @__PURE__ */ jsxs6(
|
|
465
|
+
Link,
|
|
466
|
+
{
|
|
467
|
+
href: learnMore,
|
|
468
|
+
className: "text-foreground underline underline-offset-2 transition-colors hover:text-primary",
|
|
469
|
+
children: [
|
|
470
|
+
learnMoreText ?? t("ui.common.learnMore"),
|
|
471
|
+
" \u2197"
|
|
472
|
+
]
|
|
473
|
+
}
|
|
474
|
+
)
|
|
475
|
+
] }),
|
|
476
|
+
children
|
|
477
|
+
] });
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// src/components/language-switcher.tsx
|
|
481
|
+
import Image from "next/image";
|
|
482
|
+
import { useRouter } from "next/navigation";
|
|
483
|
+
import { useLocale, useTranslations as useTranslations5 } from "next-intl";
|
|
484
|
+
import { useConfig } from "@pelatform/starter.hook";
|
|
485
|
+
import {
|
|
486
|
+
LanguageSwitcher as LanguageSwitcherBase
|
|
487
|
+
} from "pelatform-ui/components";
|
|
488
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
489
|
+
function LanguageSwitcher({
|
|
490
|
+
className,
|
|
491
|
+
type,
|
|
492
|
+
variant,
|
|
493
|
+
size,
|
|
494
|
+
showNames,
|
|
495
|
+
showFlags
|
|
496
|
+
}) {
|
|
497
|
+
const { i18n } = useConfig();
|
|
498
|
+
const router = useRouter();
|
|
499
|
+
const t = useTranslations5();
|
|
500
|
+
const currentLocale = useLocale();
|
|
501
|
+
const availableLocales = (() => {
|
|
502
|
+
if (!i18n.enabled) {
|
|
503
|
+
return [i18n.defaultLocale];
|
|
504
|
+
}
|
|
505
|
+
const availableLocales2 = Object.keys(i18n.locales);
|
|
506
|
+
return availableLocales2;
|
|
507
|
+
})();
|
|
508
|
+
const languages = availableLocales.map((locale) => {
|
|
509
|
+
const localeConfig = i18n.locales[locale] || null;
|
|
510
|
+
return {
|
|
511
|
+
code: locale,
|
|
512
|
+
name: localeConfig?.name || locale.toUpperCase(),
|
|
513
|
+
flag: `/flags/${localeConfig?.flag}.svg` || ""
|
|
514
|
+
};
|
|
515
|
+
});
|
|
516
|
+
function handleLanguageChange(newLocale) {
|
|
517
|
+
if (newLocale === currentLocale) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
document.cookie = `${i18n.localeCookieName}=${newLocale}; max-age=${60 * 60 * 24 * 365}; path=/`;
|
|
521
|
+
router.refresh();
|
|
522
|
+
}
|
|
523
|
+
return /* @__PURE__ */ jsx7(
|
|
524
|
+
LanguageSwitcherBase,
|
|
525
|
+
{
|
|
526
|
+
className,
|
|
527
|
+
type,
|
|
528
|
+
variant,
|
|
529
|
+
size,
|
|
530
|
+
showNames,
|
|
531
|
+
showFlags,
|
|
532
|
+
label: t("ui.common.language"),
|
|
533
|
+
i18nEnabled: i18n.enabled,
|
|
534
|
+
currentLocale,
|
|
535
|
+
locales: languages,
|
|
536
|
+
onLocaleChange: handleLanguageChange,
|
|
537
|
+
customFlagUrl: true,
|
|
538
|
+
Image
|
|
539
|
+
}
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// src/components/logo.tsx
|
|
544
|
+
import Link2 from "next/link";
|
|
545
|
+
import { useConfig as useConfig2 } from "@pelatform/starter.hook";
|
|
546
|
+
import { cn as cn7 } from "pelatform-ui";
|
|
547
|
+
import { Logo } from "pelatform-ui/components";
|
|
548
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
549
|
+
function LogoWithName({ className }) {
|
|
550
|
+
const { app } = useConfig2();
|
|
551
|
+
return /* @__PURE__ */ jsxs7("div", { className: cn7("flex items-center gap-2", className), children: [
|
|
552
|
+
/* @__PURE__ */ jsx8(Logo, { className: "size-7" }),
|
|
553
|
+
/* @__PURE__ */ jsx8("span", { className: "font-semibold text-lg text-mono", children: app.name })
|
|
554
|
+
] });
|
|
555
|
+
}
|
|
556
|
+
function LogoWithHref({ className, href = "/" }) {
|
|
557
|
+
const { app } = useConfig2();
|
|
558
|
+
return /* @__PURE__ */ jsxs7(Link2, { href, className: cn7("flex items-center gap-2", className), children: [
|
|
559
|
+
/* @__PURE__ */ jsx8(Logo, { className: "size-7" }),
|
|
560
|
+
/* @__PURE__ */ jsx8("span", { className: "font-semibold text-lg text-mono", children: app.name })
|
|
561
|
+
] });
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// src/components/otp-input-group.tsx
|
|
565
|
+
import { InputOTPGroup, InputOTPSeparator, InputOTPSlot } from "pelatform-ui/default";
|
|
566
|
+
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
567
|
+
function OTPInputGroup({ otpSeparators = 0 }) {
|
|
568
|
+
if (otpSeparators === 0) {
|
|
569
|
+
return /* @__PURE__ */ jsxs8(InputOTPGroup, { children: [
|
|
570
|
+
/* @__PURE__ */ jsx9(InputOTPSlot, { index: 0 }),
|
|
571
|
+
/* @__PURE__ */ jsx9(InputOTPSlot, { index: 1 }),
|
|
572
|
+
/* @__PURE__ */ jsx9(InputOTPSlot, { index: 2 }),
|
|
573
|
+
/* @__PURE__ */ jsx9(InputOTPSlot, { index: 3 }),
|
|
574
|
+
/* @__PURE__ */ jsx9(InputOTPSlot, { index: 4 }),
|
|
575
|
+
/* @__PURE__ */ jsx9(InputOTPSlot, { index: 5 })
|
|
576
|
+
] });
|
|
577
|
+
}
|
|
578
|
+
if (otpSeparators === 1) {
|
|
579
|
+
return /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
580
|
+
/* @__PURE__ */ jsxs8(InputOTPGroup, { children: [
|
|
581
|
+
/* @__PURE__ */ jsx9(InputOTPSlot, { index: 0 }),
|
|
582
|
+
/* @__PURE__ */ jsx9(InputOTPSlot, { index: 1 }),
|
|
583
|
+
/* @__PURE__ */ jsx9(InputOTPSlot, { index: 2 })
|
|
584
|
+
] }),
|
|
585
|
+
/* @__PURE__ */ jsx9(InputOTPSeparator, {}),
|
|
586
|
+
/* @__PURE__ */ jsxs8(InputOTPGroup, { children: [
|
|
587
|
+
/* @__PURE__ */ jsx9(InputOTPSlot, { index: 3 }),
|
|
588
|
+
/* @__PURE__ */ jsx9(InputOTPSlot, { index: 4 }),
|
|
589
|
+
/* @__PURE__ */ jsx9(InputOTPSlot, { index: 5 })
|
|
590
|
+
] })
|
|
591
|
+
] });
|
|
592
|
+
}
|
|
593
|
+
return /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
594
|
+
/* @__PURE__ */ jsxs8(InputOTPGroup, { children: [
|
|
595
|
+
/* @__PURE__ */ jsx9(InputOTPSlot, { index: 0 }),
|
|
596
|
+
/* @__PURE__ */ jsx9(InputOTPSlot, { index: 1 })
|
|
597
|
+
] }),
|
|
598
|
+
/* @__PURE__ */ jsx9(InputOTPSeparator, {}),
|
|
599
|
+
/* @__PURE__ */ jsxs8(InputOTPGroup, { children: [
|
|
600
|
+
/* @__PURE__ */ jsx9(InputOTPSlot, { index: 2 }),
|
|
601
|
+
/* @__PURE__ */ jsx9(InputOTPSlot, { index: 3 })
|
|
602
|
+
] }),
|
|
603
|
+
/* @__PURE__ */ jsx9(InputOTPSeparator, {}),
|
|
604
|
+
/* @__PURE__ */ jsxs8(InputOTPGroup, { children: [
|
|
605
|
+
/* @__PURE__ */ jsx9(InputOTPSlot, { index: 4 }),
|
|
606
|
+
/* @__PURE__ */ jsx9(InputOTPSlot, { index: 5 })
|
|
607
|
+
] })
|
|
608
|
+
] });
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// src/components/password-input.tsx
|
|
612
|
+
import { useState } from "react";
|
|
613
|
+
import { EyeIcon, EyeOffIcon } from "lucide-react";
|
|
614
|
+
import { cn as cn8 } from "pelatform-ui";
|
|
615
|
+
import { Button as Button4, Input as Input2 } from "pelatform-ui/default";
|
|
616
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
617
|
+
function PasswordInput({
|
|
618
|
+
className,
|
|
619
|
+
variant,
|
|
620
|
+
enableToggle,
|
|
621
|
+
onChange,
|
|
622
|
+
...props
|
|
623
|
+
}) {
|
|
624
|
+
const [disabled, setDisabled] = useState(true);
|
|
625
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
626
|
+
return /* @__PURE__ */ jsxs9("div", { className: "relative", children: [
|
|
627
|
+
/* @__PURE__ */ jsx10(
|
|
628
|
+
Input2,
|
|
629
|
+
{
|
|
630
|
+
type: isVisible && enableToggle ? "text" : "password",
|
|
631
|
+
variant,
|
|
632
|
+
className: cn8(enableToggle && "pe-10", className),
|
|
633
|
+
...props,
|
|
634
|
+
onChange: (event) => {
|
|
635
|
+
setDisabled(!event.target.value);
|
|
636
|
+
onChange?.(event);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
),
|
|
640
|
+
enableToggle && /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
641
|
+
/* @__PURE__ */ jsx10(
|
|
642
|
+
Button4,
|
|
643
|
+
{
|
|
644
|
+
type: "button",
|
|
645
|
+
variant: "ghost",
|
|
646
|
+
size: "icon",
|
|
647
|
+
className: "absolute end-0 top-0 bg-transparent!",
|
|
648
|
+
onClick: () => setIsVisible(!isVisible),
|
|
649
|
+
disabled,
|
|
650
|
+
children: isVisible ? /* @__PURE__ */ jsx10(EyeIcon, {}) : /* @__PURE__ */ jsx10(EyeOffIcon, {})
|
|
651
|
+
}
|
|
652
|
+
),
|
|
653
|
+
/* @__PURE__ */ jsx10("style", { children: `
|
|
654
|
+
.hide-password-toggle::-ms-reveal,
|
|
655
|
+
.hide-password-toggle::-ms-clear {
|
|
656
|
+
visibility: hidden;
|
|
657
|
+
pointer-events: none;
|
|
658
|
+
display: none;
|
|
659
|
+
}
|
|
660
|
+
` })
|
|
661
|
+
] })
|
|
662
|
+
] });
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// src/components/signed-in-hint.tsx
|
|
666
|
+
import Link3 from "next/link";
|
|
667
|
+
import { useTranslations as useTranslations6 } from "next-intl";
|
|
668
|
+
import { useSession } from "@pelatform/starter.hook";
|
|
669
|
+
import { Button as Button5 } from "pelatform-ui/default";
|
|
670
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
671
|
+
function SignedInHint({ linkHref = "/signin" }) {
|
|
672
|
+
const t = useTranslations6("ui.common.signed");
|
|
673
|
+
const { isPending, user } = useSession();
|
|
674
|
+
return /* @__PURE__ */ jsxs10("div", { className: "fixed start-0 bottom-0 z-40 m-5 flex flex-col gap-2", children: [
|
|
675
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1 text-muted-foreground text-xs", children: [
|
|
676
|
+
t("text"),
|
|
677
|
+
" ",
|
|
678
|
+
isPending || !user ? /* @__PURE__ */ jsx11("span", { className: "h-3 w-32 animate-pulse rounded-md border bg-muted-foreground" }) : /* @__PURE__ */ jsx11("b", { className: "text-foreground", children: user.email })
|
|
679
|
+
] }),
|
|
680
|
+
/* @__PURE__ */ jsx11(Button5, { variant: "mono", className: "h-8 w-fit rounded-lg px-3 text-xs shadow-sm", children: /* @__PURE__ */ jsx11(Link3, { href: linkHref, children: t("button") }) })
|
|
681
|
+
] });
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// src/components/user-menu.tsx
|
|
685
|
+
import { Fragment as Fragment5, useCallback, useEffect, useState as useState2 } from "react";
|
|
686
|
+
import Link4 from "next/link";
|
|
687
|
+
import { useRouter as useRouter2 } from "next/navigation";
|
|
688
|
+
import { LogOut, PlusCircleIcon, Settings, Shield, UserStar } from "lucide-react";
|
|
689
|
+
import { useTranslations as useTranslations8 } from "next-intl";
|
|
690
|
+
import {
|
|
691
|
+
useConfig as useConfig3,
|
|
692
|
+
useListDeviceSessions,
|
|
693
|
+
useSession as useSession2,
|
|
694
|
+
useSetActiveSession
|
|
695
|
+
} from "@pelatform/starter.hook";
|
|
696
|
+
import { getUserName as getUserName3 } from "@pelatform/starter.utils";
|
|
697
|
+
import { ModeSwitcher, UserAvatar as UserAvatar2 } from "pelatform-ui/components";
|
|
698
|
+
import {
|
|
699
|
+
Badge,
|
|
700
|
+
DropdownMenu,
|
|
701
|
+
DropdownMenuContent,
|
|
702
|
+
DropdownMenuItem,
|
|
703
|
+
DropdownMenuSeparator,
|
|
704
|
+
DropdownMenuTrigger,
|
|
705
|
+
Skeleton as Skeleton6
|
|
706
|
+
} from "pelatform-ui/default";
|
|
707
|
+
|
|
708
|
+
// src/components/view.tsx
|
|
709
|
+
import { KeyRoundIcon } from "lucide-react";
|
|
710
|
+
import { useLocale as useLocale2, useTranslations as useTranslations7 } from "next-intl";
|
|
711
|
+
import { getUserName as getUserName2 } from "@pelatform/starter.utils";
|
|
712
|
+
import { cn as cn9 } from "pelatform-ui";
|
|
713
|
+
import { Skeleton as Skeleton5 } from "pelatform-ui/default";
|
|
714
|
+
import { Fragment as Fragment4, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
715
|
+
function UserView({ className, classNames, isPending, size, user }) {
|
|
716
|
+
const t = useTranslations7();
|
|
717
|
+
return /* @__PURE__ */ jsxs11("div", { className: cn9("flex items-center gap-2", className, classNames?.base), children: [
|
|
718
|
+
/* @__PURE__ */ jsx12(
|
|
719
|
+
UserAvatar,
|
|
720
|
+
{
|
|
721
|
+
className: cn9(size !== "sm" && "my-0.5"),
|
|
722
|
+
classNames: classNames?.avatar,
|
|
723
|
+
isPending,
|
|
724
|
+
size,
|
|
725
|
+
user
|
|
726
|
+
}
|
|
727
|
+
),
|
|
728
|
+
/* @__PURE__ */ jsx12("div", { className: cn9("grid flex-1 text-start leading-tight", classNames?.content), children: isPending ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
729
|
+
/* @__PURE__ */ jsx12(
|
|
730
|
+
Skeleton5,
|
|
731
|
+
{
|
|
732
|
+
className: cn9(
|
|
733
|
+
"max-w-full",
|
|
734
|
+
size === "lg" ? "h-4.5 w-32" : "h-3.5 w-24",
|
|
735
|
+
classNames?.title,
|
|
736
|
+
classNames?.skeleton
|
|
737
|
+
)
|
|
738
|
+
}
|
|
739
|
+
),
|
|
740
|
+
size !== "sm" && /* @__PURE__ */ jsx12(
|
|
741
|
+
Skeleton5,
|
|
742
|
+
{
|
|
743
|
+
className: cn9(
|
|
744
|
+
"mt-1.5 max-w-full",
|
|
745
|
+
size === "lg" ? "h-3.5 w-40" : "h-3 w-32",
|
|
746
|
+
classNames?.subtitle,
|
|
747
|
+
classNames?.skeleton
|
|
748
|
+
)
|
|
749
|
+
}
|
|
750
|
+
)
|
|
751
|
+
] }) : /* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
752
|
+
/* @__PURE__ */ jsx12(
|
|
753
|
+
"span",
|
|
754
|
+
{
|
|
755
|
+
className: cn9(
|
|
756
|
+
"truncate font-semibold",
|
|
757
|
+
size === "lg" ? "text-base" : "text-sm",
|
|
758
|
+
classNames?.title
|
|
759
|
+
),
|
|
760
|
+
children: getUserName2(user) || t("ui.navigation.user")
|
|
761
|
+
}
|
|
762
|
+
),
|
|
763
|
+
!user?.isAnonymous && size !== "sm" && (user?.name || user?.username) && /* @__PURE__ */ jsx12(
|
|
764
|
+
"span",
|
|
765
|
+
{
|
|
766
|
+
className: cn9(
|
|
767
|
+
"truncate opacity-70",
|
|
768
|
+
size === "lg" ? "text-sm" : "text-xs",
|
|
769
|
+
classNames?.subtitle
|
|
770
|
+
),
|
|
771
|
+
children: user?.email
|
|
772
|
+
}
|
|
773
|
+
)
|
|
774
|
+
] }) })
|
|
775
|
+
] });
|
|
776
|
+
}
|
|
777
|
+
function ApiKeyView({ className, classNames, apiKey }) {
|
|
778
|
+
const t = useTranslations7();
|
|
779
|
+
const locale = useLocale2();
|
|
780
|
+
const formatExpiration = () => {
|
|
781
|
+
if (!apiKey.expiresAt) return t("common.time.neverExpires");
|
|
782
|
+
const expiresDate = new Date(apiKey.expiresAt);
|
|
783
|
+
return `${t("common.time.expires")} ${expiresDate.toLocaleDateString(locale ?? "en", {
|
|
784
|
+
month: "short",
|
|
785
|
+
day: "numeric",
|
|
786
|
+
year: "numeric"
|
|
787
|
+
})}`;
|
|
788
|
+
};
|
|
789
|
+
return /* @__PURE__ */ jsxs11("div", { className: cn9("flex items-center gap-3 truncate", className, classNames?.base), children: [
|
|
790
|
+
/* @__PURE__ */ jsx12(KeyRoundIcon, { className: cn9("size-4 shrink-0", classNames?.icon) }),
|
|
791
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex flex-col truncate text-start", children: [
|
|
792
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
|
|
793
|
+
/* @__PURE__ */ jsx12("span", { className: "truncate font-semibold text-sm", children: apiKey.name }),
|
|
794
|
+
/* @__PURE__ */ jsxs11("span", { className: "flex-1 truncate text-muted-foreground text-sm", children: [
|
|
795
|
+
apiKey.start,
|
|
796
|
+
"******"
|
|
797
|
+
] })
|
|
798
|
+
] }),
|
|
799
|
+
/* @__PURE__ */ jsx12("div", { className: "truncate text-muted-foreground text-xs", children: formatExpiration() })
|
|
800
|
+
] })
|
|
801
|
+
] });
|
|
802
|
+
}
|
|
803
|
+
function WorkspaceView({ className, classNames, isPending, size, workspace }) {
|
|
804
|
+
const t = useTranslations7();
|
|
805
|
+
return /* @__PURE__ */ jsxs11("div", { className: cn9("flex items-center gap-2 truncate", className, classNames?.base), children: [
|
|
806
|
+
/* @__PURE__ */ jsx12(
|
|
807
|
+
WorkspaceLogo,
|
|
808
|
+
{
|
|
809
|
+
className: cn9(size !== "sm" && "my-0.5"),
|
|
810
|
+
classNames: classNames?.avatar,
|
|
811
|
+
isPending,
|
|
812
|
+
workspace,
|
|
813
|
+
size
|
|
814
|
+
}
|
|
815
|
+
),
|
|
816
|
+
/* @__PURE__ */ jsx12("div", { className: cn9("flex flex-col truncate text-start leading-tight", classNames?.content), children: isPending ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
817
|
+
/* @__PURE__ */ jsx12(
|
|
818
|
+
Skeleton5,
|
|
819
|
+
{
|
|
820
|
+
className: cn9(
|
|
821
|
+
"max-w-full",
|
|
822
|
+
size === "lg" ? "h-4.5 w-32" : "h-3.5 w-24",
|
|
823
|
+
classNames?.title,
|
|
824
|
+
classNames?.skeleton
|
|
825
|
+
)
|
|
826
|
+
}
|
|
827
|
+
),
|
|
828
|
+
size !== "sm" && /* @__PURE__ */ jsx12(
|
|
829
|
+
Skeleton5,
|
|
830
|
+
{
|
|
831
|
+
className: cn9(
|
|
832
|
+
"mt-1.5 max-w-full",
|
|
833
|
+
size === "lg" ? "h-3.5 w-24" : "h-3 w-16",
|
|
834
|
+
classNames?.subtitle,
|
|
835
|
+
classNames?.skeleton
|
|
836
|
+
)
|
|
837
|
+
}
|
|
838
|
+
)
|
|
839
|
+
] }) : /* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
840
|
+
/* @__PURE__ */ jsx12(
|
|
841
|
+
"span",
|
|
842
|
+
{
|
|
843
|
+
className: cn9(
|
|
844
|
+
"truncate font-semibold",
|
|
845
|
+
size === "lg" ? "text-base" : "text-sm",
|
|
846
|
+
classNames?.title
|
|
847
|
+
),
|
|
848
|
+
children: workspace?.name || t("ui.navigation.workspace")
|
|
849
|
+
}
|
|
850
|
+
),
|
|
851
|
+
size !== "sm" && workspace?.slug && /* @__PURE__ */ jsx12(
|
|
852
|
+
"span",
|
|
853
|
+
{
|
|
854
|
+
className: cn9(
|
|
855
|
+
"truncate opacity-70",
|
|
856
|
+
size === "lg" ? "text-sm" : "text-xs",
|
|
857
|
+
classNames?.subtitle
|
|
858
|
+
),
|
|
859
|
+
children: workspace.slug
|
|
860
|
+
}
|
|
861
|
+
)
|
|
862
|
+
] }) })
|
|
863
|
+
] });
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// src/components/user-menu.tsx
|
|
867
|
+
import { Fragment as Fragment6, jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
868
|
+
function UserMenu({ hiddenSwitcher = false }) {
|
|
869
|
+
const { features, path } = useConfig3();
|
|
870
|
+
const router = useRouter2();
|
|
871
|
+
const t = useTranslations8();
|
|
872
|
+
const { isPending: sessionPending, user } = useSession2();
|
|
873
|
+
const { setActiveSessionAsync } = useSetActiveSession();
|
|
874
|
+
const { data: deviceSessions, isPending: deviceSessionsPending } = useListDeviceSessions({
|
|
875
|
+
enabled: features.multiSession
|
|
876
|
+
});
|
|
877
|
+
const [activeSessionPending, setActiveSessionPending] = useState2(false);
|
|
878
|
+
const isPending = sessionPending || activeSessionPending;
|
|
879
|
+
const switchAccount = useCallback(
|
|
880
|
+
async (sessionToken) => {
|
|
881
|
+
setActiveSessionPending(true);
|
|
882
|
+
try {
|
|
883
|
+
await setActiveSessionAsync({ sessionToken });
|
|
884
|
+
router.refresh();
|
|
885
|
+
setActiveSessionPending(false);
|
|
886
|
+
} catch (error) {
|
|
887
|
+
console.error("Error switching account:", error);
|
|
888
|
+
setActiveSessionPending(false);
|
|
889
|
+
}
|
|
890
|
+
},
|
|
891
|
+
[router, setActiveSessionAsync]
|
|
892
|
+
);
|
|
893
|
+
useEffect(() => {
|
|
894
|
+
if (!features.multiSession) return;
|
|
895
|
+
setActiveSessionPending(false);
|
|
896
|
+
}, [features.multiSession, user?.id]);
|
|
897
|
+
const userEmail = user?.email || "user@example.com";
|
|
898
|
+
const userName = getUserName3(user);
|
|
899
|
+
const userAvatar = user?.image || void 0;
|
|
900
|
+
const userRole = user?.role || "user";
|
|
901
|
+
return /* @__PURE__ */ jsxs12(DropdownMenu, { children: [
|
|
902
|
+
/* @__PURE__ */ jsx13(DropdownMenuTrigger, { className: "focus:outline-none focus:ring-0", children: isPending ? /* @__PURE__ */ jsx13(Skeleton6, { className: "size-8 shrink-0 rounded-full" }) : /* @__PURE__ */ jsx13(UserAvatar2, { className: "size-8", indicator: true, src: userAvatar, alt: userName }) }),
|
|
903
|
+
/* @__PURE__ */ jsxs12(DropdownMenuContent, { className: "w-56", side: "bottom", align: "end", sideOffset: 11, children: [
|
|
904
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-3 px-3 py-2", children: [
|
|
905
|
+
/* @__PURE__ */ jsx13(UserAvatar2, { src: userAvatar, alt: userName }),
|
|
906
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex min-w-0 flex-1 flex-col items-start", children: [
|
|
907
|
+
/* @__PURE__ */ jsx13("span", { className: "truncate font-semibold text-foreground text-sm", children: userName }),
|
|
908
|
+
/* @__PURE__ */ jsx13("span", { className: "block w-full truncate text-muted-foreground text-xs", children: userEmail }),
|
|
909
|
+
/* @__PURE__ */ jsx13(Badge, { variant: "success", appearance: "outline", size: "sm", className: "mt-1", children: userRole.toUpperCase() })
|
|
910
|
+
] })
|
|
911
|
+
] }),
|
|
912
|
+
/* @__PURE__ */ jsx13(DropdownMenuSeparator, {}),
|
|
913
|
+
/* @__PURE__ */ jsx13(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsxs12(Link4, { href: path.account.SETTINGS, children: [
|
|
914
|
+
/* @__PURE__ */ jsx13(Settings, {}),
|
|
915
|
+
/* @__PURE__ */ jsx13("span", { children: t("ui.navigation.preferences") })
|
|
916
|
+
] }) }),
|
|
917
|
+
/* @__PURE__ */ jsx13(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsxs12(Link4, { href: path.account.SECURITY, children: [
|
|
918
|
+
/* @__PURE__ */ jsx13(Shield, {}),
|
|
919
|
+
/* @__PURE__ */ jsx13("span", { children: t("ui.navigation.security") })
|
|
920
|
+
] }) }),
|
|
921
|
+
"admin" === userRole && /* @__PURE__ */ jsx13(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsxs12(Link4, { href: path.admin.OVERVIEW, children: [
|
|
922
|
+
/* @__PURE__ */ jsx13(UserStar, {}),
|
|
923
|
+
/* @__PURE__ */ jsx13("span", { children: "Admin Dashboard" })
|
|
924
|
+
] }) }),
|
|
925
|
+
!hiddenSwitcher && /* @__PURE__ */ jsxs12(Fragment6, { children: [
|
|
926
|
+
/* @__PURE__ */ jsx13(DropdownMenuSeparator, {}),
|
|
927
|
+
/* @__PURE__ */ jsx13(
|
|
928
|
+
ModeSwitcher,
|
|
929
|
+
{
|
|
930
|
+
type: "sub-dropdown",
|
|
931
|
+
label: {
|
|
932
|
+
system: t("ui.common.system"),
|
|
933
|
+
dark: t("ui.common.dark"),
|
|
934
|
+
light: t("ui.common.light")
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
),
|
|
938
|
+
/* @__PURE__ */ jsx13(LanguageSwitcher, { type: "sub-dropdown" })
|
|
939
|
+
] }),
|
|
940
|
+
/* @__PURE__ */ jsx13(DropdownMenuSeparator, {}),
|
|
941
|
+
/* @__PURE__ */ jsx13(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsxs12(Link4, { href: path.auth.SIGN_OUT, children: [
|
|
942
|
+
/* @__PURE__ */ jsx13(LogOut, {}),
|
|
943
|
+
/* @__PURE__ */ jsx13("span", { children: t("ui.navigation.signOut") })
|
|
944
|
+
] }) }),
|
|
945
|
+
user && features.multiSession && /* @__PURE__ */ jsxs12(Fragment6, { children: [
|
|
946
|
+
/* @__PURE__ */ jsx13(DropdownMenuSeparator, {}),
|
|
947
|
+
!deviceSessions && deviceSessionsPending && /* @__PURE__ */ jsxs12(Fragment6, { children: [
|
|
948
|
+
/* @__PURE__ */ jsx13(DropdownMenuItem, { disabled: true, children: /* @__PURE__ */ jsx13(UserView, { isPending: true }) }),
|
|
949
|
+
/* @__PURE__ */ jsx13(DropdownMenuSeparator, {})
|
|
950
|
+
] }),
|
|
951
|
+
deviceSessions?.filter((sessionData) => sessionData.user.id !== user?.id).map(({ session, user: multiUser }) => /* @__PURE__ */ jsxs12(Fragment5, { children: [
|
|
952
|
+
/* @__PURE__ */ jsxs12(DropdownMenuItem, { onClick: () => switchAccount(session.token), children: [
|
|
953
|
+
/* @__PURE__ */ jsx13(UserAvatar2, { src: multiUser?.image || void 0, alt: getUserName3(multiUser) }),
|
|
954
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex min-w-0 flex-1 flex-col items-start", children: [
|
|
955
|
+
/* @__PURE__ */ jsx13("span", { className: "truncate font-semibold text-foreground text-xs", children: getUserName3(multiUser) }),
|
|
956
|
+
/* @__PURE__ */ jsx13("span", { className: "block w-full truncate text-muted-foreground text-xs", children: multiUser?.email })
|
|
957
|
+
] })
|
|
958
|
+
] }),
|
|
959
|
+
/* @__PURE__ */ jsx13(DropdownMenuSeparator, {})
|
|
960
|
+
] }, session.id)),
|
|
961
|
+
/* @__PURE__ */ jsx13(DropdownMenuItem, { asChild: true, children: /* @__PURE__ */ jsxs12(Link4, { href: path.auth.SIGN_IN, children: [
|
|
962
|
+
/* @__PURE__ */ jsx13(PlusCircleIcon, {}),
|
|
963
|
+
t("common.actions.addAccount")
|
|
964
|
+
] }) })
|
|
965
|
+
] })
|
|
966
|
+
] })
|
|
967
|
+
] });
|
|
968
|
+
}
|
|
969
|
+
export {
|
|
970
|
+
ApiKeyView,
|
|
971
|
+
CardActionComponent,
|
|
972
|
+
CardComponent,
|
|
973
|
+
CardFooterComponent,
|
|
974
|
+
CardHeaderComponent,
|
|
975
|
+
DialogComponent,
|
|
976
|
+
DialogFooterComponent,
|
|
977
|
+
DisplayIdCard,
|
|
978
|
+
EmptyState,
|
|
979
|
+
LanguageSwitcher,
|
|
980
|
+
Logo as LogoDefault,
|
|
981
|
+
LogoWithHref,
|
|
982
|
+
LogoWithName,
|
|
983
|
+
OTPInputGroup,
|
|
984
|
+
PasswordInput,
|
|
985
|
+
SignedInHint,
|
|
986
|
+
SkeletonInputComponent,
|
|
987
|
+
SkeletonViewComponent,
|
|
988
|
+
UserAvatar,
|
|
989
|
+
UserMenu,
|
|
990
|
+
UserView,
|
|
991
|
+
WorkspaceLogo,
|
|
992
|
+
WorkspaceView
|
|
993
|
+
};
|
package/dist/style.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@source ".";
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pelatform/starter.shared",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A part of SaaS starter kit for Pelatform applications.",
|
|
5
|
+
"author": "Pelatform",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"sideEffects": false,
|
|
11
|
+
"exports": {
|
|
12
|
+
"./css": "./dist/style.css",
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"clean": "rimraf dist",
|
|
20
|
+
"clean:all": "rimraf .turbo dist node_modules",
|
|
21
|
+
"dev": "tsup --watch --onSuccess \"cp src/style.css dist/style.css\"",
|
|
22
|
+
"build": "tsup && cp src/style.css dist/style.css",
|
|
23
|
+
"types:check": "tsc --noEmit"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"keywords": [
|
|
29
|
+
"pelatform",
|
|
30
|
+
"starter kit",
|
|
31
|
+
"components",
|
|
32
|
+
"utilities"
|
|
33
|
+
],
|
|
34
|
+
"dependencies": {},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@pelatform/starter.config": "0.1.0",
|
|
37
|
+
"@pelatform/starter.hook": "0.1.1",
|
|
38
|
+
"@pelatform/starter.i18n": "0.1.2",
|
|
39
|
+
"@pelatform/starter.utils": "0.1.1",
|
|
40
|
+
"@pelatform/tsconfig": "^0.1.3",
|
|
41
|
+
"@types/react": "^19.2.7",
|
|
42
|
+
"lucide-react": "^0.556.0",
|
|
43
|
+
"next": "^16.0.7",
|
|
44
|
+
"next-intl": "^4.5.8",
|
|
45
|
+
"pelatform-ui": "^1.1.3",
|
|
46
|
+
"react": "^19.2.1",
|
|
47
|
+
"react-hook-form": "^7.68.0",
|
|
48
|
+
"tsup": "^8.5.1"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@pelatform/starter.config": ">=0.1.0",
|
|
52
|
+
"@pelatform/starter.hook": ">=0.1.0",
|
|
53
|
+
"@pelatform/starter.i18n": ">=0.1.0",
|
|
54
|
+
"@pelatform/starter.utils": ">=0.1.0",
|
|
55
|
+
"lucide-react": ">=0.55.0",
|
|
56
|
+
"next": ">=16.0.0",
|
|
57
|
+
"next-intl": ">=4.5.0",
|
|
58
|
+
"pelatform-ui": ">=1.1.0",
|
|
59
|
+
"react": ">=18.0.0 || >=19.0.0-rc.0",
|
|
60
|
+
"react-hook-form": ">=7.5.0"
|
|
61
|
+
},
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"registry": "https://registry.npmjs.org/",
|
|
64
|
+
"access": "public"
|
|
65
|
+
},
|
|
66
|
+
"lint-staged": {
|
|
67
|
+
"*.{js,jsx,ts,tsx,cjs,mjs,cts,mts}": "biome check --write --no-errors-on-unmatched",
|
|
68
|
+
"*.{md,yml,yaml}": "prettier --write",
|
|
69
|
+
"*.{json,jsonc,html}": "biome format --write --no-errors-on-unmatched"
|
|
70
|
+
}
|
|
71
|
+
}
|