generator-kodly-react-app 1.0.7 → 1.0.11

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 (101) hide show
  1. package/generators/app/index.js +63 -18
  2. package/generators/app/templates/.env.dev +4 -0
  3. package/generators/app/templates/.env.sandbox +4 -0
  4. package/generators/app/templates/STRUCTURE.md +661 -0
  5. package/generators/app/templates/components.json +22 -0
  6. package/generators/app/templates/gitignore.template +78 -2
  7. package/generators/app/templates/index.html +14 -6
  8. package/generators/app/templates/openapi-ts.config.ts +29 -0
  9. package/generators/app/templates/package.json +40 -26
  10. package/generators/app/templates/public/favicon.svg +4 -0
  11. package/generators/app/templates/src/app.tsx +8 -8
  12. package/generators/app/templates/src/components/layout/language-switcher.tsx +40 -0
  13. package/generators/app/templates/src/components/layout/theme-switcher.tsx +37 -0
  14. package/generators/app/templates/src/components/theme/theme-provider.tsx +22 -28
  15. package/generators/app/templates/src/components/ui/button.tsx +34 -32
  16. package/generators/app/templates/src/components/ui/card.tsx +76 -0
  17. package/generators/app/templates/src/components/ui/field.tsx +242 -0
  18. package/generators/app/templates/src/components/ui/form.tsx +129 -0
  19. package/generators/app/templates/src/components/ui/input-group.tsx +170 -0
  20. package/generators/app/templates/src/components/ui/input.tsx +19 -21
  21. package/generators/app/templates/src/components/ui/label.tsx +24 -0
  22. package/generators/app/templates/src/components/ui/select.tsx +184 -0
  23. package/generators/app/templates/src/components/ui/separator.tsx +31 -0
  24. package/generators/app/templates/src/components/ui/textarea.tsx +22 -0
  25. package/generators/app/templates/src/index.css +83 -26
  26. package/generators/app/templates/src/lib/i18n.ts +31 -16
  27. package/generators/app/templates/src/lib/routes.ts +14 -0
  28. package/generators/app/templates/src/lib/utils.ts +3 -4
  29. package/generators/app/templates/src/locales/en/common.json +5 -0
  30. package/generators/app/templates/src/locales/en/theme.json +5 -0
  31. package/generators/app/templates/src/locales/index.ts +31 -0
  32. package/generators/app/templates/src/locales/ja/common.json +5 -0
  33. package/generators/app/templates/src/locales/ja/theme.json +6 -0
  34. package/generators/app/templates/src/main.tsx +19 -15
  35. package/generators/app/templates/src/modules/app/layouts/app-layout.tsx +37 -0
  36. package/generators/app/templates/src/modules/app/locales/app-en.json +9 -0
  37. package/generators/app/templates/src/modules/app/locales/app-ja.json +9 -0
  38. package/generators/app/templates/src/modules/auth/components/forgot-password-form.tsx +74 -0
  39. package/generators/app/templates/src/modules/auth/components/login-form.tsx +95 -0
  40. package/generators/app/templates/src/modules/auth/components/reset-password-form.tsx +112 -0
  41. package/generators/app/templates/src/modules/auth/components/signup-form.tsx +92 -0
  42. package/generators/app/templates/src/modules/auth/{auth-context.tsx → contexts/auth-context.tsx} +3 -3
  43. package/generators/app/templates/src/modules/auth/hooks/use-auth-hook.ts +180 -0
  44. package/generators/app/templates/src/modules/auth/layouts/auth-layout.tsx +28 -0
  45. package/generators/app/templates/src/modules/auth/locales/auth-en.json +105 -0
  46. package/generators/app/templates/src/modules/auth/locales/auth-ja.json +105 -0
  47. package/generators/app/templates/src/modules/auth/schemas/form-schemas.ts +26 -0
  48. package/generators/app/templates/src/modules/landing/components/auth-hero.tsx +34 -0
  49. package/generators/app/templates/src/modules/landing/components/welcome-hero.tsx +24 -0
  50. package/generators/app/templates/src/modules/landing/landing-page-layout.tsx +24 -0
  51. package/generators/app/templates/src/modules/landing/landing-page.tsx +17 -0
  52. package/generators/app/templates/src/modules/landing/layouts/landing-page-layout.tsx +24 -0
  53. package/generators/app/templates/src/modules/landing/locales/landing-en.json +12 -0
  54. package/generators/app/templates/src/modules/landing/locales/landing-ja.json +11 -0
  55. package/generators/app/templates/src/openapi-client-config.ts +6 -0
  56. package/generators/app/templates/src/routeTree.gen.ts +268 -3
  57. package/generators/app/templates/src/router.tsx +2 -2
  58. package/generators/app/templates/src/routes/__root.tsx +2 -2
  59. package/generators/app/templates/src/routes/_landing/index.tsx +10 -0
  60. package/generators/app/templates/src/routes/_landing/route.tsx +14 -0
  61. package/generators/app/templates/src/routes/app/index.tsx +4 -21
  62. package/generators/app/templates/src/routes/app/route.tsx +12 -8
  63. package/generators/app/templates/src/routes/auth/forgot-password.tsx +10 -0
  64. package/generators/app/templates/src/routes/auth/login.tsx +6 -7
  65. package/generators/app/templates/src/routes/auth/reset-password.tsx +15 -0
  66. package/generators/app/templates/src/routes/auth/route.tsx +23 -6
  67. package/generators/app/templates/src/routes/auth/signup.tsx +11 -0
  68. package/generators/app/templates/src/sdk/@tanstack/react-query.gen.ts +91 -0
  69. package/generators/app/templates/src/sdk/client/client.gen.ts +167 -0
  70. package/generators/app/templates/src/sdk/client/index.ts +23 -0
  71. package/generators/app/templates/src/sdk/client/types.gen.ts +197 -0
  72. package/generators/app/templates/src/sdk/client/utils.gen.ts +213 -0
  73. package/generators/app/templates/src/sdk/client.gen.ts +18 -0
  74. package/generators/app/templates/src/sdk/core/auth.gen.ts +42 -0
  75. package/generators/app/templates/src/sdk/core/bodySerializer.gen.ts +100 -0
  76. package/generators/app/templates/src/sdk/core/params.gen.ts +176 -0
  77. package/generators/app/templates/src/sdk/core/pathSerializer.gen.ts +181 -0
  78. package/generators/app/templates/src/sdk/core/queryKeySerializer.gen.ts +136 -0
  79. package/generators/app/templates/src/sdk/core/serverSentEvents.gen.ts +266 -0
  80. package/generators/app/templates/src/sdk/core/types.gen.ts +118 -0
  81. package/generators/app/templates/src/sdk/core/utils.gen.ts +143 -0
  82. package/generators/app/templates/src/sdk/index.ts +4 -0
  83. package/generators/app/templates/src/sdk/schemas.gen.ts +195 -0
  84. package/generators/app/templates/src/sdk/sdk.gen.ts +80 -0
  85. package/generators/app/templates/src/sdk/types.gen.ts +158 -0
  86. package/generators/app/templates/src/sdk/zod.gen.ts +148 -0
  87. package/generators/app/templates/src/vite-env.d.ts +2 -1
  88. package/generators/app/templates/tsconfig.json +1 -1
  89. package/generators/app/templates/vite.config.js +35 -21
  90. package/generators/constants.js +1 -1
  91. package/package.json +3 -2
  92. package/generators/app/templates/.env.example +0 -5
  93. package/generators/app/templates/README.md +0 -62
  94. package/generators/app/templates/src/lib/api/client.ts +0 -35
  95. package/generators/app/templates/src/locales/en.json +0 -18
  96. package/generators/app/templates/src/modules/auth/auth-types.ts +0 -24
  97. package/generators/app/templates/src/modules/auth/login/login-form.tsx +0 -49
  98. package/generators/app/templates/src/modules/auth/login/login-page.tsx +0 -12
  99. package/generators/app/templates/src/modules/auth/use-auth-hook.ts +0 -88
  100. package/generators/app/templates/src/routes/index.tsx +0 -12
  101. package/generators/app/templates/types.d.ts +0 -3
