create-react-forge 1.0.1 → 1.0.3

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 (89) hide show
  1. package/README.md +88 -61
  2. package/dist/cli/index.js +1 -1
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/cli/parser.js +1 -1
  5. package/dist/cli/parser.js.map +1 -1
  6. package/dist/docs/architecture-generator.js +1 -1
  7. package/dist/generator/index.js +1 -1
  8. package/dist/generator/index.js.map +1 -1
  9. package/package.json +3 -2
  10. package/src/templates/overlays/base/manifest.json +16 -0
  11. package/src/templates/overlays/base/src/components/ui/Button.tsx +66 -0
  12. package/src/templates/overlays/base/src/components/ui/Input.tsx +51 -0
  13. package/src/templates/overlays/base/src/components/ui/index.ts +3 -0
  14. package/src/templates/overlays/base/src/hooks/use-disclosure.ts +21 -0
  15. package/src/templates/overlays/base/src/hooks/use-local-storage.ts +80 -0
  16. package/src/templates/overlays/base/src/lib/api-client.ts +101 -0
  17. package/src/templates/overlays/base/src/lib/utils.ts +34 -0
  18. package/src/templates/overlays/base/src/types/api.ts +47 -0
  19. package/src/templates/overlays/features/tanstack-query/manifest.json +17 -0
  20. package/src/templates/overlays/features/tanstack-query/src/features/users/api/create-user.ts +41 -0
  21. package/src/templates/overlays/features/tanstack-query/src/features/users/api/get-user.ts +27 -0
  22. package/src/templates/overlays/features/tanstack-query/src/features/users/api/get-users.ts +39 -0
  23. package/src/templates/overlays/features/tanstack-query/src/hooks/use-query-config.ts +42 -0
  24. package/src/templates/overlays/features/tanstack-query/src/lib/QueryProvider.tsx +28 -0
  25. package/src/templates/overlays/features/tanstack-query/src/lib/react-query.ts +46 -0
  26. package/src/templates/overlays/runtime/nextjs/manifest.json +27 -0
  27. package/src/templates/overlays/runtime/nextjs/next-env.d.ts +6 -0
  28. package/src/templates/overlays/runtime/nextjs/next.config.js +12 -0
  29. package/src/templates/overlays/runtime/nextjs/src/app/error.tsx +34 -0
  30. package/src/templates/overlays/runtime/nextjs/src/app/layout.tsx +23 -0
  31. package/src/templates/overlays/runtime/nextjs/src/app/loading.tsx +12 -0
  32. package/src/templates/overlays/runtime/nextjs/src/app/not-found.tsx +26 -0
  33. package/src/templates/overlays/runtime/nextjs/src/app/page.tsx +33 -0
  34. package/src/templates/overlays/runtime/nextjs/src/app/providers.tsx +14 -0
  35. package/src/templates/overlays/runtime/nextjs/src/styles/globals.css +50 -0
  36. package/src/templates/overlays/runtime/nextjs/tsconfig.json +29 -0
  37. package/src/templates/overlays/runtime/vite/index.html +14 -0
  38. package/src/templates/overlays/runtime/vite/manifest.json +28 -0
  39. package/src/templates/overlays/runtime/vite/public/vite.svg +2 -0
  40. package/src/templates/overlays/runtime/vite/src/app/App.tsx +11 -0
  41. package/src/templates/overlays/runtime/vite/src/app/provider.tsx +20 -0
  42. package/src/templates/overlays/runtime/vite/src/app/router.tsx +19 -0
  43. package/src/templates/overlays/runtime/vite/src/components/errors/ErrorFallback.tsx +21 -0
  44. package/src/templates/overlays/runtime/vite/src/components/ui/LoadingSpinner.tsx +23 -0
  45. package/src/templates/overlays/runtime/vite/src/features/misc/routes/Landing.tsx +33 -0
  46. package/src/templates/overlays/runtime/vite/src/features/misc/routes/NotFound.tsx +26 -0
  47. package/src/templates/overlays/runtime/vite/src/main.tsx +11 -0
  48. package/src/templates/overlays/runtime/vite/src/styles/globals.css +55 -0
  49. package/src/templates/overlays/runtime/vite/tsconfig.json +32 -0
  50. package/src/templates/overlays/runtime/vite/tsconfig.node.json +23 -0
  51. package/src/templates/overlays/runtime/vite/vite.config.ts +22 -0
  52. package/src/templates/overlays/state/redux/manifest.json +17 -0
  53. package/src/templates/overlays/state/redux/src/stores/Provider.tsx +17 -0
  54. package/src/templates/overlays/state/redux/src/stores/hooks.ts +11 -0
  55. package/src/templates/overlays/state/redux/src/stores/index.ts +17 -0
  56. package/src/templates/overlays/state/redux/src/stores/slices/auth.ts +54 -0
  57. package/src/templates/overlays/state/redux/src/stores/slices/notifications.ts +58 -0
  58. package/src/templates/overlays/state/redux/src/stores/store.ts +27 -0
  59. package/src/templates/overlays/state/zustand/manifest.json +16 -0
  60. package/src/templates/overlays/state/zustand/src/stores/auth.ts +48 -0
  61. package/src/templates/overlays/state/zustand/src/stores/index.ts +3 -0
  62. package/src/templates/overlays/state/zustand/src/stores/notifications.ts +54 -0
  63. package/src/templates/overlays/styling/css-modules/manifest.json +14 -0
  64. package/src/templates/overlays/styling/css-modules/src/components/ui/Button.module.css +87 -0
  65. package/src/templates/overlays/styling/css-modules/src/styles/globals.css +91 -0
  66. package/src/templates/overlays/styling/tailwind/manifest.json +18 -0
  67. package/src/templates/overlays/styling/tailwind/postcss.config.js +7 -0
  68. package/src/templates/overlays/styling/tailwind/src/styles/globals.css +54 -0
  69. package/src/templates/overlays/styling/tailwind/tailwind.config.js +62 -0
  70. package/src/templates/overlays/testing/jest/jest.config.js +24 -0
  71. package/src/templates/overlays/testing/jest/manifest.json +27 -0
  72. package/src/templates/overlays/testing/jest/src/components/ui/__tests__/Button.test.tsx +33 -0
  73. package/src/templates/overlays/testing/jest/src/testing/mocks/browser.ts +17 -0
  74. package/src/templates/overlays/testing/jest/src/testing/mocks/handlers.ts +42 -0
  75. package/src/templates/overlays/testing/jest/src/testing/mocks/server.ts +8 -0
  76. package/src/templates/overlays/testing/jest/src/testing/setup.ts +20 -0
  77. package/src/templates/overlays/testing/jest/src/testing/test-utils.tsx +39 -0
  78. package/src/templates/overlays/testing/playwright/manifest.json +21 -0
  79. package/src/templates/overlays/testing/playwright/playwright.config.ts +53 -0
  80. package/src/templates/overlays/testing/playwright/tests/e2e/accessibility.spec.ts +41 -0
  81. package/src/templates/overlays/testing/playwright/tests/e2e/home.spec.ts +41 -0
  82. package/src/templates/overlays/testing/vitest/manifest.json +28 -0
  83. package/src/templates/overlays/testing/vitest/src/components/ui/__tests__/Button.test.tsx +34 -0
  84. package/src/templates/overlays/testing/vitest/src/testing/mocks/browser.ts +17 -0
  85. package/src/templates/overlays/testing/vitest/src/testing/mocks/handlers.ts +42 -0
  86. package/src/templates/overlays/testing/vitest/src/testing/mocks/server.ts +8 -0
  87. package/src/templates/overlays/testing/vitest/src/testing/setup.ts +21 -0
  88. package/src/templates/overlays/testing/vitest/src/testing/test-utils.tsx +39 -0
  89. package/src/templates/overlays/testing/vitest/vitest.config.ts +31 -0
