@auto-engineer/frontend-generator-react-graphql 0.11.3 → 0.11.5

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 (98) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/mui-starter/auto-configure.ts +114 -0
  3. package/dist/mui-starter/codegen.ts +16 -0
  4. package/dist/mui-starter/design-system-principles.md +32 -0
  5. package/dist/mui-starter/eslint.config.ts +57 -0
  6. package/dist/mui-starter/index.html +26 -0
  7. package/dist/mui-starter/package.json +70 -0
  8. package/dist/mui-starter/postcss.config.ts +5 -0
  9. package/dist/mui-starter/public/favicon.ico +0 -0
  10. package/dist/mui-starter/src/App.css +42 -0
  11. package/dist/mui-starter/src/App.tsx +21 -0
  12. package/dist/mui-starter/src/apolloClient.ts +8 -0
  13. package/dist/mui-starter/src/hooks/use-mobile.tsx +19 -0
  14. package/dist/mui-starter/src/hooks/use-toast.ts +186 -0
  15. package/dist/mui-starter/src/index.css +0 -0
  16. package/dist/mui-starter/src/main.tsx +5 -0
  17. package/dist/mui-starter/src/pages/Index.tsx +10 -0
  18. package/dist/mui-starter/src/pages/NotFound.tsx +20 -0
  19. package/dist/mui-starter/src/theme.ts +86 -0
  20. package/dist/mui-starter/src/theme.ts.ejs +3 -0
  21. package/dist/mui-starter/tsconfig.json +40 -0
  22. package/dist/mui-starter/vite.config.ts +21 -0
  23. package/dist/shadcn-starter/auto-configure.ts +107 -0
  24. package/dist/shadcn-starter/codegen.ts +16 -0
  25. package/dist/shadcn-starter/components.json +20 -0
  26. package/dist/shadcn-starter/design-system-principles.md +23 -0
  27. package/dist/shadcn-starter/eslint.config.ts +57 -0
  28. package/dist/shadcn-starter/index.html +26 -0
  29. package/dist/shadcn-starter/package.json +101 -0
  30. package/dist/shadcn-starter/pnpm-lock.yaml +8234 -0
  31. package/dist/shadcn-starter/postcss.config.ts +6 -0
  32. package/dist/shadcn-starter/public/favicon.ico +0 -0
  33. package/dist/shadcn-starter/src/App.css +6 -0
  34. package/dist/shadcn-starter/src/App.tsx +28 -0
  35. package/dist/shadcn-starter/src/apolloClient.ts +8 -0
  36. package/dist/shadcn-starter/src/components/atoms/accordion.tsx +52 -0
  37. package/dist/shadcn-starter/src/components/atoms/alert-dialog.tsx +104 -0
  38. package/dist/shadcn-starter/src/components/atoms/alert.tsx +43 -0
  39. package/dist/shadcn-starter/src/components/atoms/aspect-ratio.tsx +5 -0
  40. package/dist/shadcn-starter/src/components/atoms/avatar.tsx +40 -0
  41. package/dist/shadcn-starter/src/components/atoms/badge.tsx +29 -0
  42. package/dist/shadcn-starter/src/components/atoms/breadcrumb.tsx +90 -0
  43. package/dist/shadcn-starter/src/components/atoms/button.tsx +47 -0
  44. package/dist/shadcn-starter/src/components/atoms/calendar.tsx +158 -0
  45. package/dist/shadcn-starter/src/components/atoms/card.tsx +43 -0
  46. package/dist/shadcn-starter/src/components/atoms/carousel.tsx +224 -0
  47. package/dist/shadcn-starter/src/components/atoms/chart.tsx +307 -0
  48. package/dist/shadcn-starter/src/components/atoms/checkbox.tsx +26 -0
  49. package/dist/shadcn-starter/src/components/atoms/collapsible.tsx +11 -0
  50. package/dist/shadcn-starter/src/components/atoms/command.tsx +132 -0
  51. package/dist/shadcn-starter/src/components/atoms/context-menu.tsx +178 -0
  52. package/dist/shadcn-starter/src/components/atoms/dialog.tsx +97 -0
  53. package/dist/shadcn-starter/src/components/atoms/drawer.tsx +87 -0
  54. package/dist/shadcn-starter/src/components/atoms/dropdown-menu.tsx +181 -0
  55. package/dist/shadcn-starter/src/components/atoms/form.tsx +136 -0
  56. package/dist/shadcn-starter/src/components/atoms/hover-card.tsx +27 -0
  57. package/dist/shadcn-starter/src/components/atoms/input-otp.tsx +61 -0
  58. package/dist/shadcn-starter/src/components/atoms/input.tsx +22 -0
  59. package/dist/shadcn-starter/src/components/atoms/label.tsx +19 -0
  60. package/dist/shadcn-starter/src/components/atoms/menubar.tsx +217 -0
  61. package/dist/shadcn-starter/src/components/atoms/navigation-menu.tsx +120 -0
  62. package/dist/shadcn-starter/src/components/atoms/pagination.tsx +81 -0
  63. package/dist/shadcn-starter/src/components/atoms/popover.tsx +29 -0
  64. package/dist/shadcn-starter/src/components/atoms/progress.tsx +25 -0
  65. package/dist/shadcn-starter/src/components/atoms/radio-group.tsx +36 -0
  66. package/dist/shadcn-starter/src/components/atoms/resizable.tsx +39 -0
  67. package/dist/shadcn-starter/src/components/atoms/scroll-area.tsx +38 -0
  68. package/dist/shadcn-starter/src/components/atoms/select.tsx +145 -0
  69. package/dist/shadcn-starter/src/components/atoms/separator.tsx +20 -0
  70. package/dist/shadcn-starter/src/components/atoms/sheet.tsx +109 -0
  71. package/dist/shadcn-starter/src/components/atoms/sidebar.tsx +641 -0
  72. package/dist/shadcn-starter/src/components/atoms/skeleton.tsx +7 -0
  73. package/dist/shadcn-starter/src/components/atoms/slider.tsx +23 -0
  74. package/dist/shadcn-starter/src/components/atoms/sonner.tsx +29 -0
  75. package/dist/shadcn-starter/src/components/atoms/switch.tsx +27 -0
  76. package/dist/shadcn-starter/src/components/atoms/table.tsx +72 -0
  77. package/dist/shadcn-starter/src/components/atoms/tabs.tsx +53 -0
  78. package/dist/shadcn-starter/src/components/atoms/textarea.tsx +21 -0
  79. package/dist/shadcn-starter/src/components/atoms/toggle-group.tsx +51 -0
  80. package/dist/shadcn-starter/src/components/atoms/toggle.tsx +37 -0
  81. package/dist/shadcn-starter/src/components/atoms/tooltip.tsx +30 -0
  82. package/dist/shadcn-starter/src/hooks/use-mobile.tsx +19 -0
  83. package/dist/shadcn-starter/src/hooks/use-toast.ts +186 -0
  84. package/dist/shadcn-starter/src/index.css +103 -0
  85. package/dist/shadcn-starter/src/index.css.ejs +27 -0
  86. package/dist/shadcn-starter/src/lib/utils.ts +6 -0
  87. package/dist/shadcn-starter/src/main.tsx +5 -0
  88. package/dist/shadcn-starter/src/mockApolloClient.ts +93 -0
  89. package/dist/shadcn-starter/src/pages/NotFound.tsx +22 -0
  90. package/dist/shadcn-starter/tailwind.config.ts +92 -0
  91. package/dist/shadcn-starter/tsconfig.json +48 -0
  92. package/dist/shadcn-starter/vite.config.ts +21 -0
  93. package/dist/src/commands/generate-client.d.ts +2 -1
  94. package/dist/src/commands/generate-client.d.ts.map +1 -1
  95. package/dist/src/commands/generate-client.js +14 -4
  96. package/dist/src/commands/generate-client.js.map +1 -1
  97. package/dist/tsconfig.tsbuildinfo +1 -1
  98. package/package.json +5 -5
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @auto-engineer/frontend-react-graphql-generator
2
2
 
