@idevconn/create-icore 0.5.2 → 0.6.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 (73) hide show
  1. package/dist/cli.js +36 -27
  2. package/dist/index.cjs +36 -27
  3. package/dist/index.d.cts +1 -1
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.js +36 -27
  6. package/package.json +1 -1
  7. package/templates/apps/api/.env.example +20 -0
  8. package/templates/apps/api/tsconfig.json +6 -1
  9. package/templates/apps/microservices/auth/.env.example +5 -0
  10. package/templates/apps/microservices/auth/tsconfig.json +6 -1
  11. package/templates/apps/microservices/jobs/tsconfig.json +6 -1
  12. package/templates/apps/microservices/notes/.env.example +5 -0
  13. package/templates/apps/microservices/notes/tsconfig.json +6 -1
  14. package/templates/apps/microservices/notes-e2e/src/support/global.d.ts +6 -0
  15. package/templates/apps/microservices/payment/.env.example +5 -0
  16. package/templates/apps/microservices/payment/tsconfig.json +6 -1
  17. package/templates/apps/microservices/upload/.env.example +5 -0
  18. package/templates/apps/microservices/upload/tsconfig.json +6 -1
  19. package/templates/apps/templates/client-antd/src/components/AccessDeniedPage.tsx +1 -1
  20. package/templates/apps/templates/client-antd/src/components/layout/LayoutHeader.tsx +1 -1
  21. package/templates/apps/templates/client-antd/src/components/layout/LayoutSider.tsx +3 -3
  22. package/templates/apps/templates/client-antd/src/routes/_dashboard/dashboard.tsx +2 -2
  23. package/templates/apps/templates/client-antd/src/routes/_dashboard/notes.tsx +2 -2
  24. package/templates/apps/templates/client-antd/src/routes/_dashboard/profile.tsx +2 -2
  25. package/templates/apps/templates/client-antd/src/routes/auth.callback.tsx +1 -1
  26. package/templates/apps/templates/client-antd/src/routes/auth.oauth.callback.tsx +1 -1
  27. package/templates/apps/templates/client-antd/src/routes/login.tsx +1 -1
  28. package/templates/apps/templates/client-antd/tsconfig.json +6 -1
  29. package/templates/apps/templates/client-antd-e2e/src/icore.spec.ts +2 -2
  30. package/templates/apps/templates/client-mui/src/components/AccessDeniedPage.tsx +1 -1
  31. package/templates/apps/templates/client-mui/src/components/layout/LayoutHeader.tsx +1 -1
  32. package/templates/apps/templates/client-mui/src/components/layout/LayoutSider.tsx +3 -15
  33. package/templates/apps/templates/client-mui/src/routes/_dashboard/dashboard.tsx +2 -6
  34. package/templates/apps/templates/client-mui/src/routes/_dashboard/notes.tsx +2 -2
  35. package/templates/apps/templates/client-mui/src/routes/_dashboard/profile.tsx +3 -3
  36. package/templates/apps/templates/client-mui/src/routes/auth.callback.tsx +1 -1
  37. package/templates/apps/templates/client-mui/src/routes/auth.oauth.callback.tsx +1 -1
  38. package/templates/apps/templates/client-mui/src/routes/login.tsx +3 -3
  39. package/templates/apps/templates/client-mui/tsconfig.json +6 -1
  40. package/templates/apps/templates/client-mui-e2e/src/icore.spec.ts +2 -2
  41. package/templates/apps/templates/client-shadcn/src/components/AccessDeniedPage.tsx +1 -1
  42. package/templates/apps/templates/client-shadcn/src/components/layout/LayoutSider.tsx +3 -3
  43. package/templates/apps/templates/client-shadcn/src/components/notes/DeleteNoteConfirm.tsx +1 -1
  44. package/templates/apps/templates/client-shadcn/src/components/notes/NoteDialog.tsx +3 -3
  45. package/templates/apps/templates/client-shadcn/src/components/notes/NotesTable.tsx +1 -1
  46. package/templates/apps/templates/client-shadcn/src/components/ui/button.tsx +1 -1
  47. package/templates/apps/templates/client-shadcn/src/components/ui/card.tsx +1 -1
  48. package/templates/apps/templates/client-shadcn/src/components/ui/input.tsx +1 -1
  49. package/templates/apps/templates/client-shadcn/src/components/ui/label.tsx +1 -1
  50. package/templates/apps/templates/client-shadcn/src/routes/_dashboard/dashboard.tsx +3 -9
  51. package/templates/apps/templates/client-shadcn/src/routes/_dashboard/notes.tsx +6 -6
  52. package/templates/apps/templates/client-shadcn/src/routes/_dashboard/profile.tsx +7 -7
  53. package/templates/apps/templates/client-shadcn/src/routes/auth.callback.tsx +1 -1
  54. package/templates/apps/templates/client-shadcn/src/routes/auth.oauth.callback.tsx +1 -1
  55. package/templates/apps/templates/client-shadcn/src/routes/login.tsx +19 -12
  56. package/templates/apps/templates/client-shadcn/tsconfig.json +6 -1
  57. package/templates/libs/auth-strategies/firebase/src/lib/__tests__/firebase-auth.contract.unit.test.ts +6 -3
  58. package/templates/libs/auth-strategies/supabase/src/lib/__tests__/supabase-auth.contract.unit.test.ts +5 -2
  59. package/templates/libs/db-strategies/firestore/src/lib/__tests__/firestore-db.contract.unit.test.ts +1 -2
  60. package/templates/libs/db-strategies/supabase/src/lib/__tests__/supabase-db.contract.unit.test.ts +1 -2
  61. package/templates/libs/shared/src/__tests__/cross-boundary.unit.test.ts +2 -1
  62. package/templates/libs/shared/src/__tests__/transport.unit.test.ts +47 -8
  63. package/templates/libs/shared/src/abilities/subjects.ts +12 -1
  64. package/templates/libs/shared/src/strategies/__tests__/fake-auth.contract.unit.test.ts +2 -2
  65. package/templates/libs/shared/src/strategies/__tests__/fake-db.contract.unit.test.ts +2 -2
  66. package/templates/libs/shared/src/strategies/__tests__/fake-storage.contract.unit.test.ts +2 -2
  67. package/templates/libs/shared/src/transport.ts +41 -0
  68. package/templates/libs/storage-strategies/cloudinary/src/lib/__tests__/cloudinary-storage.contract.unit.test.ts +1 -2
  69. package/templates/libs/storage-strategies/firebase/src/lib/__tests__/firebase-storage.contract.unit.test.ts +1 -2
  70. package/templates/libs/storage-strategies/supabase/src/lib/__tests__/supabase-storage.contract.unit.test.ts +1 -2
  71. package/templates/libs/vite-plugins/src/index.d.mts +5 -7
  72. package/templates/libs/vite-plugins/src/index.mjs +1 -1
  73. package/templates/libs/vite-plugins/tsconfig.json +2 -1
