generator-kodly-react-app 1.0.14 → 1.0.16
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/generators/app/index.js +4 -0
- package/generators/app/templates/.env.dev +1 -1
- package/generators/app/templates/.env.sandbox +2 -2
- package/generators/app/templates/I18N_MIGRATION.md +28 -35
- package/generators/app/templates/STRUCTURE.md +26 -26
- package/generators/app/templates/index.html +6 -14
- package/generators/app/templates/openapi-ts.config.ts +12 -12
- package/generators/app/templates/postcss.config.js +0 -1
- package/generators/app/templates/src/app.tsx +20 -20
- package/generators/app/templates/src/components/layout/language-switcher.tsx +20 -16
- package/generators/app/templates/src/components/layout/page-layout.tsx +31 -0
- package/generators/app/templates/src/components/layout/theme-switcher.tsx +15 -14
- package/generators/app/templates/src/components/theme/theme-provider.tsx +20 -13
- package/generators/app/templates/src/components/ui/button.tsx +15 -14
- package/generators/app/templates/src/components/ui/card.tsx +23 -16
- package/generators/app/templates/src/components/ui/field.tsx +35 -35
- package/generators/app/templates/src/components/ui/form.tsx +66 -30
- package/generators/app/templates/src/components/ui/input-group.tsx +24 -24
- package/generators/app/templates/src/components/ui/input.tsx +8 -8
- package/generators/app/templates/src/components/ui/label.tsx +9 -9
- package/generators/app/templates/src/components/ui/select.tsx +0 -1
- package/generators/app/templates/src/components/ui/separator.tsx +10 -10
- package/generators/app/templates/src/components/ui/textarea.tsx +7 -7
- package/generators/app/templates/src/index.css +12 -2
- package/generators/app/templates/src/lib/i18n.ts +16 -14
- package/generators/app/templates/src/lib/routes.ts +2 -3
- package/generators/app/templates/src/lib/utils/error-handler.ts +21 -14
- package/generators/app/templates/src/lib/utils.ts +3 -3
- package/generators/app/templates/src/locales/index.ts +12 -12
- package/generators/app/templates/src/locales/ja/theme.json +0 -1
- package/generators/app/templates/src/main.tsx +10 -12
- package/generators/app/templates/src/modules/app/layouts/app-layout.tsx +14 -25
- package/generators/app/templates/src/modules/app/locales/app-en.json +0 -1
- package/generators/app/templates/src/modules/app/locales/app-ja.json +0 -1
- package/generators/app/templates/src/modules/auth/components/auth-form.tsx +83 -0
- package/generators/app/templates/src/modules/auth/components/forgot-password-form.tsx +27 -52
- package/generators/app/templates/src/modules/auth/components/login-form.tsx +42 -72
- package/generators/app/templates/src/modules/auth/components/login-link.tsx +12 -0
- package/generators/app/templates/src/modules/auth/components/reset-password-form.tsx +46 -92
- package/generators/app/templates/src/modules/auth/components/signup-form.tsx +35 -71
- package/generators/app/templates/src/modules/auth/contexts/auth-context.tsx +10 -6
- package/generators/app/templates/src/modules/auth/hooks/use-auth-hook.ts +51 -36
- package/generators/app/templates/src/modules/auth/layouts/auth-layout.tsx +12 -15
- package/generators/app/templates/src/modules/auth/locales/auth-en.json +0 -1
- package/generators/app/templates/src/modules/auth/locales/auth-ja.json +0 -1
- package/generators/app/templates/src/modules/auth/schemas/form-schemas.ts +14 -10
- package/generators/app/templates/src/modules/landing/components/auth-hero.tsx +20 -18
- package/generators/app/templates/src/modules/landing/components/welcome-hero.tsx +13 -11
- package/generators/app/templates/src/modules/landing/landing-page-layout.tsx +1 -24
- package/generators/app/templates/src/modules/landing/landing-page.tsx +11 -6
- package/generators/app/templates/src/modules/landing/layouts/landing-page-layout.tsx +2 -21
- package/generators/app/templates/src/modules/landing/locales/landing-en.json +0 -1
- package/generators/app/templates/src/openapi-client-config.ts +1 -1
- package/generators/app/templates/src/routeTree.gen.ts +188 -188
- package/generators/app/templates/src/router.tsx +2 -2
- package/generators/app/templates/src/routes/$.tsx +0 -1
- package/generators/app/templates/src/routes/__root.tsx +2 -2
- package/generators/app/templates/src/routes/_landing/index.tsx +3 -3
- package/generators/app/templates/src/routes/_landing/route.tsx +3 -3
- package/generators/app/templates/src/routes/app/index.tsx +6 -6
- package/generators/app/templates/src/routes/app/route.tsx +4 -4
- package/generators/app/templates/src/routes/auth/forgot-password.tsx +1 -1
- package/generators/app/templates/src/routes/auth/login.tsx +3 -3
- package/generators/app/templates/src/routes/auth/reset-password.tsx +3 -3
- package/generators/app/templates/src/routes/auth/route.tsx +12 -7
- package/generators/app/templates/src/routes/auth/signup.tsx +1 -2
- package/generators/app/templates/src/sdk/@tanstack/react-query.gen.ts +157 -76
- package/generators/app/templates/src/sdk/client/client.gen.ts +35 -37
- package/generators/app/templates/src/sdk/client/index.ts +8 -8
- package/generators/app/templates/src/sdk/client/types.gen.ts +22 -19
- package/generators/app/templates/src/sdk/client/utils.gen.ts +53 -45
- package/generators/app/templates/src/sdk/client.gen.ts +18 -5
- package/generators/app/templates/src/sdk/core/auth.gen.ts +6 -6
- package/generators/app/templates/src/sdk/core/bodySerializer.gen.ts +25 -27
- package/generators/app/templates/src/sdk/core/params.gen.ts +12 -12
- package/generators/app/templates/src/sdk/core/pathSerializer.gen.ts +40 -41
- package/generators/app/templates/src/sdk/core/queryKeySerializer.gen.ts +12 -29
- package/generators/app/templates/src/sdk/core/serverSentEvents.gen.ts +26 -24
- package/generators/app/templates/src/sdk/core/types.gen.ts +17 -15
- package/generators/app/templates/src/sdk/core/utils.gen.ts +18 -18
- package/generators/app/templates/src/sdk/index.ts +40 -2
- package/generators/app/templates/src/sdk/schemas.gen.ts +108 -166
- package/generators/app/templates/src/sdk/sdk.gen.ts +145 -63
- package/generators/app/templates/src/sdk/types.gen.ts +90 -84
- package/generators/app/templates/src/sdk/zod.gen.ts +57 -74
- package/generators/app/templates/src/types/i18n.d.ts +0 -1
- package/generators/app/templates/tailwind.config.js +0 -1
- package/generators/app/templates/tsconfig.app.json +0 -1
- package/generators/app/templates/tsconfig.json +0 -1
- package/generators/app/templates/tsr.config.json +4 -0
- package/generators/app/templates/vite.config.js +13 -12
- package/package.json +2 -4
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
"use client"
|
|
1
|
+
"use client";
|
|
2
2
|
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
5
|
|
|
6
|
-
import { cn } from "@/lib/utils"
|
|
7
|
-
import { Button } from "@/components/ui/button"
|
|
8
|
-
import { Input } from "@/components/ui/input"
|
|
9
|
-
import { Textarea } from "@/components/ui/textarea"
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
import { Button } from "@/components/ui/button";
|
|
8
|
+
import { Input } from "@/components/ui/input";
|
|
9
|
+
import { Textarea } from "@/components/ui/textarea";
|
|
10
10
|
|
|
11
11
|
function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
12
12
|
return (
|
|
@@ -29,11 +29,11 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
29
29
|
// Error state.
|
|
30
30
|
"has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",
|
|
31
31
|
|
|
32
|
-
className
|
|
32
|
+
className,
|
|
33
33
|
)}
|
|
34
34
|
{...props}
|
|
35
35
|
/>
|
|
36
|
-
)
|
|
36
|
+
);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
const inputGroupAddonVariants = cva(
|
|
@@ -54,8 +54,8 @@ const inputGroupAddonVariants = cva(
|
|
|
54
54
|
defaultVariants: {
|
|
55
55
|
align: "inline-start",
|
|
56
56
|
},
|
|
57
|
-
}
|
|
58
|
-
)
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
59
|
|
|
60
60
|
function InputGroupAddon({
|
|
61
61
|
className,
|
|
@@ -70,13 +70,13 @@ function InputGroupAddon({
|
|
|
70
70
|
className={cn(inputGroupAddonVariants({ align }), className)}
|
|
71
71
|
onClick={(e) => {
|
|
72
72
|
if ((e.target as HTMLElement).closest("button")) {
|
|
73
|
-
return
|
|
73
|
+
return;
|
|
74
74
|
}
|
|
75
|
-
e.currentTarget.parentElement?.querySelector("input")?.focus()
|
|
75
|
+
e.currentTarget.parentElement?.querySelector("input")?.focus();
|
|
76
76
|
}}
|
|
77
77
|
{...props}
|
|
78
78
|
/>
|
|
79
|
-
)
|
|
79
|
+
);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
const inputGroupButtonVariants = cva(
|
|
@@ -94,8 +94,8 @@ const inputGroupButtonVariants = cva(
|
|
|
94
94
|
defaultVariants: {
|
|
95
95
|
size: "xs",
|
|
96
96
|
},
|
|
97
|
-
}
|
|
98
|
-
)
|
|
97
|
+
},
|
|
98
|
+
);
|
|
99
99
|
|
|
100
100
|
function InputGroupButton({
|
|
101
101
|
className,
|
|
@@ -113,7 +113,7 @@ function InputGroupButton({
|
|
|
113
113
|
className={cn(inputGroupButtonVariants({ size }), className)}
|
|
114
114
|
{...props}
|
|
115
115
|
/>
|
|
116
|
-
)
|
|
116
|
+
);
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
|
|
@@ -121,11 +121,11 @@ function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
|
|
|
121
121
|
<span
|
|
122
122
|
className={cn(
|
|
123
123
|
"text-muted-foreground flex items-center gap-2 text-sm [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none",
|
|
124
|
-
className
|
|
124
|
+
className,
|
|
125
125
|
)}
|
|
126
126
|
{...props}
|
|
127
127
|
/>
|
|
128
|
-
)
|
|
128
|
+
);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
function InputGroupInput({
|
|
@@ -137,11 +137,11 @@ function InputGroupInput({
|
|
|
137
137
|
data-slot="input-group-control"
|
|
138
138
|
className={cn(
|
|
139
139
|
"flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent",
|
|
140
|
-
className
|
|
140
|
+
className,
|
|
141
141
|
)}
|
|
142
142
|
{...props}
|
|
143
143
|
/>
|
|
144
|
-
)
|
|
144
|
+
);
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
function InputGroupTextarea({
|
|
@@ -153,11 +153,11 @@ function InputGroupTextarea({
|
|
|
153
153
|
data-slot="input-group-control"
|
|
154
154
|
className={cn(
|
|
155
155
|
"flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent",
|
|
156
|
-
className
|
|
156
|
+
className,
|
|
157
157
|
)}
|
|
158
158
|
{...props}
|
|
159
159
|
/>
|
|
160
|
-
)
|
|
160
|
+
);
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
export {
|
|
@@ -167,4 +167,4 @@ export {
|
|
|
167
167
|
InputGroupText,
|
|
168
168
|
InputGroupInput,
|
|
169
169
|
InputGroupTextarea,
|
|
170
|
-
}
|
|
170
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as React from "react"
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
import { cn } from "@/lib/utils"
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
4
|
|
|
5
5
|
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
|
6
6
|
({ className, type, ...props }, ref) => {
|
|
@@ -9,14 +9,14 @@ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
|
|
9
9
|
type={type}
|
|
10
10
|
className={cn(
|
|
11
11
|
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
12
|
-
className
|
|
12
|
+
className,
|
|
13
13
|
)}
|
|
14
14
|
ref={ref}
|
|
15
15
|
{...props}
|
|
16
16
|
/>
|
|
17
|
-
)
|
|
18
|
-
}
|
|
19
|
-
)
|
|
20
|
-
Input.displayName = "Input"
|
|
17
|
+
);
|
|
18
|
+
},
|
|
19
|
+
);
|
|
20
|
+
Input.displayName = "Input";
|
|
21
21
|
|
|
22
|
-
export { Input }
|
|
22
|
+
export { Input };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import * as LabelPrimitive from "@radix-ui/react-label"
|
|
3
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
4
|
|
|
5
|
-
import { cn } from "@/lib/utils"
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
6
|
|
|
7
7
|
const labelVariants = cva(
|
|
8
|
-
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
9
|
-
)
|
|
8
|
+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
|
9
|
+
);
|
|
10
10
|
|
|
11
11
|
const Label = React.forwardRef<
|
|
12
12
|
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
@@ -18,7 +18,7 @@ const Label = React.forwardRef<
|
|
|
18
18
|
className={cn(labelVariants(), className)}
|
|
19
19
|
{...props}
|
|
20
20
|
/>
|
|
21
|
-
))
|
|
22
|
-
Label.displayName = LabelPrimitive.Root.displayName
|
|
21
|
+
));
|
|
22
|
+
Label.displayName = LabelPrimitive.Root.displayName;
|
|
23
23
|
|
|
24
|
-
export { Label }
|
|
24
|
+
export { Label };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
"use client"
|
|
1
|
+
"use client";
|
|
2
2
|
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import * as SeparatorPrimitive from "@radix-ui/react-separator"
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as SeparatorPrimitive from "@radix-ui/react-separator";
|
|
5
5
|
|
|
6
|
-
import { cn } from "@/lib/utils"
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
7
|
|
|
8
8
|
const Separator = React.forwardRef<
|
|
9
9
|
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
|
@@ -11,7 +11,7 @@ const Separator = React.forwardRef<
|
|
|
11
11
|
>(
|
|
12
12
|
(
|
|
13
13
|
{ className, orientation = "horizontal", decorative = true, ...props },
|
|
14
|
-
ref
|
|
14
|
+
ref,
|
|
15
15
|
) => (
|
|
16
16
|
<SeparatorPrimitive.Root
|
|
17
17
|
ref={ref}
|
|
@@ -20,12 +20,12 @@ const Separator = React.forwardRef<
|
|
|
20
20
|
className={cn(
|
|
21
21
|
"shrink-0 bg-border",
|
|
22
22
|
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
|
|
23
|
-
className
|
|
23
|
+
className,
|
|
24
24
|
)}
|
|
25
25
|
{...props}
|
|
26
26
|
/>
|
|
27
|
-
)
|
|
28
|
-
)
|
|
29
|
-
Separator.displayName = SeparatorPrimitive.Root.displayName
|
|
27
|
+
),
|
|
28
|
+
);
|
|
29
|
+
Separator.displayName = SeparatorPrimitive.Root.displayName;
|
|
30
30
|
|
|
31
|
-
export { Separator }
|
|
31
|
+
export { Separator };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as React from "react"
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
import { cn } from "@/lib/utils"
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
4
|
|
|
5
5
|
const Textarea = React.forwardRef<
|
|
6
6
|
HTMLTextAreaElement,
|
|
@@ -10,13 +10,13 @@ const Textarea = React.forwardRef<
|
|
|
10
10
|
<textarea
|
|
11
11
|
className={cn(
|
|
12
12
|
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
13
|
-
className
|
|
13
|
+
className,
|
|
14
14
|
)}
|
|
15
15
|
ref={ref}
|
|
16
16
|
{...props}
|
|
17
17
|
/>
|
|
18
|
-
)
|
|
19
|
-
})
|
|
20
|
-
Textarea.displayName = "Textarea"
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
Textarea.displayName = "Textarea";
|
|
21
21
|
|
|
22
|
-
export { Textarea }
|
|
22
|
+
export { Textarea };
|
|
@@ -7,7 +7,18 @@
|
|
|
7
7
|
|
|
8
8
|
body {
|
|
9
9
|
@apply m-0;
|
|
10
|
-
font-family:
|
|
10
|
+
font-family:
|
|
11
|
+
system-ui,
|
|
12
|
+
-apple-system,
|
|
13
|
+
"Segoe UI",
|
|
14
|
+
"Roboto",
|
|
15
|
+
"Oxygen",
|
|
16
|
+
"Ubuntu",
|
|
17
|
+
"Cantarell",
|
|
18
|
+
"Fira Sans",
|
|
19
|
+
"Droid Sans",
|
|
20
|
+
"Helvetica Neue",
|
|
21
|
+
sans-serif;
|
|
11
22
|
-webkit-font-smoothing: antialiased;
|
|
12
23
|
-moz-osx-font-smoothing: grayscale;
|
|
13
24
|
}
|
|
@@ -133,4 +144,3 @@ body {
|
|
|
133
144
|
@apply bg-background text-foreground;
|
|
134
145
|
}
|
|
135
146
|
}
|
|
136
|
-
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import i18n from
|
|
2
|
-
import { initReactI18next } from
|
|
3
|
-
import { mergeTranslations } from
|
|
4
|
-
import * as z from
|
|
5
|
-
import { setLocaleInClient } from
|
|
1
|
+
import i18n from "i18next";
|
|
2
|
+
import { initReactI18next } from "react-i18next";
|
|
3
|
+
import { mergeTranslations } from "@/locales";
|
|
4
|
+
import * as z from "zod";
|
|
5
|
+
import { setLocaleInClient } from "@/modules/auth/hooks/use-auth-hook";
|
|
6
6
|
|
|
7
|
-
export type SupportedLanguages =
|
|
8
|
-
const supportedLanguages: SupportedLanguages[] = [
|
|
9
|
-
const defaultLanguage: SupportedLanguages =
|
|
10
|
-
const localStorageKey =
|
|
7
|
+
export type SupportedLanguages = "en" | "ja";
|
|
8
|
+
const supportedLanguages: SupportedLanguages[] = ["en", "ja"];
|
|
9
|
+
const defaultLanguage: SupportedLanguages = "en";
|
|
10
|
+
const localStorageKey = "app-language";
|
|
11
11
|
|
|
12
12
|
const setLanguageHooks = (language: SupportedLanguages) => {
|
|
13
13
|
setLocaleInClient(language);
|
|
14
|
-
z.config(
|
|
14
|
+
z.config(
|
|
15
|
+
(z.locales[language] ?? z.locales[defaultLanguage] ?? z.locales["en"])(),
|
|
16
|
+
);
|
|
15
17
|
};
|
|
16
18
|
|
|
17
19
|
const getStoredLanguage = (): SupportedLanguages => {
|
|
@@ -29,8 +31,8 @@ const getStoredLanguage = (): SupportedLanguages => {
|
|
|
29
31
|
|
|
30
32
|
if (!i18n.isInitialized) {
|
|
31
33
|
const lng = getStoredLanguage();
|
|
32
|
-
const enResources = mergeTranslations(
|
|
33
|
-
const jaResources = mergeTranslations(
|
|
34
|
+
const enResources = mergeTranslations("en");
|
|
35
|
+
const jaResources = mergeTranslations("ja");
|
|
34
36
|
i18n.use(initReactI18next).init({
|
|
35
37
|
resources: {
|
|
36
38
|
en: {
|
|
@@ -42,8 +44,8 @@ if (!i18n.isInitialized) {
|
|
|
42
44
|
...jaResources,
|
|
43
45
|
},
|
|
44
46
|
},
|
|
45
|
-
defaultNS:
|
|
46
|
-
ns: [
|
|
47
|
+
defaultNS: "translation",
|
|
48
|
+
ns: ["translation", "auth", "app", "landing", "common", "theme"],
|
|
47
49
|
supportedLngs: supportedLanguages,
|
|
48
50
|
lng,
|
|
49
51
|
fallbackLng: defaultLanguage,
|
|
@@ -5,10 +5,9 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* Default route for authenticated users
|
|
7
7
|
*/
|
|
8
|
-
export const DEFAULT_LOGGED_IN_ROUTE =
|
|
8
|
+
export const DEFAULT_LOGGED_IN_ROUTE = "/app";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Default route for non-authenticated users (landing page)
|
|
12
12
|
*/
|
|
13
|
-
export const DEFAULT_NON_LOGGED_IN_ROUTE =
|
|
14
|
-
|
|
13
|
+
export const DEFAULT_NON_LOGGED_IN_ROUTE = "/";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { toast } from
|
|
2
|
-
import { AxiosError } from
|
|
1
|
+
import { toast } from "sonner";
|
|
2
|
+
import { AxiosError } from "axios";
|
|
3
3
|
|
|
4
4
|
interface BackendErrorResponse {
|
|
5
5
|
message?: string;
|
|
@@ -22,7 +22,7 @@ interface BackendErrorResponse {
|
|
|
22
22
|
*/
|
|
23
23
|
export const handleBackendError = (error: unknown): void => {
|
|
24
24
|
// Check if it's an AxiosError
|
|
25
|
-
if (error instanceof Error &&
|
|
25
|
+
if (error instanceof Error && "isAxiosError" in error) {
|
|
26
26
|
const axiosError = error as AxiosError<BackendErrorResponse>;
|
|
27
27
|
const response = axiosError.response;
|
|
28
28
|
|
|
@@ -31,46 +31,53 @@ export const handleBackendError = (error: unknown): void => {
|
|
|
31
31
|
const errorData = response.data;
|
|
32
32
|
|
|
33
33
|
// Check if the response matches the expected format
|
|
34
|
-
if (
|
|
34
|
+
if (
|
|
35
|
+
errorData &&
|
|
36
|
+
typeof errorData === "object" &&
|
|
37
|
+
"message" in errorData &&
|
|
38
|
+
"status" in errorData
|
|
39
|
+
) {
|
|
35
40
|
// Use the message from the backend response
|
|
36
|
-
const errorMessage = errorData.message ||
|
|
41
|
+
const errorMessage = errorData.message || "An error occurred";
|
|
37
42
|
toast.error(errorMessage);
|
|
38
43
|
return;
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
// If the response doesn't match the expected format,
|
|
42
47
|
// try to extract a message from the response data
|
|
43
|
-
if (errorData && typeof errorData ===
|
|
48
|
+
if (errorData && typeof errorData === "object") {
|
|
44
49
|
const errorObj = errorData as Record<string, unknown>;
|
|
45
50
|
const message =
|
|
46
|
-
(typeof errorObj.message ===
|
|
47
|
-
(typeof errorObj.error ===
|
|
51
|
+
(typeof errorObj.message === "string" ? errorObj.message : null) ||
|
|
52
|
+
(typeof errorObj.error === "string" ? errorObj.error : null) ||
|
|
48
53
|
response.statusText ||
|
|
49
|
-
|
|
54
|
+
"An error occurred";
|
|
50
55
|
toast.error(message);
|
|
51
56
|
return;
|
|
52
57
|
}
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
// Handle network errors (no response) - connection refused, timeout, etc.
|
|
56
|
-
if (axiosError.code ===
|
|
57
|
-
const networkErrorMessage =
|
|
61
|
+
if (axiosError.code === "ERR_NETWORK" || !response) {
|
|
62
|
+
const networkErrorMessage =
|
|
63
|
+
axiosError.message ||
|
|
64
|
+
"Network error. Please check your connection and try again.";
|
|
58
65
|
toast.error(networkErrorMessage);
|
|
59
66
|
return;
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
// Fallback for other AxiosError cases
|
|
63
|
-
const errorMessage = axiosError.message ||
|
|
70
|
+
const errorMessage = axiosError.message || "An error occurred";
|
|
64
71
|
toast.error(errorMessage);
|
|
65
72
|
return;
|
|
66
73
|
}
|
|
67
74
|
|
|
68
75
|
// If it's not an axios error, show a generic message
|
|
69
76
|
if (error instanceof Error) {
|
|
70
|
-
toast.error(error.message ||
|
|
77
|
+
toast.error(error.message || "An error occurred");
|
|
71
78
|
return;
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
// Final fallback
|
|
75
|
-
toast.error(
|
|
82
|
+
toast.error("An unexpected error occurred");
|
|
76
83
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { clsx, type ClassValue } from "clsx"
|
|
2
|
-
import { twMerge } from "tailwind-merge"
|
|
1
|
+
import { clsx, type ClassValue } from "clsx";
|
|
2
|
+
import { twMerge } from "tailwind-merge";
|
|
3
3
|
|
|
4
4
|
export function cn(...inputs: ClassValue[]) {
|
|
5
|
-
return twMerge(clsx(inputs))
|
|
5
|
+
return twMerge(clsx(inputs));
|
|
6
6
|
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { SupportedLanguages } from
|
|
2
|
-
import enAuth from
|
|
3
|
-
import enApp from
|
|
4
|
-
import enLanding from
|
|
5
|
-
import enCommon from
|
|
6
|
-
import enTheme from
|
|
7
|
-
import jaAuth from
|
|
8
|
-
import jaApp from
|
|
9
|
-
import jaLanding from
|
|
10
|
-
import jaCommon from
|
|
11
|
-
import jaTheme from
|
|
1
|
+
import { SupportedLanguages } from "@/lib/i18n";
|
|
2
|
+
import enAuth from "@/modules/auth/locales/auth-en.json";
|
|
3
|
+
import enApp from "@/modules/app/locales/app-en.json";
|
|
4
|
+
import enLanding from "@/modules/landing/locales/landing-en.json";
|
|
5
|
+
import enCommon from "@/locales/en/common.json";
|
|
6
|
+
import enTheme from "@/locales/en/theme.json";
|
|
7
|
+
import jaAuth from "@/modules/auth/locales/auth-ja.json";
|
|
8
|
+
import jaApp from "@/modules/app/locales/app-ja.json";
|
|
9
|
+
import jaLanding from "@/modules/landing/locales/landing-ja.json";
|
|
10
|
+
import jaCommon from "@/locales/ja/common.json";
|
|
11
|
+
import jaTheme from "@/locales/ja/theme.json";
|
|
12
12
|
|
|
13
13
|
export const mergeTranslations = (lang: SupportedLanguages) => {
|
|
14
|
-
if (lang ===
|
|
14
|
+
if (lang === "en") {
|
|
15
15
|
return {
|
|
16
16
|
auth: { ...enAuth },
|
|
17
17
|
app: { ...enApp },
|
|
@@ -1,27 +1,25 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import ReactDOM from
|
|
3
|
-
import { I18nextProvider } from
|
|
4
|
-
import i18n from
|
|
5
|
-
import { ThemeProvider } from
|
|
6
|
-
import { QueryClient, QueryClientProvider } from
|
|
7
|
-
import App from
|
|
1
|
+
import React from "react";
|
|
2
|
+
import ReactDOM from "react-dom/client";
|
|
3
|
+
import { I18nextProvider } from "react-i18next";
|
|
4
|
+
import i18n from "@/lib/i18n";
|
|
5
|
+
import { ThemeProvider } from "@/components/theme/theme-provider";
|
|
6
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
7
|
+
import App from "@/app";
|
|
8
8
|
|
|
9
9
|
const queryClient = new QueryClient();
|
|
10
10
|
|
|
11
|
-
const rootElement = document.getElementById(
|
|
11
|
+
const rootElement = document.getElementById("app");
|
|
12
12
|
if (rootElement && !rootElement.innerHTML) {
|
|
13
13
|
const root = ReactDOM.createRoot(rootElement);
|
|
14
14
|
root.render(
|
|
15
15
|
<React.StrictMode>
|
|
16
16
|
<I18nextProvider i18n={i18n}>
|
|
17
|
-
<ThemeProvider
|
|
18
|
-
defaultTheme='light'
|
|
19
|
-
storageKey='vite-ui-theme'>
|
|
17
|
+
<ThemeProvider defaultTheme="light" storageKey="vite-ui-theme">
|
|
20
18
|
<QueryClientProvider client={queryClient}>
|
|
21
19
|
<App />
|
|
22
20
|
</QueryClientProvider>
|
|
23
21
|
</ThemeProvider>
|
|
24
22
|
</I18nextProvider>
|
|
25
|
-
</React.StrictMode
|
|
23
|
+
</React.StrictMode>,
|
|
26
24
|
);
|
|
27
25
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { useTranslation } from 'react-i18next';
|
|
1
|
+
import { PageLayout } from "@/components/layout/page-layout";
|
|
2
|
+
import { Button } from "@/components/ui/button";
|
|
3
|
+
import { useLogout } from "@/modules/auth/hooks/use-auth-hook";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
6
5
|
|
|
7
6
|
export function AppLayout({ children }: { children: React.ReactNode }) {
|
|
8
7
|
const { t } = useTranslation();
|
|
@@ -13,25 +12,15 @@ export function AppLayout({ children }: { children: React.ReactNode }) {
|
|
|
13
12
|
};
|
|
14
13
|
|
|
15
14
|
return (
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
onClick={handleLogout}
|
|
27
|
-
disabled={isPending}
|
|
28
|
-
variant='outline'>
|
|
29
|
-
{t('app.welcome.logout')}
|
|
30
|
-
</Button>
|
|
31
|
-
</div>
|
|
32
|
-
</div>
|
|
33
|
-
</header>
|
|
34
|
-
<main className='container mx-auto p-8'>{children}</main>
|
|
35
|
-
</div>
|
|
15
|
+
<PageLayout
|
|
16
|
+
mainClassName="container mx-auto p-8"
|
|
17
|
+
actions={
|
|
18
|
+
<Button onClick={handleLogout} disabled={isPending} variant="outline">
|
|
19
|
+
{t("app.welcome.logout")}
|
|
20
|
+
</Button>
|
|
21
|
+
}
|
|
22
|
+
>
|
|
23
|
+
{children}
|
|
24
|
+
</PageLayout>
|
|
36
25
|
);
|
|
37
26
|
}
|