@@ -0,0 +1,101 @@
1
+ /**
2
+ * API Client - Centralized HTTP client following bulletproof-react patterns
3
+ * Replace with your actual API configuration
4
+ */
5
+
6
+ export type RequestConfig = {
7
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
8
+ headers?: Record<string, string>;
9
+ body?: unknown;
10
+ params?: Record<string, string | number | boolean>;
11
+ };
12
+
13
+ export type ApiResponse<T> = {
14
+ data: T;
15
+ status: number;
16
+ };
17
+
18
+ const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3001';
19
+
20
+ /**
21
+ * Build URL with query parameters
22
+ */
23
+ function buildUrl(endpoint: string, params?: Record<string, string | number | boolean>): string {
24
+ const url = new URL(endpoint, API_URL);
25
+
26
+ if (params) {
27
+ Object.entries(params).forEach(([key, value]) => {
28
+ url.searchParams.append(key, String(value));
29
+ });
30
+ }
31
+
32
+ return url.toString();
33
+ }
34
+
35
+ /**
36
+ * Get authorization headers (customize based on your auth strategy)
37
+ */
38
+ function getAuthHeaders(): Record<string, string> {
39
+ const token = localStorage.getItem('token');
40
+
41
+ if (token) {
42
+ return { Authorization: `Bearer ${token}` };
43
+ }
44
+
45
+ return {};
46
+ }
47
+
48
+ /**
49
+ * Main API client function
50
+ */
51
+ export async function apiClient<T>(
52
+ endpoint: string,
53
+ config: RequestConfig = {}
54
+ ): Promise<ApiResponse<T>> {
55
+ const { method = 'GET', headers = {}, body, params } = config;
56
+
57
+ const url = buildUrl(endpoint, params);
58
+
59
+ const response = await fetch(url, {
60
+ method,
61
+ headers: {
62
+ 'Content-Type': 'application/json',
63
+ ...getAuthHeaders(),
64
+ ...headers,
65
+ },
66
+ body: body ? JSON.stringify(body) : undefined,
67
+ });
68
+
69
+ if (!response.ok) {
70
+ const error = await response.json().catch(() => ({}));
71
+ throw new Error(error.message || `HTTP error! status: ${response.status}`);
72
+ }
73
+
74
+ const data = await response.json();
75
+
76
+ return {
77
+ data,
78
+ status: response.status,
79
+ };
80
+ }
81
+
82
+ /**
83
+ * Convenience methods
84
+ */
85
+ export const api = {
86
+ get: <T>(endpoint: string, params?: Record<string, string | number | boolean>) =>
87
+ apiClient<T>(endpoint, { method: 'GET', params }),
88
+
89
+ post: <T>(endpoint: string, body: unknown) =>
90
+ apiClient<T>(endpoint, { method: 'POST', body }),
91
+
92
+ put: <T>(endpoint: string, body: unknown) =>
93
+ apiClient<T>(endpoint, { method: 'PUT', body }),
94
+
95
+ patch: <T>(endpoint: string, body: unknown) =>
96
+ apiClient<T>(endpoint, { method: 'PATCH', body }),
97
+
98
+ delete: <T>(endpoint: string) =>
99
+ apiClient<T>(endpoint, { method: 'DELETE' }),
100
+ };
101
+
@@ -0,0 +1,34 @@
1
+ import { clsx, type ClassValue } from 'clsx';
2
+
3
+ /**
4
+ * Utility function for constructing className strings conditionally
5
+ */
6
+ export function cn(...inputs: ClassValue[]) {
7
+ return clsx(inputs);
8
+ }
9
+
10
+ /**
11
+ * Format a date to a human-readable string
12
+ */
13
+ export function formatDate(date: Date | string): string {
14
+ return new Intl.DateTimeFormat('en-US', {
15
+ month: 'long',
16
+ day: 'numeric',
17
+ year: 'numeric',
18
+ }).format(new Date(date));
19
+ }
20
+
21
+ /**
22
+ * Delay execution for a specified number of milliseconds
23
+ */
24
+ export function sleep(ms: number): Promise<void> {
25
+ return new Promise((resolve) => setTimeout(resolve, ms));
26
+ }
27
+
28
+ /**
29
+ * Generate a unique ID
30
+ */
31
+ export function generateId(): string {
32
+ return Math.random().toString(36).substring(2, 9);
33
+ }
34
+
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Common API types following bulletproof-react patterns
3
+ */
4
+
5
+ export type BaseEntity = {
6
+ id: string;
7
+ createdAt: string;
8
+ updatedAt: string;
9
+ };
10
+
11
+ export type PaginatedResponse<T> = {
12
+ data: T[];
13
+ meta: {
14
+ page: number;
15
+ limit: number;
16
+ total: number;
17
+ totalPages: number;
18
+ };
19
+ };
20
+
21
+ export type ApiError = {
22
+ message: string;
23
+ statusCode: number;
24
+ errors?: Record<string, string[]>;
25
+ };
26
+
27
+ export type User = BaseEntity & {
28
+ email: string;
29
+ name: string;
30
+ avatar?: string;
31
+ role: 'admin' | 'user';
32
+ };
33
+
34
+ export type AuthResponse = {
35
+ user: User;
36
+ token: string;
37
+ };
38
+
39
+ export type LoginCredentials = {
40
+ email: string;
41
+ password: string;
42
+ };
43
+
44
+ export type RegisterCredentials = LoginCredentials & {
45
+ name: string;
46
+ };
47
+
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "feature-tanstack-query",
3
+ "version": "1.0.0",
4
+ "description": "TanStack Query (React Query) data fetching setup",
5
+ "compatibleWith": ["runtime-vite", "runtime-nextjs"],
6
+ "dependencies": {
7
+ "@tanstack/react-query": "^5.60.0",
8
+ "@tanstack/react-query-devtools": "^5.60.0"
9
+ },
10
+ "devDependencies": {},
11
+ "scripts": {},
12
+ "filePatterns": {
13
+ "include": ["**/*"],
14
+ "exclude": ["manifest.json"]
15
+ }
16
+ }
17
+
@@ -0,0 +1,41 @@
1
+ import { api } from '@/lib/api-client';
2
+ import type { User } from '@/types/api';
3
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
4
+ import { usersKeys } from './get-users';
5
+
6
+ /**
7
+ * Mutation hook for creating a user
8
+ * Following bulletproof-react patterns with optimistic updates
9
+ */
10
+
11
+ type CreateUserData = {
12
+ name: string;
13
+ email: string;
14
+ };
15
+
16
+ async function createUser(data: CreateUserData) {
17
+ const response = await api.post<User>('/api/users', data);
18
+ return response.data;
19
+ }
20
+
21
+ type UseCreateUserOptions = {
22
+ onSuccess?: (user: User) => void;
23
+ onError?: (error: Error) => void;
24
+ };
25
+
26
+ export function useCreateUser(options: UseCreateUserOptions = {}) {
27
+ const queryClient = useQueryClient();
28
+
29
+ return useMutation({
30
+ mutationFn: createUser,
31
+ onSuccess: (data) => {
32
+ // Invalidate and refetch users list
33
+ queryClient.invalidateQueries({ queryKey: usersKeys.lists() });
34
+ options.onSuccess?.(data);
35
+ },
36
+ onError: (error) => {
37
+ options.onError?.(error as Error);
38
+ },
39
+ });
40
+ }
41
+
@@ -0,0 +1,27 @@
1
+ import { useQuery } from '@tanstack/react-query';
2
+ import { api } from '@/lib/api-client';
3
+ import type { User } from '@/types/api';
4
+ import { usersKeys } from './get-users';
5
+
6
+ /**
7
+ * Query hook for fetching a single user
8
+ */
9
+
10
+ async function getUser(userId: string) {
11
+ const response = await api.get<User>(`/api/users/${userId}`);
12
+ return response.data;
13
+ }
14
+
15
+ type UseUserOptions = {
16
+ userId: string;
17
+ enabled?: boolean;
18
+ };
19
+
20
+ export function useUser({ userId, enabled = true }: UseUserOptions) {
21
+ return useQuery({
22
+ queryKey: usersKeys.detail(userId),
23
+ queryFn: () => getUser(userId),
24
+ enabled,
25
+ });
26
+ }
27
+
@@ -0,0 +1,39 @@
1
+ import { useQuery } from '@tanstack/react-query';
2
+ import { api } from '@/lib/api-client';
3
+ import type { User, PaginatedResponse } from '@/types/api';
4
+
5
+ /**
6
+ * Example query hook following bulletproof-react patterns
7
+ * Each feature has its own API folder with typed query hooks
8
+ */
9
+
10
+ type GetUsersParams = {
11
+ page?: number;
12
+ limit?: number;
13
+ };
14
+
15
+ async function getUsers(params: GetUsersParams = {}) {
16
+ const { page = 1, limit = 10 } = params;
17
+ const response = await api.get<PaginatedResponse<User>>('/api/users', {
18
+ page,
19
+ limit,
20
+ });
21
+ return response.data;
22
+ }
23
+
24
+ export function useUsers(params: GetUsersParams = {}) {
25
+ return useQuery({
26
+ queryKey: ['users', params],
27
+ queryFn: () => getUsers(params),
28
+ });
29
+ }
30
+
31
+ // Query key factory for better organization
32
+ export const usersKeys = {
33
+ all: ['users'] as const,
34
+ lists: () => [...usersKeys.all, 'list'] as const,
35
+ list: (params: GetUsersParams) => [...usersKeys.lists(), params] as const,
36
+ details: () => [...usersKeys.all, 'detail'] as const,
37
+ detail: (id: string) => [...usersKeys.details(), id] as const,
38
+ };
39
+
@@ -0,0 +1,42 @@
1
+ import { useQueryClient } from '@tanstack/react-query';
2
+
3
+ /**
4
+ * Hook to access and manage the query client
5
+ * Following bulletproof-react patterns
6
+ */
7
+ export function useQueryConfig() {
8
+ const queryClient = useQueryClient();
9
+
10
+ const invalidateQueries = (queryKey: string[]) => {
11
+ return queryClient.invalidateQueries({ queryKey });
12
+ };
13
+
14
+ const prefetchQuery = <T>(
15
+ queryKey: string[],
16
+ queryFn: () => Promise<T>
17
+ ) => {
18
+ return queryClient.prefetchQuery({ queryKey, queryFn });
19
+ };
20
+
21
+ const setQueryData = <T>(queryKey: string[], data: T) => {
22
+ return queryClient.setQueryData(queryKey, data);
23
+ };
24
+
25
+ const getQueryData = <T>(queryKey: string[]): T | undefined => {
26
+ return queryClient.getQueryData(queryKey);
27
+ };
28
+
29
+ const removeQueries = (queryKey: string[]) => {
30
+ return queryClient.removeQueries({ queryKey });
31
+ };
32
+
33
+ return {
34
+ queryClient,
35
+ invalidateQueries,
36
+ prefetchQuery,
37
+ setQueryData,
38
+ getQueryData,
39
+ removeQueries,
40
+ };
41
+ }
42
+
@@ -0,0 +1,28 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { QueryClientProvider } from '@tanstack/react-query';
5
+ import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
6
+ import { createQueryClient } from './react-query';
7
+
8
+ type QueryProviderProps = {
9
+ children: React.ReactNode;
10
+ };
11
+
12
+ /**
13
+ * React Query Provider with DevTools
14
+ * Wrap your app with this component to enable data fetching
15
+ */
16
+ export function QueryProvider({ children }: QueryProviderProps) {
17
+ // Create a new QueryClient instance for each request (for SSR)
18
+ // but reuse the same instance on client-side navigation
19
+ const [queryClient] = useState(() => createQueryClient());
20
+
21
+ return (
22
+ <QueryClientProvider client={queryClient}>
23
+ {children}
24
+ <ReactQueryDevtools initialIsOpen={false} />
25
+ </QueryClientProvider>
26
+ );
27
+ }
28
+
@@ -0,0 +1,46 @@
1
+ import { DefaultOptions, QueryClient } from '@tanstack/react-query';
2
+
3
+ /**
4
+ * React Query configuration following bulletproof-react patterns
5
+ */
6
+
7
+ const queryConfig: DefaultOptions = {
8
+ queries: {
9
+ // Stale time: 1 minute
10
+ staleTime: 1000 * 60,
11
+ // Don't refetch on window focus by default
12
+ refetchOnWindowFocus: false,
13
+ // Retry failed requests once
14
+ retry: 1,
15
+ // Retry delay
16
+ retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
17
+ },
18
+ mutations: {
19
+ // Don't retry mutations by default
20
+ retry: false,
21
+ },
22
+ };
23
+
24
+ export function createQueryClient(): QueryClient {
25
+ return new QueryClient({
26
+ defaultOptions: queryConfig,
27
+ });
28
+ }
29
+
30
+ // Singleton query client for client-side usage
31
+ let browserQueryClient: QueryClient | undefined = undefined;
32
+
33
+ export function getQueryClient(): QueryClient {
34
+ if (typeof window === 'undefined') {
35
+ // Server: always create a new query client
36
+ return createQueryClient();
37
+ }
38
+
39
+ // Browser: create once and reuse
40
+ if (!browserQueryClient) {
41
+ browserQueryClient = createQueryClient();
42
+ }
43
+
44
+ return browserQueryClient;
45
+ }
46
+
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "runtime-nextjs",
3
+ "version": "1.0.0",
4
+ "description": "Next.js App Router runtime following bulletproof-react patterns",
5
+ "compatibleWith": ["base"],
6
+ "dependencies": {
7
+ "react": "^18.2.0",
8
+ "react-dom": "^18.2.0",
9
+ "next": "^14.2.0"
10
+ },
11
+ "devDependencies": {
12
+ "@types/react": "^18.2.0",
13
+ "@types/react-dom": "^18.2.0",
14
+ "@types/node": "^20.0.0"
15
+ },
16
+ "scripts": {
17
+ "dev": "next dev",
18
+ "build": "next build",
19
+ "start": "next start",
20
+ "lint": "next lint"
21
+ },
22
+ "filePatterns": {
23
+ "include": ["**/*"],
24
+ "exclude": ["manifest.json"]
25
+ }
26
+ }
27
+
@@ -0,0 +1,6 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+
4
+ // NOTE: This file should not be edited
5
+ // see https://nextjs.org/docs/basic-features/typescript for more information.
6
+
@@ -0,0 +1,12 @@
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ reactStrictMode: true,
4
+ poweredByHeader: false,
5
+ // Enable experimental features if needed
6
+ // experimental: {
7
+ // serverActions: true,
8
+ // },
9
+ };
10
+
11
+ module.exports = nextConfig;
12
+
@@ -0,0 +1,34 @@
1
+ 'use client';
2
+
3
+ import { useEffect } from 'react';
4
+
5
+ export default function Error({
6
+ error,
7
+ reset,
8
+ }: {
9
+ error: Error & { digest?: string };
10
+ reset: () => void;
11
+ }) {
12
+ useEffect(() => {
13
+ // Log the error to an error reporting service
14
+ console.error(error);
15
+ }, [error]);
16
+
17
+ return (
18
+ <div className="flex min-h-screen flex-col items-center justify-center" role="alert">
19
+ <div className="text-center">
20
+ <h1 className="text-2xl font-bold text-red-600">Something went wrong</h1>
21
+ <p className="mt-4 text-gray-600">
22
+ {error.message || 'An unexpected error occurred'}
23
+ </p>
24
+ <button
25
+ onClick={reset}
26
+ className="mt-6 rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500"
27
+ >
28
+ Try again
29
+ </button>
30
+ </div>
31
+ </div>
32
+ );
33
+ }
34
+
@@ -0,0 +1,23 @@
1
+ import '@/styles/globals.css';
2
+ import type { Metadata } from 'next';
3
+ import { Providers } from './providers';
4
+
5
+ export const metadata: Metadata = {
6
+ title: '{{PROJECT_NAME}}',
7
+ description: 'A production-ready Next.js application',
8
+ };
9
+
10
+ export default function RootLayout({
11
+ children,
12
+ }: {
13
+ children: React.ReactNode;
14
+ }) {
15
+ return (
16
+ <html lang="en">
17
+ <body>
18
+ <Providers>{children}</Providers>
19
+ </body>
20
+ </html>
21
+ );
22
+ }
23
+
@@ -0,0 +1,12 @@
1
+ export default function Loading() {
2
+ return (
3
+ <div className="flex min-h-screen items-center justify-center">
4
+ <div
5
+ className="h-8 w-8 animate-spin rounded-full border-2 border-gray-300 border-t-indigo-600"
6
+ role="status"
7
+ aria-label="Loading"
8
+ />
9
+ </div>
10
+ );
11
+ }
12
+
@@ -0,0 +1,26 @@
1
+ import Link from 'next/link';
2
+
3
+ export default function NotFound() {
4
+ return (
5
+ <div className="flex min-h-screen flex-col items-center justify-center">
6
+ <div className="text-center">
7
+ <p className="text-base font-semibold text-indigo-600">404</p>
8
+ <h1 className="mt-4 text-3xl font-bold tracking-tight sm:text-5xl">
9
+ Page not found
10
+ </h1>
11
+ <p className="mt-6 text-base leading-7 text-gray-600">
12
+ Sorry, we couldn't find the page you're looking for.
13
+ </p>
14
+ <div className="mt-10 flex items-center justify-center gap-x-6">
15
+ <Link
16
+ href="/"
17
+ className="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
18
+ >
19
+ Go back home
20
+ </Link>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ );
25
+ }
26
+
@@ -0,0 +1,33 @@
1
+ import Link from 'next/link';
2
+
3
+ export default function HomePage() {
4
+ return (
5
+ <div className="flex min-h-screen flex-col items-center justify-center">
6
+ <div className="text-center">
7
+ <h1 className="text-4xl font-bold tracking-tight sm:text-6xl">
8
+ Welcome to Your App
9
+ </h1>
10
+ <p className="mt-6 text-lg leading-8 text-gray-600">
11
+ A production-ready Next.js application scaffolded with create-react-forge.
12
+ </p>
13
+ <div className="mt-10 flex items-center justify-center gap-x-6">
14
+ <Link
15
+ href="/dashboard"
16
+ className="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
17
+ >
18
+ Get started
19
+ </Link>
20
+ <a
21
+ href="https://github.com/alan2207/bulletproof-react"
22
+ className="text-sm font-semibold leading-6"
23
+ target="_blank"
24
+ rel="noopener noreferrer"
25
+ >
26
+ Learn more <span aria-hidden="true">→</span>
27
+ </a>
28
+ </div>
29
+ </div>
30
+ </div>
31
+ );
32
+ }
33
+
@@ -0,0 +1,14 @@
1
+ 'use client';
2
+
3
+ import { ReactNode } from 'react';
4
+
5
+ type ProvidersProps = {
6
+ children: ReactNode;
7
+ };
8
+
9
+ export function Providers({ children }: ProvidersProps) {
10
+ // Add providers here (React Query, Theme, etc.)
11
+ // They will be added by feature overlays
12
+ return <>{children}</>;
13
+ }
14
+
@@ -0,0 +1,50 @@
1
+ /* Base styles - will be enhanced by styling overlay (tailwind/css-modules) */
2
+
3
+ *,
4
+ *::before,
5
+ *::after {
6
+ box-sizing: border-box;
7
+ }
8
+
9
+ * {
10
+ margin: 0;
11
+ }
12
+
13
+ html,
14
+ body {
15
+ height: 100%;
16
+ }
17
+
18
+ body {
19
+ line-height: 1.5;
20
+ -webkit-font-smoothing: antialiased;
21
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
22
+ 'Helvetica Neue', Arial, sans-serif;
23
+ }
24
+
25
+ img,
26
+ picture,
27
+ video,
28
+ canvas,
29
+ svg {
30
+ display: block;
31
+ max-width: 100%;
32
+ }
33
+
34
+ input,
35
+ button,
36
+ textarea,
37
+ select {
38
+ font: inherit;
39
+ }
40
+
41
+ p,
42
+ h1,
43
+ h2,
44
+ h3,
45
+ h4,
46
+ h5,
47
+ h6 {
48
+ overflow-wrap: break-word;
49
+ }
50
+