@@ -10,7 +10,7 @@ export function AccessDeniedPage() {
10
10
  title={t('error.accessDenied')}
11
11
  subTitle={t('error.unknown')}
12
12
  extra={
13
- <Link to="/_dashboard/dashboard">
13
+ <Link to="/dashboard">
14
14
  <Button type="primary">Dashboard</Button>
15
15
  </Link>
16
16
  }
@@ -36,7 +36,7 @@ export function LayoutHeader() {
36
36
  { type: 'divider' },
37
37
  {
38
38
  key: 'profile',
39
- label: <Link to="/_dashboard/profile">{t('nav.profile')}</Link>,
39
+ label: <Link to="/profile">{t('nav.profile')}</Link>,
40
40
  },
41
41
  {
42
42
  key: 'logout',
@@ -19,17 +19,17 @@ export function LayoutSider() {
19
19
  {
20
20
  key: 'dashboard',
21
21
  icon: <DashboardOutlined />,
22
- label: <Link to="/_dashboard/dashboard">{t('nav.dashboard')}</Link>,
22
+ label: <Link to="/dashboard">{t('nav.dashboard')}</Link>,
23
23
  },
24
24
  {
25
25
  key: 'notes',
26
26
  icon: <FileTextOutlined />,
27
- label: <Link to="/_dashboard/notes">{t('notes.title')}</Link>,
27
+ label: <Link to="/notes">{t('notes.title')}</Link>,
28
28
  },
29
29
  {
30
30
  key: 'profile',
31
31
  icon: <UserOutlined />,
32
- label: <Link to="/_dashboard/profile">{t('nav.profile')}</Link>,
32
+ label: <Link to="/profile">{t('nav.profile')}</Link>,
33
33
  },
34
34
  ];
35
35
 
@@ -1,7 +1,7 @@
1
1
  import { createFileRoute, Link } from '@tanstack/react-router';
2
2
  import { Button, Card } from 'antd';
3
3
  import { useAuthStore } from '@icore/template-shared';
4
- import { PageLayout } from '../../components/PageLayout';
4
+ import { PageLayout } from '@/components/PageLayout';
5
5
 
6
6
  function DashboardHome() {
7
7
  const user = useAuthStore((s) => s.user);
@@ -11,7 +11,7 @@ function DashboardHome() {
11
11
  title="Hello, world"
12
12
  style={{ maxWidth: 600 }}
13
13
  extra={
14
- <Link to="/_dashboard/profile">
14
+ <Link to="/profile">
15
15
  <Button type="link">Go to profile →</Button>
16
16
  </Link>
17
17
  }
@@ -3,14 +3,14 @@ import { useState } from 'react';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import { Button, Form, Input, Modal, Popconfirm, Space, Table, Typography } from 'antd';
5
5
  import type { ColumnsType } from 'antd/es/table';
6
- import { PageLayout } from '../../components/PageLayout';
6
+ import { PageLayout } from '@/components/PageLayout';
7
7
  import {
8
8
  useCreateNote,
9
9
  useDeleteNote,
10
10
  useNotesList,
11
11
  useUpdateNote,
12
12
  type Note,
13
- } from '../../queries/notes';
13
+ } from '@/queries/notes';
14
14
 
15
15
  const PAGE_SIZE = 20;
16
16
 
@@ -3,8 +3,8 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
3
3
  import { useEffect, useState } from 'react';
4
4
  import { Button, Form, Input } from 'antd';
5
5
  import { useDraft, useNotify, useAuthStore } from '@icore/template-shared';
6
- import { PageLayout } from '../../components/PageLayout';
7
- import { api } from '../../main';
6
+ import { PageLayout } from '@/components/PageLayout';
7
+ import { api } from '@/main';
8
8
 
9
9
  interface ProfilePayload {
10
10
  uid: string;
@@ -49,7 +49,7 @@ function CallbackPage() {
49
49
  .then((session) => {
50
50
  setAuth(session);
51
51
  setStatus('done');
52
- void navigate({ to: '/_dashboard/dashboard' });
52
+ void navigate({ to: '/dashboard' });
53
53
  })
54
54
  .catch((err) => {
55
55
  setStatus('error');
@@ -32,7 +32,7 @@ function OAuthCallbackPage() {
32
32
  user: { id: userId, email },
33
33
  });
34
34
  setStatus('done');
35
- void navigate({ to: '/_dashboard/dashboard' });
35
+ void navigate({ to: '/dashboard' });
36
36
  }, []);
37
37
 
38
38
  return (
@@ -41,7 +41,7 @@ function LoginPage() {
41
41
  });
42
42
  setAuth(session);
43
43
  notify.success(t('auth.login'));
44
- await navigate({ to: '/_dashboard/dashboard' });
44
+ await navigate({ to: '/dashboard' });
45
45
  } catch (err) {
46
46
  notify.error(err instanceof Error ? err.message : t('error.unknown'));
47
47
  }
@@ -8,7 +8,12 @@
8
8
  "esModuleInterop": false,
9
9
  "allowSyntheticDefaultImports": true,
10
10
  "strict": true,
11
- "types": ["vite/client", "vitest"]
11
+ "types": ["vite/client", "vitest"],
12
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
13
+ "paths": {
14
+ "@/*": ["./src/*"]
15
+ },
16
+ "baseUrl": "."
12
17
  },
13
18
  "files": [],
14
19
  "include": [],
@@ -14,12 +14,12 @@ test.describe('icore client-antd smoke', () => {
14
14
  });
15
15
 
16
16
  test('protected route redirects to login when unauthenticated', async ({ page }) => {
17
- await page.goto('/_dashboard/dashboard');
17
+ await page.goto('/dashboard');
18
18
  await expect(page).toHaveURL(/\/login$/);
19
19
  });
20
20
 
21
21
  test('profile route redirects to login when unauthenticated', async ({ page }) => {
22
- await page.goto('/_dashboard/profile');
22
+ await page.goto('/profile');
23
23
  await expect(page).toHaveURL(/\/login$/);
24
24
  });
25
25
  });
@@ -16,7 +16,7 @@ export function AccessDeniedPage() {
16
16
  {t('error.unknown')}
17
17
  </Typography>
18
18
  <Box>
19
- <Button component={Link} to="/_dashboard/dashboard" variant="contained">
19
+ <Button component={Link} to="/dashboard" variant="contained">
20
20
  Dashboard
21
21
  </Button>
22
22
  </Box>
@@ -91,7 +91,7 @@ export function LayoutHeader() {
91
91
  {user?.email ?? ''}
92
92
  </Typography>
93
93
  </MenuItem>
94
- <MenuItem component={Link} to="/_dashboard/profile" onClick={handleMenuClose}>
94
+ <MenuItem component={Link} to="/profile" onClick={handleMenuClose}>
95
95
  {t('nav.profile')}
96
96
  </MenuItem>
97
97
  <MenuItem onClick={handleLogout} sx={{ color: 'error.main' }}>
@@ -25,33 +25,21 @@ export function LayoutSider() {
25
25
  }}
26
26
  >
27
27
  <List>
28
- <ListItemButton
29
- component={Link}
30
- to="/_dashboard/dashboard"
31
- selected={pathname === '/dashboard'}
32
- >
28
+ <ListItemButton component={Link} to="/dashboard" selected={pathname === '/dashboard'}>
33
29
  <ListItemIcon>
34
30
  <DashboardOutlinedIcon />
35
31
  </ListItemIcon>
36
32
  <ListItemText primary={t('nav.dashboard')} />
37
33
  </ListItemButton>
38
34
 
39
- <ListItemButton
40
- component={Link}
41
- to="/_dashboard/notes"
42
- selected={pathname.includes('/notes')}
43
- >
35
+ <ListItemButton component={Link} to="/notes" selected={pathname.includes('/notes')}>
44
36
  <ListItemIcon>
45
37
  <NoteOutlinedIcon />
46
38
  </ListItemIcon>
47
39
  <ListItemText primary={t('notes.title')} />
48
40
  </ListItemButton>
49
41
 
50
- <ListItemButton
51
- component={Link}
52
- to="/_dashboard/profile"
53
- selected={pathname === '/profile'}
54
- >
42
+ <ListItemButton component={Link} to="/profile" selected={pathname === '/profile'}>
55
43
  <ListItemIcon>
56
44
  <PersonOutlineIcon />
57
45
  </ListItemIcon>
@@ -1,7 +1,7 @@
1
1
  import { createFileRoute, Link } from '@tanstack/react-router';
2
2
  import { Card, CardContent, CardHeader, Typography } from '@mui/material';
3
3
  import { useAuthStore } from '@icore/template-shared';
4
- import { PageLayout } from '../../components/PageLayout';
4
+ import { PageLayout } from '@/components/PageLayout';
5
5
 
6
6
  function DashboardHome() {
7
7
  const user = useAuthStore((s) => s.user);
@@ -13,11 +13,7 @@ function DashboardHome() {
13
13
  subheader="Edit this page in src/routes/_dashboard/dashboard.tsx"
14
14
  />
15
15
  <CardContent>
16
- <Typography
17
- component={Link}
18
- to="/_dashboard/profile"
19
- sx={{ textDecoration: 'underline' }}
20
- >
16
+ <Typography component={Link} to="/profile" sx={{ textDecoration: 'underline' }}>
21
17
  Go to profile →
22
18
  </Typography>
23
19
  </CardContent>
@@ -22,14 +22,14 @@ import {
22
22
  } from '@mui/material';
23
23
  import EditIcon from '@mui/icons-material/Edit';
24
24
  import DeleteIcon from '@mui/icons-material/Delete';
25
- import { PageLayout } from '../../components/PageLayout';
25
+ import { PageLayout } from '@/components/PageLayout';
26
26
  import {
27
27
  useCreateNote,
28
28
  useDeleteNote,
29
29
  useNotesList,
30
30
  useUpdateNote,
31
31
  type Note,
32
- } from '../../queries/notes';
32
+ } from '@/queries/notes';
33
33
 
34
34
  const PAGE_SIZE = 20;
35
35
 
@@ -3,8 +3,8 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
3
3
  import { useEffect, useState } from 'react';
4
4
  import { Box, Button, TextField } from '@mui/material';
5
5
  import { useDraft, useNotify, useAuthStore } from '@icore/template-shared';
6
- import { PageLayout } from '../../components/PageLayout';
7
- import { api } from '../../main';
6
+ import { PageLayout } from '@/components/PageLayout';
7
+ import { api } from '@/main';
8
8
 
9
9
  interface ProfilePayload {
10
10
  uid: string;
@@ -45,7 +45,7 @@ function ProfilePage() {
45
45
  onError: (err) => notify.error(err instanceof Error ? err.message : 'save_failed'),
46
46
  });
47
47
 
48
- function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
48
+ function handleSubmit(e: React.SyntheticEvent<HTMLFormElement>) {
49
49
  e.preventDefault();
50
50
  save.mutate(name);
51
51
  }
@@ -49,7 +49,7 @@ function CallbackPage() {
49
49
  .then((session) => {
50
50
  setAuth(session);
51
51
  setStatus('done');
52
- void navigate({ to: '/_dashboard/dashboard' });
52
+ void navigate({ to: '/dashboard' });
53
53
  })
54
54
  .catch((err) => {
55
55
  setStatus('error');
@@ -32,7 +32,7 @@ function OAuthCallbackPage() {
32
32
  user: { id: userId, email },
33
33
  });
34
34
  setStatus('done');
35
- void navigate({ to: '/_dashboard/dashboard' });
35
+ void navigate({ to: '/dashboard' });
36
36
  }, []);
37
37
 
38
38
  return (
@@ -32,7 +32,7 @@ function LoginPage() {
32
32
  const [sentEmail, setSentEmail] = useState('');
33
33
  const [submitting, setSubmitting] = useState(false);
34
34
 
35
- async function handlePasswordSubmit(e: React.FormEvent<HTMLFormElement>) {
35
+ async function handlePasswordSubmit(e: React.SyntheticEvent<HTMLFormElement>) {
36
36
  e.preventDefault();
37
37
  setSubmitting(true);
38
38
  try {
@@ -47,7 +47,7 @@ function LoginPage() {
47
47
  });
48
48
  setAuth(session);
49
49
  notify.success(t('auth.login'));
50
- await navigate({ to: '/_dashboard/dashboard' });
50
+ await navigate({ to: '/dashboard' });
51
51
  } catch (err) {
52
52
  notify.error(err instanceof Error ? err.message : t('error.unknown'));
53
53
  } finally {
@@ -55,7 +55,7 @@ function LoginPage() {
55
55
  }
56
56
  }
57
57
 
58
- async function handleMagicLinkSubmit(e: React.FormEvent<HTMLFormElement>) {
58
+ async function handleMagicLinkSubmit(e: React.SyntheticEvent<HTMLFormElement>) {
59
59
  e.preventDefault();
60
60
  setSubmitting(true);
61
61
  try {
@@ -8,7 +8,12 @@
8
8
  "esModuleInterop": false,
9
9
  "allowSyntheticDefaultImports": true,
10
10
  "strict": true,
11
- "types": ["vite/client", "vitest"]
11
+ "types": ["vite/client", "vitest"],
12
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
13
+ "paths": {
14
+ "@/*": ["./src/*"]
15
+ },
16
+ "baseUrl": "."
12
17
  },
13
18
  "files": [],
14
19
  "include": [],
@@ -14,12 +14,12 @@ test.describe('icore client-mui smoke', () => {
14
14
  });
15
15
 
16
16
  test('protected route redirects to login when unauthenticated', async ({ page }) => {
17
- await page.goto('/_dashboard/dashboard');
17
+ await page.goto('/dashboard');
18
18
  await expect(page).toHaveURL(/\/login$/);
19
19
  });
20
20
 
21
21
  test('profile route redirects to login when unauthenticated', async ({ page }) => {
22
- await page.goto('/_dashboard/profile');
22
+ await page.goto('/profile');
23
23
  await expect(page).toHaveURL(/\/login$/);
24
24
  });
25
25
  });
@@ -7,7 +7,7 @@ export function AccessDeniedPage() {
7
7
  <div className="min-h-[60vh] flex flex-col items-center justify-center gap-2 text-center px-6">
8
8
  <h1 className="text-2xl font-semibold">{t('error.accessDenied')}</h1>
9
9
  <p className="text-muted-foreground">{t('error.unknown')}</p>
10
- <Link to="/_dashboard/dashboard" className="mt-4 underline">
10
+ <Link to="/dashboard" className="mt-4 underline">
11
11
  ← Dashboard
12
12
  </Link>
13
13
  </div>
@@ -15,7 +15,7 @@ export function LayoutSider() {
15
15
  >
16
16
  <nav className="flex flex-col gap-1 p-2 flex-1">
17
17
  <Link
18
- to="/_dashboard/dashboard"
18
+ to="/dashboard"
19
19
  activeOptions={{ exact: true }}
20
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
21
  >
@@ -23,14 +23,14 @@ export function LayoutSider() {
23
23
  {!collapsed && <span>{t('nav.dashboard')}</span>}
24
24
  </Link>
25
25
  <Link
26
- to="/_dashboard/notes"
26
+ to="/notes"
27
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
28
  >
29
29
  <StickyNote size={16} className="shrink-0" />
30
30
  {!collapsed && <span>{t('notes.title')}</span>}
31
31
  </Link>
32
32
  <Link
33
- to="/_dashboard/profile"
33
+ to="/profile"
34
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
35
  >
36
36
  <User size={16} className="shrink-0" />
@@ -1,6 +1,6 @@
1
1
  import { useTranslation } from 'react-i18next';
2
2
  import { Button } from '../ui/button';
3
- import type { Note } from '../../queries/notes';
3
+ import type { Note } from '@/queries/notes';
4
4
 
5
5
  interface Props {
6
6
  note: Note | null;
@@ -1,9 +1,9 @@
1
- import { FormEvent, useEffect, useState } from 'react';
1
+ import { SyntheticEvent, useEffect, useState } from 'react';
2
2
  import { useTranslation } from 'react-i18next';
3
3
  import { Button } from '../ui/button';
4
4
  import { Input } from '../ui/input';
5
5
  import { Label } from '../ui/label';
6
- import type { Note } from '../../queries/notes';
6
+ import type { Note } from '@/queries/notes';
7
7
 
8
8
  interface Props {
9
9
  open: boolean;
@@ -27,7 +27,7 @@ export function NoteDialog({ open, initial, saving, onClose, onSubmit }: Props)
27
27
 
28
28
  if (!open) return null;
29
29
 
30
- function handleSubmit(e: FormEvent) {
30
+ function handleSubmit(e: SyntheticEvent<HTMLFormElement>) {
31
31
  e.preventDefault();
32
32
  onSubmit({ title, body });
33
33
  }
@@ -1,6 +1,6 @@
1
1
  import { useTranslation } from 'react-i18next';
2
2
  import { Button } from '../ui/button';
3
- import type { Note } from '../../queries/notes';
3
+ import type { Note } from '@/queries/notes';
4
4
 
5
5
  interface Props {
6
6
  items: Note[];
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { Slot } from '@radix-ui/react-slot';
3
3
  import { cva, type VariantProps } from 'class-variance-authority';
4
- import { cn } from '../../lib/utils';
4
+ import { cn } from '@/lib/utils';
5
5
 
6
6
  const buttonVariants = cva(
7
7
  'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
@@ -4,7 +4,7 @@
4
4
  // vendored design-system primitives that always travel together are kept
5
5
  // in a single file matching the upstream shadcn source structure.
6
6
  import * as React from 'react';
7
- import { cn } from '../../lib/utils';
7
+ import { cn } from '@/lib/utils';
8
8
 
9
9
  const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
10
10
  ({ className, ...props }, ref) => (
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { cn } from '../../lib/utils';
2
+ import { cn } from '@/lib/utils';
3
3
 
4
4
  export type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
5
5
 
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import * as LabelPrimitive from '@radix-ui/react-label';
3
3
  import { cva, type VariantProps } from 'class-variance-authority';
4
- import { cn } from '../../lib/utils';
4
+ import { cn } from '@/lib/utils';
5
5
 
6
6
  const labelVariants = cva(
7
7
  'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
@@ -1,13 +1,7 @@
1
1
  import { createFileRoute, Link } from '@tanstack/react-router';
2
2
  import { useAuthStore } from '@icore/template-shared';
3
- import {
4
- Card,
5
- CardContent,
6
- CardDescription,
7
- CardHeader,
8
- CardTitle,
9
- } from '../../components/ui/card';
10
- import { PageLayout } from '../../components/PageLayout';
3
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
4
+ import { PageLayout } from '@/components/PageLayout';
11
5
 
12
6
  function DashboardHome() {
13
7
  const user = useAuthStore((s) => s.user);
@@ -19,7 +13,7 @@ function DashboardHome() {
19
13
  <CardDescription>Edit this page in src/routes/_dashboard/dashboard.tsx</CardDescription>
20
14
  </CardHeader>
21
15
  <CardContent>
22
- <Link to="/_dashboard/profile" className="underline">
16
+ <Link to="/profile" className="underline">
23
17
  Go to profile →
24
18
  </Link>
25
19
  </CardContent>
@@ -1,18 +1,18 @@
1
1
  import { createFileRoute } from '@tanstack/react-router';
2
2
  import { useState } from 'react';
3
3
  import { useTranslation } from 'react-i18next';
4
- import { PageLayout } from '../../components/PageLayout';
5
- import { Button } from '../../components/ui/button';
6
- import { NotesTable } from '../../components/notes/NotesTable';
7
- import { NoteDialog } from '../../components/notes/NoteDialog';
8
- import { DeleteNoteConfirm } from '../../components/notes/DeleteNoteConfirm';
4
+ import { PageLayout } from '@/components/PageLayout';
5
+ import { Button } from '@/components/ui/button';
6
+ import { NotesTable } from '@/components/notes/NotesTable';
7
+ import { NoteDialog } from '@/components/notes/NoteDialog';
8
+ import { DeleteNoteConfirm } from '@/components/notes/DeleteNoteConfirm';
9
9
  import {
10
10
  useCreateNote,
11
11
  useDeleteNote,
12
12
  useNotesList,
13
13
  useUpdateNote,
14
14
  type Note,
15
- } from '../../queries/notes';
15
+ } from '@/queries/notes';
16
16
 
17
17
  const PAGE_SIZE = 20;
18
18
 
@@ -1,12 +1,12 @@
1
1
  import { createFileRoute } from '@tanstack/react-router';
2
2
  import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
3
- import { FormEvent, useEffect, useState } from 'react';
3
+ import { SyntheticEvent, useEffect, useState } from 'react';
4
4
  import { useDraft, useNotify, useAuthStore } from '@icore/template-shared';
5
- import { PageLayout } from '../../components/PageLayout';
6
- import { Button } from '../../components/ui/button';
7
- import { Input } from '../../components/ui/input';
8
- import { Label } from '../../components/ui/label';
9
- import { api } from '../../main';
5
+ import { PageLayout } from '@/components/PageLayout';
6
+ import { Button } from '@/components/ui/button';
7
+ import { Input } from '@/components/ui/input';
8
+ import { Label } from '@/components/ui/label';
9
+ import { api } from '@/main';
10
10
 
11
11
  interface ProfilePayload {
12
12
  uid: string;
@@ -47,7 +47,7 @@ function ProfilePage() {
47
47
  onError: (err) => notify.error(err instanceof Error ? err.message : 'save_failed'),
48
48
  });
49
49
 
50
- function handleSubmit(e: FormEvent) {
50
+ function handleSubmit(e: SyntheticEvent<HTMLFormElement>) {
51
51
  e.preventDefault();
52
52
  save.mutate(name);
53
53
  }
@@ -49,7 +49,7 @@ function CallbackPage() {
49
49
  .then((session) => {
50
50
  setAuth(session);
51
51
  setStatus('done');
52
- void navigate({ to: '/_dashboard/dashboard' });
52
+ void navigate({ to: '/dashboard' });
53
53
  })
54
54
  .catch((err) => {
55
55
  setStatus('error');
@@ -32,7 +32,7 @@ function OAuthCallbackPage() {
32
32
  user: { id: userId, email },
33
33
  });
34
34
  setStatus('done');
35
- void navigate({ to: '/_dashboard/dashboard' });
35
+ void navigate({ to: '/dashboard' });
36
36
  }, []);
37
37
 
38
38
  return (
@@ -1,12 +1,19 @@
1
- import { createFileRoute, useNavigate } from '@tanstack/react-router';
2
- import { FormEvent, useState } from 'react';
3
- import { useTranslation } from 'react-i18next';
4
- import { useAuthStore, useNotify } from '@icore/template-shared';
5
- import { api } from '../main';
6
- import { Button } from '../components/ui/button';
7
- import { Input } from '../components/ui/input';
8
- import { Label } from '../components/ui/label';
9
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../components/ui/card';
1
+ import { SyntheticEvent, createFileRoute, useNavigate } from '@tanstack/react-router';
2
+ import { SyntheticEvent, useState } from 'react';
3
+ import { SyntheticEvent, useTranslation } from 'react-i18next';
4
+ import { SyntheticEvent, useAuthStore, useNotify } from '@icore/template-shared';
5
+ import { SyntheticEvent, api } from '../main';
6
+ import { SyntheticEvent, Button } from '../components/ui/button';
7
+ import { SyntheticEvent, Input } from '../components/ui/input';
8
+ import { SyntheticEvent, Label } from '../components/ui/label';
9
+ import {
10
+ SyntheticEvent,
11
+ Card,
12
+ CardContent,
13
+ CardDescription,
14
+ CardHeader,
15
+ CardTitle,
16
+ } from '../components/ui/card';
10
17
 
11
18
  type Mode = 'password' | 'magicLinkRequest' | 'magicLinkSent';
12
19
 
@@ -21,7 +28,7 @@ function LoginPage() {
21
28
  const [password, setPassword] = useState('');
22
29
  const [submitting, setSubmitting] = useState(false);
23
30
 
24
- async function handlePasswordSubmit(e: FormEvent) {
31
+ async function handlePasswordSubmit(e: SyntheticEvent<HTMLFormElement>) {
25
32
  e.preventDefault();
26
33
  setSubmitting(true);
27
34
  try {
@@ -36,7 +43,7 @@ function LoginPage() {
36
43
  });
37
44
  setAuth(session);
38
45
  notify.success(t('auth.login'));
39
- await navigate({ to: '/_dashboard/dashboard' });
46
+ await navigate({ to: '/dashboard' });
40
47
  } catch (err) {
41
48
  notify.error(err instanceof Error ? err.message : t('error.unknown'));
42
49
  } finally {
@@ -44,7 +51,7 @@ function LoginPage() {
44
51
  }
45
52
  }
46
53
 
47
- async function handleMagicLinkSubmit(e: FormEvent) {
54
+ async function handleMagicLinkSubmit(e: SyntheticEvent<HTMLFormElement>) {
48
55
  e.preventDefault();
49
56
  setSubmitting(true);
50
57
  try {
@@ -8,7 +8,12 @@
8
8
  "esModuleInterop": false,
9
9
  "allowSyntheticDefaultImports": true,
10
10
  "strict": true,
11
- "types": ["vite/client", "vitest"]
11
+ "types": ["vite/client", "vitest"],
12
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
13
+ "paths": {
14
+ "@/*": ["./src/*"]
15
+ },
16
+ "baseUrl": "."
12
17
  },
13
18
  "files": [],
14
19
  "include": [],