@@ -1,10 +1,86 @@
1
+ # Dependencies
1
2
  node_modules
2
- .DS_Store
3
+ .pnp
4
+ .pnp.js
5
+ .yarn/cache
6
+ .yarn/unplugged
7
+ .yarn/build-state.yml
8
+ .yarn/install-state.gz
9
+
10
+ # Build outputs
3
11
  dist
4
12
  dist-ssr
5
13
  dist-electron
6
- *.local
14
+ build
15
+ out
16
+ .next
17
+ .nuxt
18
+ .output
19
+
20
+ # Environment variables
7
21
  .env
8
22
  .env.local
23
+ .env.development.local
24
+ .env.test.local
25
+ .env.production.local
9
26
  .env.*.local
27
+ *.local
28
+
29
+ # Logs
30
+ logs
31
+ *.log
32
+ npm-debug.log*
33
+ yarn-debug.log*
34
+ yarn-error.log*
35
+ pnpm-debug.log*
36
+ lerna-debug.log*
37
+
38
+ # OS files
39
+ .DS_Store
40
+ .DS_Store?
41
+ ._*
42
+ .Spotlight-V100
43
+ .Trashes
44
+ ehthumbs.db
45
+ Thumbs.db
46
+ Desktop.ini
47
+
48
+ # IDE and editors
49
+ .vscode/*
50
+ !.vscode/extensions.json
51
+ !.vscode/settings.json
52
+ .idea
53
+ *.swp
54
+ *.swo
55
+ *~
56
+ .project
57
+ .classpath
58
+ .settings/
59
+ *.sublime-project
60
+ *.sublime-workspace
61
+
62
+ # Testing
63
+ coverage
64
+ *.lcov
65
+ .nyc_output
66
+ .jest
67
+ .cache
68
+
69
+ # TypeScript
70
+ *.tsbuildinfo
71
+ .tsbuildinfo
72
+
73
+ # Temporary files
74
+ *.tmp
75
+ *.temp
76
+ .cache
77
+ .parcel-cache
78
+ .turbo
79
+
80
+ # Package manager
81
+ .pnpm-store
10
82
 
83
+ # Misc
84
+ *.pem
85
+ .vite
86
+ .swc
@@ -4,15 +4,23 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta
6
6
  name="viewport"
7
- content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
8
- />
9
- <meta name="theme-color" content="#000000" />
10
- <meta name="description" content="<%= appNameTitleCase %> Application" />
7
+ content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
8
+ <meta
9
+ name="theme-color"
10
+ content="#000000" />
11
+ <meta
12
+ name="description"
13
+ content="<%= appNameTitleCase %>" />
14
+ <link
15
+ rel="icon"
16
+ type="image/svg+xml"
17
+ href="/favicon.svg" />
11
18
  <title><%= appNameTitleCase %></title>
12
19
  </head>
13
20
  <body>
14
21
  <div id="app"></div>
15
- <script type="module" src="/src/main.tsx"></script>
22
+ <script
23
+ type="module"
24
+ src="/src/main.tsx"></script>
16
25
  </body>
17
26
  </html>
18
-
@@ -0,0 +1,29 @@
1
+ import { defineConfig } from '@hey-api/openapi-ts';
2
+ import { loadEnv } from 'vite';
3
+
4
+ // Get the mode from environment variable (set by npm scripts)
5
+ const mode = process.env.MODE || 'development';
6
+
7
+ // Load environment variables from .env files
8
+ const env = loadEnv(mode, process.cwd(), '');
9
+
10
+ export default defineConfig({
11
+ input: `${env.VITE_API_BASE_URL}/v3/api-docs`,
12
+ output: 'src/sdk',
13
+ plugins: [
14
+ {
15
+ name: '@hey-api/client-axios',
16
+ runtimeConfigPath: '@/openapi-client-config.ts',
17
+ },
18
+ '@tanstack/react-query',
19
+ 'zod',
20
+ {
21
+ name: '@hey-api/sdk',
22
+ validator: true,
23
+ },
24
+ {
25
+ name: '@hey-api/schemas',
26
+ type: 'json',
27
+ },
28
+ ],
29
+ });
@@ -1,47 +1,61 @@
1
1
  {
2
- "name": "<%= appNameSlug %>",
2
+ "name": "app",
3
3
  "private": true,
4
4
  "type": "module",
5
5
  "version": "0.0.1",
6
6
  "scripts": {
7
7
  "generate-routes": "tsr generate",
8
8
  "watch-routes": "tsr watch",
9
- "vite:dev": "vite",
9
+ "vite": "vite",
10
+ "vite:dev": "vite --mode dev",
11
+ "vite:sandbox": "vite --mode sandbox",
10
12
  "dev": "npm-run-all --parallel watch-routes vite:dev",
11
- "start": "dev",
12
- "build": "npm run generate-routes && vite build && tsc",
13
- "serve": "vite preview"
13
+ "dev:sandbox": "npm-run-all --parallel watch-routes vite:sandbox",
14
+ "build": "npm run generate-routes && vite build --mode dev && tsc",
15
+ "build:sandbox": "npm run generate-routes && vite build --mode sandbox && tsc",
16
+ "serve": "vite preview",
17
+ "openapi-ts:dev": "MODE=dev openapi-ts",
18
+ "openapi-ts:sandbox": "MODE=sandbox openapi-ts"
14
19
  },
15
20
  "dependencies": {
21
+ "@hey-api/openapi-ts": "^0.90.2",
22
+ "@hookform/resolvers": "^5.2.2",
23
+ "@radix-ui/react-label": "^2.1.8",
24
+ "@radix-ui/react-select": "^2.2.6",
25
+ "@radix-ui/react-separator": "^1.1.8",
16
26
  "@radix-ui/react-slot": "^1.2.4",
17
- "@tanstack/react-query": "<%= TANSTACK_REACT_QUERY_VERSION %>",
18
- "@tanstack/react-router": "<%= TANSTACK_ROUTER_VERSION %>",
19
- "@tanstack/router-plugin": "<%= TANSTACK_ROUTER_PLUGIN_VERSION %>",
20
- "axios": "<%= AXIOS_VERSION %>",
27
+ "@tanstack/react-query": "^5.90.16",
28
+ "@tanstack/react-router": "^1.145.7",
29
+ "@tanstack/router-plugin": "^1.145.7",
30
+ "axios": "^1.13.2",
21
31
  "class-variance-authority": "^0.7.1",
22
32
  "clsx": "^2.1.1",
23
- "dotenv": "<%= DOTENV_VERSION %>",
24
- "i18next": "<%= I18NEXT_VERSION %>",
25
- "jotai": "<%= JOTAI_VERSION %>",
26
- "lucide-react": "^0.555.0",
27
- "react": "<%= REACT_VERSION %>",
28
- "react-dom": "<%= REACT_DOM_VERSION %>",
29
- "react-i18next": "<%= REACT_I18NEXT_VERSION %>",
33
+ "dotenv": "^17.2.3",
34
+ "i18next": "^25.7.3",
35
+ "jotai": "^2.16.1",
36
+ "lucide-react": "^0.562.0",
37
+ "react": "^19.2.3",
38
+ "react-dom": "^19.2.3",
39
+ "react-hook-form": "^7.70.0",
40
+ "react-i18next": "^16.5.1",
30
41
  "sonner": "^2.0.7",
31
- "tailwind-merge": "^3.4.0"
42
+ "tailwind-merge": "^3.4.0",
43
+ "tailwindcss-animate": "^1.0.7",
44
+ "tw-animate-css": "^1.4.0",
45
+ "zod": "^4.3.5"
32
46
  },
33
47
  "devDependencies": {
34
- "@tailwindcss/postcss": "<%= TAILWIND_VERSION %>",
35
- "@tailwindcss/vite": "<%= TAILWIND_VERSION %>",
36
- "@tanstack/router-cli": "^1.139.14",
37
- "@types/node": "^24.10.1",
48
+ "@tailwindcss/postcss": "^4.1.18",
49
+ "@tailwindcss/vite": "^4.1.18",
50
+ "@tanstack/router-cli": "^1.145.7",
51
+ "@types/node": "^25.0.3",
38
52
  "@types/react": "^19.2.7",
39
53
  "@types/react-dom": "^19.2.3",
40
- "@vitejs/plugin-react": "^5.1.1",
41
- "cross-env": "<%= CROSS_ENV_VERSION %>",
54
+ "@vitejs/plugin-react": "^5.1.2",
55
+ "babel-plugin-react-compiler": "^1.0.0",
42
56
  "npm-run-all": "^4.1.5",
43
- "tailwindcss": "<%= TAILWIND_VERSION %>",
44
- "typescript": "<%= TYPESCRIPT_VERSION %>",
45
- "vite": "<%= VITE_VERSION %>"
57
+ "tailwindcss": "^4.1.18",
58
+ "typescript": "^5.9.3",
59
+ "vite": "^7.3.1"
46
60
  }
47
61
  }
@@ -0,0 +1,4 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <rect width="32" height="32" rx="10" fill="#000000"/>
3
+ <path d="M 9 7 L 9 25 M 9 16 L 19 7 M 9 16 L 19 25" stroke="#ffffff" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
4
+ </svg>
@@ -3,11 +3,11 @@ import { useAtomValue } from 'jotai';
3
3
  import { Loader2 } from 'lucide-react';
4
4
  import { useEffect } from 'react';
5
5
  import { Toaster } from 'sonner';
6
- import './index.css';
7
- import { AuthProvider } from './modules/auth/auth-context';
8
- import { authTokenAtom, currentUserDetailsAtom, useValidateToken, setLocaleInAxios } from './modules/auth/use-auth-hook';
9
- import { router } from './router';
10
- import { getLanguage } from './lib/i18n';
6
+ import '@/index.css';
7
+ import { AuthProvider } from '@/modules/auth/contexts/auth-context';
8
+ import { authTokenAtom, currentUserDetailsAtom, useValidateToken, setLocaleInClient } from '@/modules/auth/hooks/use-auth-hook';
9
+ import { router } from '@/router';
10
+ import { getLanguage, SupportedLanguages } from '@/lib/i18n';
11
11
 
12
12
  declare module '@tanstack/react-router' {
13
13
  interface Register {
@@ -31,13 +31,13 @@ export default function App() {
31
31
  const token = useAtomValue(authTokenAtom);
32
32
 
33
33
  useEffect(() => {
34
- const currentLanguage = getLanguage() as 'en';
35
- setLocaleInAxios(currentLanguage);
34
+ const currentLanguage = getLanguage() as SupportedLanguages;
35
+ setLocaleInClient(currentLanguage);
36
36
  }, []);
37
37
 
38
38
  useEffect(() => {
39
39
  if (token) {
40
- mutate();
40
+ mutate({});
41
41
  }
42
42
  }, [token, mutate]);
43
43
 
@@ -0,0 +1,40 @@
1
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
2
+ import { getLanguage, changeLanguage } from '@/lib/i18n';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { useEffect, useState } from 'react';
5
+
6
+ export function LanguageSwitcher() {
7
+ const { i18n } = useTranslation();
8
+ const [currentLang, setCurrentLang] = useState<'en' | 'ja'>(() => {
9
+ return (getLanguage() as 'en' | 'ja') || 'en';
10
+ });
11
+
12
+ useEffect(() => {
13
+ const handleLanguageChange = (lng: string) => {
14
+ setCurrentLang(lng as 'en' | 'ja');
15
+ };
16
+
17
+ i18n.on('languageChanged', handleLanguageChange);
18
+ return () => {
19
+ i18n.off('languageChanged', handleLanguageChange);
20
+ };
21
+ }, [i18n]);
22
+
23
+ const handleLanguageChange = (value: string) => {
24
+ changeLanguage(value as 'en' | 'ja');
25
+ };
26
+
27
+ return (
28
+ <Select
29
+ value={currentLang}
30
+ onValueChange={handleLanguageChange}>
31
+ <SelectTrigger className='min-w-[120px]'>
32
+ <SelectValue />
33
+ </SelectTrigger>
34
+ <SelectContent>
35
+ <SelectItem value='en'>English</SelectItem>
36
+ <SelectItem value='ja'>日本語</SelectItem>
37
+ </SelectContent>
38
+ </Select>
39
+ );
40
+ }
@@ -0,0 +1,37 @@
1
+ import { Button } from '@/components/ui/button';
2
+ import { ThemeTypes, useTheme } from '@/components/theme/theme-provider';
3
+ import { Moon, Sun, Monitor } from 'lucide-react';
4
+
5
+ export function ThemeSwitcher() {
6
+ const { theme, setTheme } = useTheme();
7
+
8
+ const cycleTheme = () => {
9
+ const themes: ThemeTypes[] = ['light', 'dark', 'system'];
10
+ const currentIndex = themes.indexOf(theme);
11
+ const nextIndex = (currentIndex + 1) % themes.length;
12
+ setTheme(themes[nextIndex]);
13
+ };
14
+
15
+ const getIcon = () => {
16
+ switch (theme) {
17
+ case 'light':
18
+ return <Sun className='h-4 w-4' />;
19
+ case 'dark':
20
+ return <Moon className='h-4 w-4' />;
21
+ case 'system':
22
+ return <Monitor className='h-4 w-4' />;
23
+ default:
24
+ return <Monitor className='h-4 w-4' />;
25
+ }
26
+ };
27
+
28
+ return (
29
+ <Button
30
+ variant='outline'
31
+ size='icon'
32
+ onClick={cycleTheme}
33
+ aria-label='Toggle theme'>
34
+ {getIcon()}
35
+ </Button>
36
+ );
37
+ }
@@ -1,45 +1,41 @@
1
- import { createContext, useContext, useEffect, useState } from "react";
1
+ import { createContext, useContext, useEffect, useState } from 'react';
2
2
 
3
- type Theme = "dark" | "light" | "system";
3
+ export type ThemeTypes = 'dark' | 'light' | 'system';
4
4
 
5
5
  type ThemeProviderProps = {
6
6
  children: React.ReactNode;
7
- defaultTheme?: Theme;
7
+ defaultTheme?: ThemeTypes;
8
8
  storageKey?: string;
9
9
  };
10
10
 
11
11
  type ThemeProviderState = {
12
- theme: Theme;
13
- setTheme: (theme: Theme) => void;
12
+ theme: ThemeTypes;
13
+ setTheme: (theme: ThemeTypes) => void;
14
14
  };
15
15
 
16
16
  const initialState: ThemeProviderState = {
17
- theme: "system",
17
+ theme: 'system' as ThemeTypes,
18
18
  setTheme: () => null,
19
19
  };
20
20
 
21
21
  const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
22
22
 
23
- export function ThemeProvider({
24
- children,
25
- defaultTheme = "system",
26
- storageKey = "vite-ui-theme",
27
- ...props
28
- }: ThemeProviderProps) {
29
- const [theme, setTheme] = useState<Theme>(
30
- () => (localStorage.getItem(storageKey) as Theme) || defaultTheme
31
- );
23
+ export function ThemeProvider({ children, defaultTheme = 'system', storageKey = 'vite-ui-theme', ...props }: ThemeProviderProps) {
24
+ const [theme, setTheme] = useState<ThemeTypes>(() => {
25
+ const stored = localStorage.getItem(storageKey);
26
+ if (stored && ['dark', 'light', 'system'].includes(stored)) {
27
+ return stored as ThemeTypes;
28
+ }
29
+ return 'system';
30
+ });
32
31
 
33
32
  useEffect(() => {
34
33
  const root = window.document.documentElement;
35
34
 
36
- root.classList.remove("light", "dark");
35
+ root.classList.remove('light', 'dark');
37
36
 
38
- if (theme === "system") {
39
- const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
40
- .matches
41
- ? "dark"
42
- : "light";
37
+ if (theme === 'system') {
38
+ const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
43
39
  root.classList.add(systemTheme);
44
40
  return;
45
41
  }
@@ -49,14 +45,16 @@ export function ThemeProvider({
49
45
 
50
46
  const value = {
51
47
  theme,
52
- setTheme: (theme: Theme) => {
48
+ setTheme: (theme: ThemeTypes) => {
53
49
  localStorage.setItem(storageKey, theme);
54
50
  setTheme(theme);
55
51
  },
56
52
  };
57
53
 
58
54
  return (
59
- <ThemeProviderContext.Provider {...props} value={value}>
55
+ <ThemeProviderContext.Provider
56
+ {...props}
57
+ value={value}>
60
58
  {children}
61
59
  </ThemeProviderContext.Provider>
62
60
  );
@@ -64,10 +62,6 @@ export function ThemeProvider({
64
62
 
65
63
  export const useTheme = () => {
66
64
  const context = useContext(ThemeProviderContext);
67
-
68
- if (context === undefined)
69
- throw new Error("useTheme must be used within a ThemeProvider");
70
-
65
+ if (context === undefined) throw new Error('useTheme must be used within a ThemeProvider');
71
66
  return context;
72
67
  };
73
-
@@ -1,23 +1,28 @@
1
- import * as React from "react";
2
- import { Slot } from "@radix-ui/react-slot";
3
- import { cva, type VariantProps } from "class-variance-authority";
4
- import { cn } from "@/lib/utils";
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "@/lib/utils"
5
6
 
6
7
  const buttonVariants = cva(
7
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:cursor-not-allowed disabled:opacity-50 hover:cursor-pointer outline-none focus-visible:ring-2 focus-visible:ring-ring",
8
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
8
9
  {
9
10
  variants: {
10
11
  variant: {
11
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
12
- destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
13
- outline: "border border-border bg-background hover:bg-accent hover:text-accent-foreground",
14
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
12
+ default:
13
+ "bg-primary text-primary-foreground shadow hover:bg-primary/90",
14
+ destructive:
15
+ "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
16
+ outline:
17
+ "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
18
+ secondary:
19
+ "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
15
20
  ghost: "hover:bg-accent hover:text-accent-foreground",
16
21
  link: "text-primary underline-offset-4 hover:underline",
17
22
  },
18
23
  size: {
19
24
  default: "h-9 px-4 py-2",
20
- sm: "h-8 rounded-md px-3",
25
+ sm: "h-8 rounded-md px-3 text-xs",
21
26
  lg: "h-10 rounded-md px-8",
22
27
  icon: "h-9 w-9",
23
28
  },
@@ -27,29 +32,26 @@ const buttonVariants = cva(
27
32
  size: "default",
28
33
  },
29
34
  }
30
- );
31
-
32
- function Button({
33
- className,
34
- variant,
35
- size,
36
- asChild = false,
37
- type,
38
- ...props
39
- }: React.ComponentProps<"button"> &
40
- VariantProps<typeof buttonVariants> & {
41
- asChild?: boolean;
42
- }) {
43
- const Comp = asChild ? Slot : "button";
35
+ )
44
36
 
45
- return (
46
- <Comp
47
- type={type ?? "button"}
48
- className={cn(buttonVariants({ variant, size, className }))}
49
- {...props}
50
- />
51
- );
37
+ export interface ButtonProps
38
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
39
+ VariantProps<typeof buttonVariants> {
40
+ asChild?: boolean
52
41
  }
53
42
 
54
- export { Button, buttonVariants };
43
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
44
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
45
+ const Comp = asChild ? Slot : "button"
46
+ return (
47
+ <Comp
48
+ className={cn(buttonVariants({ variant, size, className }))}
49
+ ref={ref}
50
+ {...props}
51
+ />
52
+ )
53
+ }
54
+ )
55
+ Button.displayName = "Button"
55
56
 
57
+ export { Button, buttonVariants }
@@ -0,0 +1,76 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ const Card = React.forwardRef<
6
+ HTMLDivElement,
7
+ React.HTMLAttributes<HTMLDivElement>
8
+ >(({ className, ...props }, ref) => (
9
+ <div
10
+ ref={ref}
11
+ className={cn(
12
+ "rounded-xl border bg-card text-card-foreground shadow",
13
+ className
14
+ )}
15
+ {...props}
16
+ />
17
+ ))
18
+ Card.displayName = "Card"
19
+
20
+ const CardHeader = React.forwardRef<
21
+ HTMLDivElement,
22
+ React.HTMLAttributes<HTMLDivElement>
23
+ >(({ className, ...props }, ref) => (
24
+ <div
25
+ ref={ref}
26
+ className={cn("flex flex-col space-y-1.5 p-6", className)}
27
+ {...props}
28
+ />
29
+ ))
30
+ CardHeader.displayName = "CardHeader"
31
+
32
+ const CardTitle = React.forwardRef<
33
+ HTMLDivElement,
34
+ React.HTMLAttributes<HTMLDivElement>
35
+ >(({ className, ...props }, ref) => (
36
+ <div
37
+ ref={ref}
38
+ className={cn("font-semibold leading-none tracking-tight", className)}
39
+ {...props}
40
+ />
41
+ ))
42
+ CardTitle.displayName = "CardTitle"
43
+
44
+ const CardDescription = React.forwardRef<
45
+ HTMLDivElement,
46
+ React.HTMLAttributes<HTMLDivElement>
47
+ >(({ className, ...props }, ref) => (
48
+ <div
49
+ ref={ref}
50
+ className={cn("text-sm text-muted-foreground", className)}
51
+ {...props}
52
+ />
53
+ ))
54
+ CardDescription.displayName = "CardDescription"
55
+
56
+ const CardContent = React.forwardRef<
57
+ HTMLDivElement,
58
+ React.HTMLAttributes<HTMLDivElement>
59
+ >(({ className, ...props }, ref) => (
60
+ <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
61
+ ))
62
+ CardContent.displayName = "CardContent"
63
+
64
+ const CardFooter = React.forwardRef<
65
+ HTMLDivElement,
66
+ React.HTMLAttributes<HTMLDivElement>
67
+ >(({ className, ...props }, ref) => (
68
+ <div
69
+ ref={ref}
70
+ className={cn("flex items-center p-6 pt-0", className)}
71
+ {...props}
72
+ />
73
+ ))
74
+ CardFooter.displayName = "CardFooter"
75
+
76
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }