@idevconn/create-icore 0.6.3 → 0.7.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.
Files changed (87) hide show
  1. package/dist/cli.js +384 -276
  2. package/dist/index.cjs +385 -277
  3. package/dist/index.d.cts +3 -3
  4. package/dist/index.d.ts +3 -3
  5. package/dist/index.js +382 -274
  6. package/package.json +1 -1
  7. package/templates/.yarn/releases/yarn-4.16.0.cjs +944 -0
  8. package/templates/.yarnrc.yml +1 -1
  9. package/templates/apps/api/src/app/storage/storage.controller.ts +28 -0
  10. package/templates/apps/microservices/auth/src/app/app.module.ts +20 -2
  11. package/templates/apps/microservices/notes/src/app/app.module.ts +17 -2
  12. package/templates/apps/microservices/upload/src/app/app.module.ts +17 -2
  13. package/templates/apps/microservices/upload/src/app/storage.controller.ts +7 -0
  14. package/templates/apps/templates/client-antd/src/components/auth/AuthBrandPanel.tsx +59 -0
  15. package/templates/apps/templates/client-antd/src/components/auth/CheckEmailScreen.tsx +28 -0
  16. package/templates/apps/templates/client-antd/src/components/auth/LoginForm.tsx +116 -0
  17. package/templates/apps/templates/client-antd/src/components/auth/MagicLinkForm.tsx +95 -0
  18. package/templates/apps/templates/client-antd/src/components/auth/RegisterForm.tsx +98 -0
  19. package/templates/apps/templates/client-antd/src/globals.less +6 -0
  20. package/templates/apps/templates/client-antd/src/main.tsx +1 -1
  21. package/templates/apps/templates/client-antd/src/routes/login.tsx +45 -181
  22. package/templates/apps/templates/client-mui/src/components/auth/AuthBrandPanel.tsx +59 -0
  23. package/templates/apps/templates/client-mui/src/components/auth/CheckEmailScreen.tsx +28 -0
  24. package/templates/apps/templates/client-mui/src/components/auth/LoginForm.tsx +141 -0
  25. package/templates/apps/templates/client-mui/src/components/auth/MagicLinkForm.tsx +106 -0
  26. package/templates/apps/templates/client-mui/src/components/auth/RegisterForm.tsx +113 -0
  27. package/templates/apps/templates/client-mui/src/main.tsx +1 -1
  28. package/templates/apps/templates/client-mui/src/routes/login.tsx +50 -186
  29. package/templates/apps/templates/client-shadcn/src/components/auth/AuthBrandPanel.tsx +52 -0
  30. package/templates/apps/templates/client-shadcn/src/components/auth/CheckEmailScreen.tsx +29 -0
  31. package/templates/apps/templates/client-shadcn/src/components/auth/LoginForm.tsx +161 -0
  32. package/templates/apps/templates/client-shadcn/src/components/auth/MagicLinkForm.tsx +110 -0
  33. package/templates/apps/templates/client-shadcn/src/components/auth/RegisterForm.tsx +107 -0
  34. package/templates/apps/templates/client-shadcn/src/components/layout/LayoutHeader.tsx +31 -10
  35. package/templates/apps/templates/client-shadcn/src/components/layout/LayoutSider.tsx +22 -27
  36. package/templates/apps/templates/client-shadcn/src/components/ui/card.tsx +1 -1
  37. package/templates/apps/templates/client-shadcn/src/globals.css +39 -13
  38. package/templates/apps/templates/client-shadcn/src/routes/auth.callback.tsx +1 -1
  39. package/templates/apps/templates/client-shadcn/src/routes/login.tsx +55 -165
  40. package/templates/libs/auth-strategies/mongodb/CHANGELOG.md +8 -0
  41. package/templates/libs/auth-strategies/mongodb/README.md +11 -0
  42. package/templates/libs/auth-strategies/mongodb/eslint.config.mjs +19 -0
  43. package/templates/libs/auth-strategies/mongodb/jest.config.cts +10 -0
  44. package/templates/libs/auth-strategies/mongodb/package.json +16 -0
  45. package/templates/libs/auth-strategies/mongodb/project.json +19 -0
  46. package/templates/libs/auth-strategies/mongodb/src/index.ts +1 -0
  47. package/templates/libs/auth-strategies/mongodb/src/lib/__tests__/mongodb-auth.strategy.unit.test.ts +42 -0
  48. package/templates/libs/auth-strategies/mongodb/src/lib/auth-mongodb.spec.ts +7 -0
  49. package/templates/libs/auth-strategies/mongodb/src/lib/auth-mongodb.ts +3 -0
  50. package/templates/libs/auth-strategies/mongodb/src/lib/mongodb-auth.strategy.ts +188 -0
  51. package/templates/libs/auth-strategies/mongodb/tsconfig.json +23 -0
  52. package/templates/libs/auth-strategies/mongodb/tsconfig.lib.json +10 -0
  53. package/templates/libs/auth-strategies/mongodb/tsconfig.spec.json +16 -0
  54. package/templates/libs/db-strategies/mongodb/CHANGELOG.md +7 -0
  55. package/templates/libs/db-strategies/mongodb/README.md +11 -0
  56. package/templates/libs/db-strategies/mongodb/eslint.config.mjs +19 -0
  57. package/templates/libs/db-strategies/mongodb/jest.config.cts +10 -0
  58. package/templates/libs/db-strategies/mongodb/package.json +14 -0
  59. package/templates/libs/db-strategies/mongodb/project.json +19 -0
  60. package/templates/libs/db-strategies/mongodb/src/index.ts +1 -0
  61. package/templates/libs/db-strategies/mongodb/src/lib/__tests__/mongodb-db.strategy.unit.test.ts +38 -0
  62. package/templates/libs/db-strategies/mongodb/src/lib/mongodb-db.strategy.ts +108 -0
  63. package/templates/libs/db-strategies/mongodb/src/lib/mongodb.spec.ts +7 -0
  64. package/templates/libs/db-strategies/mongodb/src/lib/mongodb.ts +3 -0
  65. package/templates/libs/db-strategies/mongodb/tsconfig.json +23 -0
  66. package/templates/libs/db-strategies/mongodb/tsconfig.lib.json +10 -0
  67. package/templates/libs/db-strategies/mongodb/tsconfig.spec.json +16 -0
  68. package/templates/libs/shared/src/strategies/storage.ts +3 -0
  69. package/templates/libs/storage-strategies/mongodb/CHANGELOG.md +8 -0
  70. package/templates/libs/storage-strategies/mongodb/README.md +11 -0
  71. package/templates/libs/storage-strategies/mongodb/eslint.config.mjs +19 -0
  72. package/templates/libs/storage-strategies/mongodb/jest.config.cts +10 -0
  73. package/templates/libs/storage-strategies/mongodb/package.json +14 -0
  74. package/templates/libs/storage-strategies/mongodb/project.json +19 -0
  75. package/templates/libs/storage-strategies/mongodb/src/index.ts +1 -0
  76. package/templates/libs/storage-strategies/mongodb/src/lib/__tests__/mongodb-storage.strategy.unit.test.ts +38 -0
  77. package/templates/libs/storage-strategies/mongodb/src/lib/mongodb-storage.strategy.ts +93 -0
  78. package/templates/libs/storage-strategies/mongodb/src/lib/storage-mongodb.spec.ts +7 -0
  79. package/templates/libs/storage-strategies/mongodb/src/lib/storage-mongodb.ts +3 -0
  80. package/templates/libs/storage-strategies/mongodb/tsconfig.json +23 -0
  81. package/templates/libs/storage-strategies/mongodb/tsconfig.lib.json +10 -0
  82. package/templates/libs/storage-strategies/mongodb/tsconfig.spec.json +16 -0
  83. package/templates/libs/template-shared/src/lib/i18n/keys.ts +216 -56
  84. package/templates/libs/template-shared/src/lib/stores/theme.store.ts +1 -6
  85. package/templates/libs/upload-client/src/lib/upload-client.service.ts +7 -0
  86. package/templates/tsconfig.base.json +4 -1
  87. package/templates/.yarn/releases/yarn-4.15.0.cjs +0 -940
@@ -0,0 +1,110 @@
1
+ import { SyntheticEvent, useState } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { Loader2, MailCheck } from 'lucide-react';
4
+ import { Button } from '../ui/button';
5
+ import { Input } from '../ui/input';
6
+ import { Label } from '../ui/label';
7
+
8
+ interface MagicLinkFormProps {
9
+ onError: (msg: string) => void;
10
+ onSwitchToLogin: () => void;
11
+ api: <T>(path: string, init?: RequestInit) => Promise<T>;
12
+ }
13
+
14
+ export function MagicLinkForm({ onError, onSwitchToLogin, api }: MagicLinkFormProps) {
15
+ const { t } = useTranslation();
16
+ const [email, setEmail] = useState('');
17
+ const [submitting, setSubmitting] = useState(false);
18
+ const [sent, setSent] = useState(false);
19
+
20
+ async function handleSubmit(e: SyntheticEvent<HTMLFormElement>) {
21
+ e.preventDefault();
22
+ setSubmitting(true);
23
+ try {
24
+ await api('/auth/magic-link', {
25
+ method: 'POST',
26
+ headers: { 'Content-Type': 'application/json' },
27
+ body: JSON.stringify({ email }),
28
+ });
29
+ setSent(true);
30
+ } catch (err) {
31
+ onError(err instanceof Error ? err.message : t('error.unknown'));
32
+ } finally {
33
+ setSubmitting(false);
34
+ }
35
+ }
36
+
37
+ if (sent) {
38
+ return (
39
+ <div className="flex flex-col items-center text-center space-y-4 py-4">
40
+ <div className="flex h-14 w-14 items-center justify-center rounded-full bg-[--color-primary]/15">
41
+ <MailCheck size={24} className="text-[--color-primary]" />
42
+ </div>
43
+ <div className="space-y-1">
44
+ <h3 className="text-lg font-semibold">{t('auth.magicLinkSent')}</h3>
45
+ <p className="text-sm text-[--color-muted-foreground] max-w-xs">
46
+ {t('auth.magicLinkSentDescription', { email })}
47
+ </p>
48
+ </div>
49
+ <Button
50
+ type="button"
51
+ variant="outline"
52
+ className="w-full cursor-pointer"
53
+ onClick={() => {
54
+ setEmail('');
55
+ setSent(false);
56
+ }}
57
+ >
58
+ {t('auth.magicLinkUseDifferentEmail')}
59
+ </Button>
60
+ <button
61
+ type="button"
62
+ onClick={onSwitchToLogin}
63
+ className="text-sm text-[--color-muted-foreground] hover:underline cursor-pointer"
64
+ >
65
+ {t('auth.backToLogin')}
66
+ </button>
67
+ </div>
68
+ );
69
+ }
70
+
71
+ return (
72
+ <div className="space-y-5">
73
+ <div className="space-y-1">
74
+ <h1 className="text-2xl font-bold">{t('auth.withMagicLink')}</h1>
75
+ <p className="text-sm text-[--color-muted-foreground]">
76
+ {t('auth.magicLinkSentDescription', { email: 'your email' }).replace(
77
+ 'your email',
78
+ t('auth.email').toLowerCase(),
79
+ )}
80
+ </p>
81
+ </div>
82
+
83
+ <form onSubmit={handleSubmit} className="space-y-4">
84
+ <div className="space-y-2">
85
+ <Label htmlFor="ml-email">{t('auth.email')}</Label>
86
+ <Input
87
+ id="ml-email"
88
+ type="email"
89
+ value={email}
90
+ onChange={(e) => setEmail(e.target.value)}
91
+ placeholder="you@example.com"
92
+ required
93
+ autoComplete="email"
94
+ />
95
+ </div>
96
+ <Button type="submit" className="w-full cursor-pointer" disabled={submitting}>
97
+ {submitting ? <Loader2 size={16} className="animate-spin" /> : t('auth.sendMagicLink')}
98
+ </Button>
99
+ </form>
100
+
101
+ <button
102
+ type="button"
103
+ onClick={onSwitchToLogin}
104
+ className="block w-full text-center text-sm text-[--color-muted-foreground] hover:underline cursor-pointer"
105
+ >
106
+ {t('auth.backToLogin')}
107
+ </button>
108
+ </div>
109
+ );
110
+ }
@@ -0,0 +1,107 @@
1
+ import { SyntheticEvent, useState } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { Loader2 } from 'lucide-react';
4
+ import { Button } from '../ui/button';
5
+ import { Input } from '../ui/input';
6
+ import { Label } from '../ui/label';
7
+
8
+ interface RegisterFormProps {
9
+ onSuccess: (email: string) => void;
10
+ onError: (msg: string) => void;
11
+ onSwitchToLogin: () => void;
12
+ api: <T>(path: string, init?: RequestInit) => Promise<T>;
13
+ }
14
+
15
+ export function RegisterForm({ onSuccess, onError, onSwitchToLogin, api }: RegisterFormProps) {
16
+ const { t } = useTranslation();
17
+ const [email, setEmail] = useState('');
18
+ const [password, setPassword] = useState('');
19
+ const [confirm, setConfirm] = useState('');
20
+ const [submitting, setSubmitting] = useState(false);
21
+ const [validationError, setValidationError] = useState('');
22
+
23
+ async function handleSubmit(e: SyntheticEvent<HTMLFormElement>) {
24
+ e.preventDefault();
25
+ setValidationError('');
26
+ if (password !== confirm) {
27
+ setValidationError(t('auth.passwordMismatch'));
28
+ return;
29
+ }
30
+ setSubmitting(true);
31
+ try {
32
+ await api('/auth/register', {
33
+ method: 'POST',
34
+ headers: { 'Content-Type': 'application/json' },
35
+ body: JSON.stringify({ email, password }),
36
+ });
37
+ onSuccess(email);
38
+ } catch (err) {
39
+ onError(err instanceof Error ? err.message : t('error.unknown'));
40
+ } finally {
41
+ setSubmitting(false);
42
+ }
43
+ }
44
+
45
+ return (
46
+ <div className="space-y-5">
47
+ <div className="space-y-1">
48
+ <h1 className="text-2xl font-bold">{t('auth.registerTitle')}</h1>
49
+ <p className="text-sm text-[--color-muted-foreground]">{t('auth.registerSubtitle')}</p>
50
+ </div>
51
+
52
+ <form onSubmit={handleSubmit} className="space-y-4">
53
+ <div className="space-y-2">
54
+ <Label htmlFor="reg-email">{t('auth.email')}</Label>
55
+ <Input
56
+ id="reg-email"
57
+ type="email"
58
+ value={email}
59
+ onChange={(e) => setEmail(e.target.value)}
60
+ placeholder="you@example.com"
61
+ required
62
+ autoComplete="email"
63
+ />
64
+ </div>
65
+ <div className="space-y-2">
66
+ <Label htmlFor="reg-password">{t('auth.password')}</Label>
67
+ <Input
68
+ id="reg-password"
69
+ type="password"
70
+ value={password}
71
+ onChange={(e) => setPassword(e.target.value)}
72
+ required
73
+ autoComplete="new-password"
74
+ />
75
+ </div>
76
+ <div className="space-y-2">
77
+ <Label htmlFor="reg-confirm">{t('auth.confirmPassword')}</Label>
78
+ <Input
79
+ id="reg-confirm"
80
+ type="password"
81
+ value={confirm}
82
+ onChange={(e) => setConfirm(e.target.value)}
83
+ required
84
+ autoComplete="new-password"
85
+ />
86
+ {validationError && (
87
+ <p className="text-xs text-[--color-destructive]">{validationError}</p>
88
+ )}
89
+ </div>
90
+ <Button type="submit" className="w-full cursor-pointer" disabled={submitting}>
91
+ {submitting ? <Loader2 size={16} className="animate-spin" /> : t('auth.register')}
92
+ </Button>
93
+ </form>
94
+
95
+ <p className="text-center text-sm text-[--color-muted-foreground]">
96
+ {t('auth.switchToLogin')}{' '}
97
+ <button
98
+ type="button"
99
+ onClick={onSwitchToLogin}
100
+ className="text-[--color-primary] font-medium hover:underline cursor-pointer"
101
+ >
102
+ {t('auth.switchToLoginLink')}
103
+ </button>
104
+ </p>
105
+ </div>
106
+ );
107
+ }
@@ -1,5 +1,6 @@
1
1
  import { useTranslation } from 'react-i18next';
2
2
  import { useNavigate } from '@tanstack/react-router';
3
+ import { LogOut } from 'lucide-react';
3
4
  import { useAuthStore, setStoredLocale, type IcoreLocale } from '@icore/template-shared';
4
5
  import { Button } from '../ui/button';
5
6
  import { ThemeToggle } from '../ThemeToggle';
@@ -27,17 +28,22 @@ export function LayoutHeader() {
27
28
  }
28
29
 
29
30
  return (
30
- <header className="border-b border-border px-6 py-3 flex items-center justify-between bg-background">
31
- <span className="font-semibold text-foreground tracking-tight">icore</span>
31
+ <header className="sticky top-0 z-30 flex h-14 items-center justify-between border-b border-[--color-border] bg-[--color-background]/80 px-4 backdrop-blur-sm">
32
+ <div className="flex items-center gap-2">
33
+ <div className="flex h-6 w-6 items-center justify-center rounded bg-[--color-primary]">
34
+ <span className="text-xs font-bold text-[--color-primary-foreground]">i</span>
35
+ </div>
36
+ <span className="text-sm font-semibold tracking-tight">iCore</span>
37
+ </div>
32
38
 
33
- <div className="flex items-center gap-4">
34
- <div className="flex items-center gap-1">
39
+ <div className="flex items-center gap-1">
40
+ <div className="flex items-center rounded-md border border-[--color-border] overflow-hidden mr-2">
35
41
  {LOCALES.map(({ code, label }) => (
36
42
  <button
37
43
  key={code}
38
- onClick={() => handleLocale(code)}
39
- className="text-xs px-2 py-1 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors"
40
44
  type="button"
45
+ onClick={() => handleLocale(code)}
46
+ className="px-2.5 py-1 text-xs text-[--color-muted-foreground] hover:bg-[--color-muted] hover:text-[--color-foreground] transition-colors cursor-pointer"
41
47
  >
42
48
  {label}
43
49
  </button>
@@ -46,14 +52,29 @@ export function LayoutHeader() {
46
52
 
47
53
  <ThemeToggle />
48
54
 
49
- <div className="flex items-center gap-2">
50
- <span className="text-sm text-muted-foreground hidden sm:inline">
55
+ <div className="hidden sm:flex items-center gap-2 ml-1 pl-2 border-l border-[--color-border]">
56
+ <span className="text-xs text-[--color-muted-foreground] max-w-[140px] truncate">
51
57
  {user?.email ?? ''}
52
58
  </span>
53
- <Button variant="outline" size="sm" onClick={handleLogout}>
54
- {t('common.logout')}
59
+ <Button
60
+ variant="ghost"
61
+ size="icon"
62
+ onClick={handleLogout}
63
+ aria-label={t('common.logout')}
64
+ className="h-8 w-8 cursor-pointer"
65
+ >
66
+ <LogOut size={15} />
55
67
  </Button>
56
68
  </div>
69
+
70
+ <Button
71
+ variant="ghost"
72
+ size="sm"
73
+ onClick={handleLogout}
74
+ className="sm:hidden cursor-pointer"
75
+ >
76
+ <LogOut size={15} />
77
+ </Button>
57
78
  </div>
58
79
  </header>
59
80
  );
@@ -3,46 +3,41 @@ import { Link } from '@tanstack/react-router';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import { ChevronLeft, ChevronRight, LayoutDashboard, StickyNote, User } from 'lucide-react';
5
5
 
6
+ const NAV_ITEMS = [
7
+ { to: '/dashboard' as const, icon: LayoutDashboard, labelKey: 'nav.dashboard', exact: true },
8
+ { to: '/notes' as const, icon: StickyNote, labelKey: 'nav.notes', exact: false },
9
+ { to: '/profile' as const, icon: User, labelKey: 'nav.profile', exact: false },
10
+ ];
11
+
6
12
  export function LayoutSider() {
7
13
  const { t } = useTranslation();
8
14
  const [collapsed, setCollapsed] = useState(false);
9
15
 
10
16
  return (
11
17
  <aside
12
- className={`relative flex flex-col border-r border-border bg-background transition-all duration-200 ${
13
- collapsed ? 'w-12' : 'w-48'
18
+ className={`relative flex flex-col border-r border-[--color-border] bg-[--color-card] transition-all duration-200 ${
19
+ collapsed ? 'w-14' : 'w-52'
14
20
  }`}
15
21
  >
16
- <nav className="flex flex-col gap-1 p-2 flex-1">
17
- <Link
18
- to="/dashboard"
19
- activeOptions={{ exact: true }}
20
- className="flex items-center gap-2 px-2 py-2 rounded hover:bg-muted text-sm text-foreground transition-colors [&.active]:bg-muted [&.active]:font-medium"
21
- >
22
- <LayoutDashboard size={16} className="shrink-0" />
23
- {!collapsed && <span>{t('nav.dashboard')}</span>}
24
- </Link>
25
- <Link
26
- to="/notes"
27
- className="flex items-center gap-2 px-2 py-2 rounded hover:bg-muted text-sm text-foreground transition-colors [&.active]:bg-muted [&.active]:font-medium"
28
- >
29
- <StickyNote size={16} className="shrink-0" />
30
- {!collapsed && <span>{t('notes.title')}</span>}
31
- </Link>
32
- <Link
33
- to="/profile"
34
- className="flex items-center gap-2 px-2 py-2 rounded hover:bg-muted text-sm text-foreground transition-colors [&.active]:bg-muted [&.active]:font-medium"
35
- >
36
- <User size={16} className="shrink-0" />
37
- {!collapsed && <span>{t('nav.profile')}</span>}
38
- </Link>
22
+ <nav className="flex flex-col gap-0.5 p-2 flex-1 pt-3">
23
+ {NAV_ITEMS.map(({ to, icon: Icon, labelKey, exact }) => (
24
+ <Link
25
+ key={to}
26
+ to={to}
27
+ activeOptions={{ exact }}
28
+ className="group flex items-center gap-3 rounded-md px-2.5 py-2 text-sm text-[--color-muted-foreground] transition-colors hover:bg-[--color-muted] hover:text-[--color-foreground] [&.active]:bg-[--color-primary]/10 [&.active]:text-[--color-primary] [&.active]:font-medium cursor-pointer"
29
+ >
30
+ <Icon size={16} className="shrink-0" />
31
+ {!collapsed && <span className="truncate">{t(labelKey)}</span>}
32
+ </Link>
33
+ ))}
39
34
  </nav>
40
35
 
41
36
  <button
42
- onClick={() => setCollapsed((c) => !c)}
43
- className="absolute -right-3 top-4 z-10 flex h-6 w-6 items-center justify-center rounded-full border border-border bg-background text-muted-foreground hover:text-foreground shadow-sm"
44
37
  type="button"
38
+ onClick={() => setCollapsed((c) => !c)}
45
39
  aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
40
+ className="absolute -right-3 top-5 z-10 flex h-6 w-6 cursor-pointer items-center justify-center rounded-full border border-[--color-border] bg-[--color-card] text-[--color-muted-foreground] shadow-sm hover:text-[--color-foreground] transition-colors"
46
41
  >
47
42
  {collapsed ? <ChevronRight size={12} /> : <ChevronLeft size={12} />}
48
43
  </button>
@@ -11,7 +11,7 @@ const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElemen
11
11
  <div
12
12
  ref={ref}
13
13
  className={cn(
14
- 'rounded-lg border border-[--color-border] bg-[--color-background] text-[--color-foreground] shadow-sm',
14
+ 'rounded-lg border border-[--color-border] bg-[--color-card] text-[--color-card-foreground] shadow-sm',
15
15
  className,
16
16
  )}
17
17
  {...props}
@@ -1,27 +1,53 @@
1
1
  @import 'tailwindcss';
2
+ @import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700&display=swap');
2
3
 
3
4
  @theme {
4
- --color-background: oklch(0.99 0 0);
5
- --color-foreground: oklch(0.15 0 0);
6
- --color-primary: oklch(0.45 0.18 250);
7
- --color-primary-foreground: oklch(0.98 0 0);
8
- --color-muted: oklch(0.96 0 0);
9
- --color-muted-foreground: oklch(0.45 0 0);
10
- --color-border: oklch(0.92 0 0);
11
- --color-destructive: oklch(0.55 0.25 25);
5
+ --font-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
6
+
7
+ /* Light mode defaults */
8
+ --color-background: #ffffff;
9
+ --color-foreground: #0f172a;
10
+ --color-card: #f8fafc;
11
+ --color-card-foreground: #0f172a;
12
+ --color-primary: #16a34a;
13
+ --color-primary-foreground: #ffffff;
14
+ --color-secondary: #f1f5f9;
15
+ --color-secondary-foreground: #0f172a;
16
+ --color-muted: #f1f5f9;
17
+ --color-muted-foreground: #475569;
18
+ --color-border: #e2e8f0;
19
+ --color-input: #e2e8f0;
20
+ --color-ring: #16a34a;
21
+ --color-destructive: #ef4444;
12
22
  --radius-default: 0.5rem;
13
23
  }
14
24
 
15
25
  @layer base {
26
+ * {
27
+ font-family: var(--font-sans);
28
+ }
29
+
16
30
  body {
17
31
  background-color: var(--color-background);
18
32
  color: var(--color-foreground);
33
+ -webkit-font-smoothing: antialiased;
19
34
  }
35
+
36
+ /* OLED Dark mode */
20
37
  html.dark {
21
- --color-background: oklch(0.15 0 0);
22
- --color-foreground: oklch(0.98 0 0);
23
- --color-muted: oklch(0.25 0 0);
24
- --color-muted-foreground: oklch(0.7 0 0);
25
- --color-border: oklch(0.3 0 0);
38
+ --color-background: #020617;
39
+ --color-foreground: #f8fafc;
40
+ --color-card: #0f172a;
41
+ --color-card-foreground: #f8fafc;
42
+ --color-primary: #22c55e;
43
+ --color-primary-foreground: #020617;
44
+ --color-secondary: #1e293b;
45
+ --color-secondary-foreground: #f8fafc;
46
+ --color-muted: #1e293b;
47
+ --color-muted-foreground: #94a3b8;
48
+ --color-border: #1e293b;
49
+ --color-input: #1e293b;
50
+ --color-ring: #22c55e;
51
+ --color-destructive: #ef4444;
26
52
  }
27
53
  }
@@ -3,7 +3,7 @@ import { useEffect, useState } from 'react';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import { useAuthStore, useNotify } from '@icore/template-shared';
5
5
  import { Loader2 } from 'lucide-react';
6
- import { api } from '../main';
6
+ import { api } from '@/main';
7
7
 
8
8
  type Status = 'verifying' | 'done' | 'error';
9
9