3
+ ## 0.11.5
4
+
5
+ ### Patch Changes
6
+
7
+ - Fix paths and deps issues
8
+
9
+ - Updated dependencies []:
10
+ - @auto-engineer/message-bus@0.11.5
11
+ - @auto-engineer/ai-gateway@0.11.5
12
+
13
+ ## 0.11.4
14
+
15
+ ### Patch Changes
16
+
17
+ - Fixes paths and deps
18
+
19
+ - Updated dependencies []:
20
+ - @auto-engineer/ai-gateway@0.11.4
21
+ - @auto-engineer/message-bus@0.11.4
22
+
3
23
  ## 0.11.3
4
24
 
5
25
  ### Patch Changes
@@ -0,0 +1,114 @@
1
+ import { createFile, templatePropsAIMapper } from '@auto-engineer/frontend-generator-react-graphql';
2
+
3
+ const MUIObject = {
4
+ palette: {
5
+ text: {
6
+ primary: '',
7
+ secondary: '',
8
+ disabled: '',
9
+ },
10
+ primary: {
11
+ main: '',
12
+ light: '',
13
+ dark: '',
14
+ contrastText: '',
15
+ },
16
+ secondary: {
17
+ main: '',
18
+ light: '',
19
+ dark: '',
20
+ contrastText: '',
21
+ },
22
+ background: {
23
+ default: '',
24
+ paper: '',
25
+ },
26
+ action: {
27
+ disabled: '',
28
+ active: '',
29
+ focus: '',
30
+ hover: '',
31
+ activatedOpacity: 1,
32
+ disabledBackground: '',
33
+ focusOpacity: 1,
34
+ selected: '',
35
+ disabledOpacity: 1,
36
+ hoverOpacity: 1,
37
+ selectedOpacity: 1,
38
+ },
39
+ error: {
40
+ main: '',
41
+ light: '',
42
+ dark: '',
43
+ contrastText: '',
44
+ },
45
+ warning: {
46
+ main: '',
47
+ light: '',
48
+ dark: '',
49
+ contrastText: '',
50
+ },
51
+ info: {
52
+ main: '',
53
+ light: '',
54
+ dark: '',
55
+ contrastText: '',
56
+ },
57
+ success: {
58
+ main: '',
59
+ light: '',
60
+ dark: '',
61
+ contrastText: '',
62
+ },
63
+ divider: '',
64
+ },
65
+ breakpoints: {
66
+ values: {
67
+ xs: 444,
68
+ sm: 600,
69
+ md: 900,
70
+ lg: 1200,
71
+ xl: 1536,
72
+ },
73
+ },
74
+ spacing: 8,
75
+ shape: {
76
+ borderRadius: 4,
77
+ },
78
+ typography: {
79
+ fontFamily: 'Roboto',
80
+ fontWeightLight: 300,
81
+ fontWeightRegular: 400,
82
+ fontWeightMedium: 500,
83
+ fontWeightBold: 700,
84
+ fontSize: 16,
85
+ },
86
+ };
87
+
88
+ await createFile(
89
+ 'theme.ts.ejs',
90
+ 'theme.ts',
91
+ templatePropsAIMapper('../.context/figma-file.json', {
92
+ prompt: `
93
+ - Goal: Map Figma variables into a valid MUI v7+ createTheme options object like this: ${JSON.stringify(MUIObject)}
94
+ - The expected output format is:
95
+ {
96
+ "themeOptions": { ... }
97
+ }
98
+ - Use only the properties explicitly requested in the given MUI object template.
99
+ - If an exact match does not exist, try to find the closest variable, but leave it empty if there’s no good fit.
100
+ - Do NOT include any functions or computed values in the theme, for example:
101
+ - WRONG: "spacing": (factor) => factor * 8
102
+ - CORRECT: "spacing": 4
103
+ - Follow official MUI structure and guidelines for palette, typography, spacing, breakpoints, z-index, and transitions.
104
+
105
+ DOCS LINKS:
106
+ - https://mui.com/material-ui/customization/palette/
107
+ - https://mui.com/material-ui/customization/typography/
108
+ - https://mui.com/material-ui/customization/spacing/
109
+ - https://mui.com/material-ui/customization/breakpoints/
110
+ - https://mui.com/material-ui/customization/z-index/
111
+ - https://mui.com/material-ui/customization/transitions/
112
+ `,
113
+ }),
114
+ );
@@ -0,0 +1,16 @@
1
+ import type { CodegenConfig } from '@graphql-codegen/cli';
2
+
3
+ const config: CodegenConfig = {
4
+ overwrite: true,
5
+ schema: './schema.graphql',
6
+ documents: ['src/graphql/**/*.ts', 'src/graphql/**/*.tsx'],
7
+ ignoreNoDocuments: true,
8
+ generates: {
9
+ 'src/gql/': {
10
+ preset: 'client',
11
+ plugins: [],
12
+ },
13
+ },
14
+ };
15
+
16
+ export default config;
@@ -0,0 +1,32 @@
1
+ Theme
2
+
3
+ - DO NOT MODIFY THE THEME TOKENS UNDER `src/theme.ts`! IMPORTANT
4
+
5
+ Components
6
+
7
+ - use atoms from @mui/material instead of creating new ones in components/atoms
8
+ - use Typography component from @mui/material for text elements whenever possible
9
+
10
+ Branding & Visual Identity:
11
+
12
+ - You must **infer the branding** (tone, visual identity, personality) directly from the MUI theme config in `src/theme.ts` and do NOT change those tokens
13
+ - Use the brand's tone and color palette consistently across all components, spacing, interactions, and layout structure.
14
+ - Avoid visual indicators like H1/H2 headings — do not use them to represent concepts or sections.
15
+ - Communicate page hierarchy through layout, spacing, color intensity, and component usage — **not headings**.
16
+
17
+ Design Patterns:
18
+
19
+ - Layouts must be responsive by default.
20
+ - Components must reflect brand personality: minimal, bold, playful, formal, etc., as inferred from the theme.
21
+ - Component files should be small and composable.
22
+
23
+ UX Principles:
24
+
25
+ - Do not use visual headers or hero titles to convey page identity.
26
+ - Use dynamic hierarchy expressed through tone, spacing, and primary-color signals.
27
+ - Prefer subtle transitions and clear focus indicators.
28
+
29
+ Page Templates
30
+
31
+ - **Sidebar with main panel**
32
+ - 30/70 panels
@@ -0,0 +1,57 @@
1
+ import js from '@eslint/js';
2
+ import globals from 'globals';
3
+ import reactHooks from 'eslint-plugin-react-hooks';
4
+ import reactRefresh from 'eslint-plugin-react-refresh';
5
+ import tseslint from 'typescript-eslint';
6
+ import { dirname } from 'path';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+
11
+ export default tseslint.config(
12
+ js.configs.recommended,
13
+ ...tseslint.configs.recommended,
14
+ {
15
+ languageOptions: {
16
+ parserOptions: {
17
+ tsconfigRootDir: __dirname,
18
+ project: ['./tsconfig.json'],
19
+ },
20
+ },
21
+ },
22
+ {
23
+ ignores: ['dist', 'eslint.config.ts', 'postcss.config.js'],
24
+ },
25
+ {
26
+ files: ['**/*.{ts,tsx}'],
27
+ languageOptions: {
28
+ ecmaVersion: 2020,
29
+ globals: globals.browser,
30
+ parserOptions: {
31
+ project: './tsconfig.json',
32
+ },
33
+ },
34
+ plugins: {
35
+ 'react-hooks': reactHooks,
36
+ 'react-refresh': reactRefresh,
37
+ },
38
+ rules: {
39
+ ...reactHooks.configs.recommended.rules,
40
+ 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
41
+ '@typescript-eslint/no-unused-vars': 'off',
42
+ '@typescript-eslint/no-unsafe-assignment': 'off',
43
+ '@typescript-eslint/no-unsafe-call': 'off',
44
+ '@typescript-eslint/no-unsafe-member-access': 'off',
45
+ '@typescript-eslint/no-unsafe-return': 'off',
46
+ '@typescript-eslint/strict-boolean-expressions': 'off',
47
+ '@typescript-eslint/no-floating-promises': 'off',
48
+ '@typescript-eslint/no-misused-promises': 'off',
49
+ '@typescript-eslint/no-implied-eval': 'off',
50
+ '@typescript-eslint/no-unnecessary-type-assertion': 'off',
51
+ '@typescript-eslint/restrict-plus-operands': 'off',
52
+ '@typescript-eslint/restrict-template-expressions': 'off',
53
+ '@typescript-eslint/unbound-method': 'off',
54
+ '@typescript-eslint/no-explicit-any': 'off',
55
+ },
56
+ },
57
+ );
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Auto Starter</title>
8
+ <meta name="description" content="Auto Generated Project" />
9
+ <meta name="author" content="Auto" />
10
+
11
+ <meta property="og:title" content="Auto Starter" />
12
+ <meta property="og:description" content="Auto Generated Project" />
13
+ <meta property="og:type" content="website" />
14
+ <!-- <meta property="og:image" content="https://on.auto/opengraph-image-p98pqg.png" /> -->
15
+
16
+ <meta name="twitter:card" content="summary_large_image" />
17
+ <meta name="twitter:site" content="@beonauto" />
18
+ <meta name="twitter:image" content="https://on.auto/opengraph-image-p98pqg.png" />
19
+ </head>
20
+
21
+ <body>
22
+ <div id="root"></div>
23
+ <script type="module" src="/src/main.tsx"></script>
24
+ </body>
25
+
26
+ </html>
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@examples/react-graphql-mui-starter",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "start": "vite",
9
+ "build": "vite build",
10
+ "build:dev": "vite build --mode development",
11
+ "lint": "eslint .",
12
+ "preview": "vite preview",
13
+ "codegen": "graphql-codegen --config codegen.ts",
14
+ "format": "prettier --write \"**/*.{js,ts,json,md,yml,yaml}\" --ignore-path ../../../.prettierignore --log-level warn",
15
+ "type:check": "tsc --noEmit",
16
+ "lint:fix": "eslint .",
17
+ "format:fix": "prettier --write \"**/*.{js,ts,json,md,yml,yaml}\" --ignore-path ../../../.prettierignore --log-level warn",
18
+ "auto-configure": "tsx auto-configure.ts"
19
+ },
20
+ "dependencies": {
21
+ "@apollo/client": "^3.13.8",
22
+ "@emotion/react": "^11.14.0",
23
+ "@emotion/styled": "^11.14.1",
24
+ "@hookform/resolvers": "^3.10.0",
25
+ "@mui/material": "^7.3.1",
26
+ "@tanstack/react-query": "^5.56.2",
27
+ "class-variance-authority": "^0.7.1",
28
+ "clsx": "^2.1.1",
29
+ "cmdk": "^1.1.1",
30
+ "date-fns": "^3.6.0",
31
+ "embla-carousel-react": "^8.6.0",
32
+ "graphql": "16.8.1",
33
+ "input-otp": "^1.4.2",
34
+ "lucide-react": "^0.462.0",
35
+ "next-themes": "^0.3.0",
36
+ "react": "^18.3.1",
37
+ "react-day-picker": "^9.7.0",
38
+ "react-dom": "^18.3.1",
39
+ "react-hook-form": "^7.57.0",
40
+ "react-resizable-panels": "^2.1.9",
41
+ "react-router-dom": "^6.26.2",
42
+ "recharts": "^2.15.3",
43
+ "sonner": "^1.7.4",
44
+ "vaul": "^0.9.9",
45
+ "zod": "^3.25.63",
46
+ "@auto-engineer/frontend-generator-react-graphql": "workspace:*",
47
+ "@auto-engineer/ai-gateway": "workspace:*",
48
+ "ejs": "^3.1.10",
49
+ "commander": "^14.0.0"
50
+ },
51
+ "devDependencies": {
52
+ "@eslint/js": "^9.9.0",
53
+ "@graphql-codegen/cli": "3.2.0",
54
+ "@graphql-codegen/client-preset": "2.1.0",
55
+ "@graphql-typed-document-node/core": "^3.2.0",
56
+ "@types/node": "^22.5.5",
57
+ "@types/react": "^18.3.3",
58
+ "@types/react-dom": "^18.3.0",
59
+ "@vitejs/plugin-react": "^4.7.0",
60
+ "autoprefixer": "^10.4.20",
61
+ "eslint": "^9.9.0",
62
+ "eslint-plugin-react-hooks": "^5.1.0-rc.0",
63
+ "eslint-plugin-react-refresh": "^0.4.9",
64
+ "globals": "^15.9.0",
65
+ "postcss": "^8.4.47",
66
+ "typescript": "^5.5.3",
67
+ "typescript-eslint": "^8.0.1",
68
+ "vite": "^5.4.1"
69
+ }
70
+ }
@@ -0,0 +1,5 @@
1
+ import autoprefixer from 'autoprefixer';
2
+
3
+ export default {
4
+ plugins: [autoprefixer],
5
+ };
File without changes
@@ -0,0 +1,42 @@
1
+ #root {
2
+ max-width: 1280px;
3
+ margin: 0 auto;
4
+ padding: 2rem;
5
+ text-align: center;
6
+ }
7
+
8
+ .logo {
9
+ height: 6em;
10
+ padding: 1.5em;
11
+ will-change: filter;
12
+ transition: filter 300ms;
13
+ }
14
+ .logo:hover {
15
+ filter: drop-shadow(0 0 2em #646cffaa);
16
+ }
17
+ .logo.react:hover {
18
+ filter: drop-shadow(0 0 2em #61dafbaa);
19
+ }
20
+
21
+ @keyframes logo-spin {
22
+ from {
23
+ transform: rotate(0deg);
24
+ }
25
+ to {
26
+ transform: rotate(360deg);
27
+ }
28
+ }
29
+
30
+ @media (prefers-reduced-motion: no-preference) {
31
+ a:nth-of-type(2) .logo {
32
+ animation: logo-spin infinite 20s linear;
33
+ }
34
+ }
35
+
36
+ .card {
37
+ padding: 2em;
38
+ }
39
+
40
+ .read-the-docs {
41
+ color: #888;
42
+ }
@@ -0,0 +1,21 @@
1
+ import { ApolloProvider } from '@apollo/client';
2
+ import { BrowserRouter, Route, Routes } from 'react-router-dom';
3
+ import { Index } from '@/pages/Index';
4
+ import { NotFound } from '@/pages/NotFound';
5
+ import { apolloClient } from '@/apolloClient';
6
+ import { ThemeProvider } from '@mui/material';
7
+ import { theme } from '@/theme.ts';
8
+
9
+ export const App = () => (
10
+ <ApolloProvider client={apolloClient}>
11
+ <ThemeProvider theme={theme}>
12
+ <BrowserRouter>
13
+ <Routes>
14
+ <Route path="/" element={<Index />} />
15
+ {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */}
16
+ <Route path="*" element={<NotFound />} />
17
+ </Routes>
18
+ </BrowserRouter>
19
+ </ThemeProvider>
20
+ </ApolloProvider>
21
+ );
@@ -0,0 +1,8 @@
1
+ import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';
2
+
3
+ export const apolloClient = new ApolloClient({
4
+ link: new HttpLink({
5
+ uri: 'http://localhost:4000/graphql',
6
+ }),
7
+ cache: new InMemoryCache(),
8
+ });
@@ -0,0 +1,19 @@
1
+ import * as React from 'react';
2
+
3
+ const MOBILE_BREAKPOINT = 768;
4
+
5
+ export function useMobile() {
6
+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined);
7
+
8
+ React.useEffect(() => {
9
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
10
+ const onChange = () => {
11
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
12
+ };
13
+ mql.addEventListener('change', onChange);
14
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
15
+ return () => mql.removeEventListener('change', onChange);
16
+ }, []);
17
+
18
+ return !!isMobile;
19
+ }
@@ -0,0 +1,186 @@
1
+ import * as React from 'react';
2
+
3
+ const TOAST_LIMIT = 1;
4
+ const TOAST_REMOVE_DELAY = 1000000;
5
+
6
+ type ToasterToast = any & {
7
+ id: string;
8
+ title?: React.ReactNode;
9
+ description?: React.ReactNode;
10
+ action?: any;
11
+ };
12
+
13
+ const actionTypes = {
14
+ ADD_TOAST: 'ADD_TOAST',
15
+ UPDATE_TOAST: 'UPDATE_TOAST',
16
+ DISMISS_TOAST: 'DISMISS_TOAST',
17
+ REMOVE_TOAST: 'REMOVE_TOAST',
18
+ } as const;
19
+
20
+ let count = 0;
21
+
22
+ function genId() {
23
+ count = (count + 1) % Number.MAX_SAFE_INTEGER;
24
+ return count.toString();
25
+ }
26
+
27
+ type ActionType = typeof actionTypes;
28
+
29
+ type Action =
30
+ | {
31
+ type: ActionType['ADD_TOAST'];
32
+ toast: ToasterToast;
33
+ }
34
+ | {
35
+ type: ActionType['UPDATE_TOAST'];
36
+ toast: Partial<ToasterToast>;
37
+ }
38
+ | {
39
+ type: ActionType['DISMISS_TOAST'];
40
+ toastId?: ToasterToast['id'];
41
+ }
42
+ | {
43
+ type: ActionType['REMOVE_TOAST'];
44
+ toastId?: ToasterToast['id'];
45
+ };
46
+
47
+ interface State {
48
+ toasts: ToasterToast[];
49
+ }
50
+
51
+ const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
52
+
53
+ const addToRemoveQueue = (toastId: string) => {
54
+ if (toastTimeouts.has(toastId)) {
55
+ return;
56
+ }
57
+
58
+ const timeout = setTimeout(() => {
59
+ toastTimeouts.delete(toastId);
60
+ dispatch({
61
+ type: 'REMOVE_TOAST',
62
+ toastId: toastId,
63
+ });
64
+ }, TOAST_REMOVE_DELAY);
65
+
66
+ toastTimeouts.set(toastId, timeout);
67
+ };
68
+
69
+ export const reducer = (state: State, action: Action): State => {
70
+ switch (action.type) {
71
+ case 'ADD_TOAST':
72
+ return {
73
+ ...state,
74
+ toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
75
+ };
76
+
77
+ case 'UPDATE_TOAST':
78
+ return {
79
+ ...state,
80
+ toasts: state.toasts.map((t) => (t.id === action.toast.id ? { ...t, ...action.toast } : t)),
81
+ };
82
+
83
+ case 'DISMISS_TOAST': {
84
+ const { toastId } = action;
85
+
86
+ // ! Side effects ! - This could be extracted into a dismissToast() action,
87
+ // but I'll keep it here for simplicity
88
+ if (toastId) {
89
+ addToRemoveQueue(toastId);
90
+ } else {
91
+ state.toasts.forEach((toast) => {
92
+ addToRemoveQueue(toast.id);
93
+ });
94
+ }
95
+
96
+ return {
97
+ ...state,
98
+ toasts: state.toasts.map((t) =>
99
+ t.id === toastId || toastId === undefined
100
+ ? {
101
+ ...t,
102
+ open: false,
103
+ }
104
+ : t,
105
+ ),
106
+ };
107
+ }
108
+ case 'REMOVE_TOAST':
109
+ if (action.toastId === undefined) {
110
+ return {
111
+ ...state,
112
+ toasts: [],
113
+ };
114
+ }
115
+ return {
116
+ ...state,
117
+ toasts: state.toasts.filter((t) => t.id !== action.toastId),
118
+ };
119
+ }
120
+ };
121
+
122
+ const listeners: Array<(state: State) => void> = [];
123
+
124
+ let memoryState: State = { toasts: [] };
125
+
126
+ function dispatch(action: Action) {
127
+ memoryState = reducer(memoryState, action);
128
+ listeners.forEach((listener) => {
129
+ listener(memoryState);
130
+ });
131
+ }
132
+
133
+ type Toast = Omit<ToasterToast, 'id'>;
134
+
135
+ function toast({ ...props }: Toast) {
136
+ const id = genId();
137
+
138
+ const update = (props: ToasterToast) =>
139
+ dispatch({
140
+ type: 'UPDATE_TOAST',
141
+ toast: { ...props, id },
142
+ });
143
+ const dismiss = () => dispatch({ type: 'DISMISS_TOAST', toastId: id });
144
+
145
+ dispatch({
146
+ type: 'ADD_TOAST',
147
+ toast: {
148
+ ...props,
149
+ id,
150
+ open: true,
151
+ onOpenChange: (open) => {
152
+ if (!open) dismiss();
153
+ },
154
+ },
155
+ });
156
+
157
+ return {
158
+ id: id,
159
+ dismiss,
160
+ update,
161
+ };
162
+ }
163
+
164
+ function useToast() {
165
+ const [state, setState] = React.useState<State>(memoryState);
166
+
167
+ React.useEffect(() => {
168
+ listeners.push(setState);
169
+ return () => {
170
+ const index = listeners.indexOf(setState);
171
+ if (index > -1) {
172
+ listeners.splice(index, 1);
173
+ }
174
+ };
175
+ }, [state]);
176
+
177
+ return {
178
+ ...state,
179
+ toast,
180
+ dismiss: (toastId?: string) => {
181
+ if (toastId) dispatch({ type: 'DISMISS_TOAST', toastId });
182
+ },
183
+ };
184
+ }
185
+
186
+ export { useToast, toast };
File without changes
@@ -0,0 +1,5 @@
1
+ import { createRoot } from 'react-dom/client';
2
+ import { App } from './App';
3
+ import './index.css';
4
+
5
+ createRoot(document.getElementById('root')!).render(<App />);
@@ -0,0 +1,10 @@
1
+ export const Index = () => {
2
+ return (
3
+ <div>
4
+ <div>
5
+ <h1>Welcome to Your Blank App</h1>
6
+ <p>Start building your amazing project here!</p>
7
+ </div>
8
+ </div>
9
+ );
10
+ };
@@ -0,0 +1,20 @@
1
+ import { useLocation } from 'react-router-dom';
2
+ import { useEffect } from 'react';
3
+
4
+ export const NotFound = () => {
5
+ const location = useLocation();
6
+
7
+ useEffect(() => {
8
+ console.error('404 Error: User attempted to access non-existent route:', location.pathname);
9
+ }, [location.pathname]);
10
+
11
+ return (
12
+ <div>
13
+ <div>
14
+ <h1>404</h1>
15
+ <p>Oops! Page not found</p>
16
+ <a href="/">Return to Home</a>
17
+ </div>
18
+ </div>
19
+ );
20
+ };