metabinaries 1.0.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.
- package/LICENSE +21 -0
- package/README.md +43 -0
- package/index.js +164 -0
- package/package.json +32 -0
- package/src/constants.js +62 -0
- package/src/templates/app.js +527 -0
- package/src/templates/configs.js +303 -0
- package/src/templates/core.js +328 -0
- package/src/templates/folder-structure.js +21 -0
- package/src/templates/layout.js +279 -0
- package/src/templates/misc.js +277 -0
- package/src/templates/packages.js +42 -0
- package/src/templates/ui-2.js +585 -0
- package/src/templates/ui-3.js +606 -0
- package/src/templates/ui-4.js +615 -0
- package/src/templates/ui.js +777 -0
- package/src/utils.js +38 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
// Layout & Shared Components Templates
|
|
2
|
+
export const layoutTemplates = {
|
|
3
|
+
'components/footer.tsx': `export default function Footer() {
|
|
4
|
+
return <footer className="py-8 text-center text-gray-400">Β© 2025 MetaBinaries</footer>;
|
|
5
|
+
}`,
|
|
6
|
+
|
|
7
|
+
'components/layout/LanguageSwitcher.tsx': `'use client';
|
|
8
|
+
|
|
9
|
+
import { Globe } from 'lucide-react';
|
|
10
|
+
import { Button } from '@/components/ui/button';
|
|
11
|
+
import {
|
|
12
|
+
DropdownMenu,
|
|
13
|
+
DropdownMenuContent,
|
|
14
|
+
DropdownMenuItem,
|
|
15
|
+
DropdownMenuTrigger,
|
|
16
|
+
} from '@/components/ui/dropdown-menu';
|
|
17
|
+
import { useParams } from 'next/navigation';
|
|
18
|
+
import { useRouter, usePathname } from '@/i18n/navigation';
|
|
19
|
+
import { cn } from '@/lib/utils';
|
|
20
|
+
|
|
21
|
+
export function LanguageSwitcher() {
|
|
22
|
+
const params = useParams();
|
|
23
|
+
const router = useRouter();
|
|
24
|
+
const pathname = usePathname();
|
|
25
|
+
const locale = params?.locale as string;
|
|
26
|
+
const currentLocale = (locale as 'en' | 'ar') || 'en';
|
|
27
|
+
|
|
28
|
+
const handleLocaleChange = (newLocale: 'en' | 'ar') => {
|
|
29
|
+
if (newLocale === currentLocale) return;
|
|
30
|
+
|
|
31
|
+
// Base path removed
|
|
32
|
+
const BASE_PATH = '';
|
|
33
|
+
|
|
34
|
+
// Get current pathname from browser
|
|
35
|
+
const currentPath = window.location.pathname;
|
|
36
|
+
|
|
37
|
+
// Remove base path if present (e.g., "/workspace/en/admin" -> "/en/admin")
|
|
38
|
+
const pathWithoutBase = currentPath.startsWith(BASE_PATH)
|
|
39
|
+
? currentPath.slice(BASE_PATH.length)
|
|
40
|
+
: currentPath;
|
|
41
|
+
|
|
42
|
+
// Split by current locale to get the path without locale
|
|
43
|
+
// e.g., "/en/admin/users" -> ["", "admin/users"]
|
|
44
|
+
const pathParts = pathWithoutBase.split(\`/\${currentLocale}\`);
|
|
45
|
+
const pathWithoutLocale =
|
|
46
|
+
pathParts.length > 1
|
|
47
|
+
? pathParts[1] || '/'
|
|
48
|
+
: pathWithoutBase.startsWith('/')
|
|
49
|
+
? pathWithoutBase.slice(1)
|
|
50
|
+
: pathWithoutBase;
|
|
51
|
+
|
|
52
|
+
// Ensure the path starts with / and normalize it
|
|
53
|
+
const normalizedPath = pathWithoutLocale.startsWith('/')
|
|
54
|
+
? pathWithoutLocale
|
|
55
|
+
: \`/\${pathWithoutLocale}\`;
|
|
56
|
+
|
|
57
|
+
// Construct the new path with base path and new locale
|
|
58
|
+
const newPath =
|
|
59
|
+
normalizedPath === '/'
|
|
60
|
+
? \`\${BASE_PATH}/\${newLocale}\`
|
|
61
|
+
: \`\${BASE_PATH}/\${newLocale}\${normalizedPath}\`;
|
|
62
|
+
|
|
63
|
+
// Preserve query parameters and hash if any
|
|
64
|
+
const searchParams = window.location.search;
|
|
65
|
+
const hash = window.location.hash;
|
|
66
|
+
const newUrl = \`\${newPath}\${searchParams}\${hash}\`;
|
|
67
|
+
|
|
68
|
+
// Navigate to the new locale path using window.location for reliability
|
|
69
|
+
window.location.href = newUrl;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<DropdownMenu>
|
|
74
|
+
<DropdownMenuTrigger asChild>
|
|
75
|
+
<Button
|
|
76
|
+
variant='ghost'
|
|
77
|
+
size='sm'
|
|
78
|
+
className='text-setup-text hover:text-setup-text hover:bg-setup-primary/20'
|
|
79
|
+
title='Change Language'
|
|
80
|
+
>
|
|
81
|
+
<Globe className='h-4 w-4' />
|
|
82
|
+
<span className='ml-2 text-xs uppercase'>{currentLocale}</span>
|
|
83
|
+
<span className='sr-only'>Change Language</span>
|
|
84
|
+
</Button>
|
|
85
|
+
</DropdownMenuTrigger>
|
|
86
|
+
<DropdownMenuContent
|
|
87
|
+
align='end'
|
|
88
|
+
className='bg-setup-primary border-setup-secondary/30'
|
|
89
|
+
>
|
|
90
|
+
<DropdownMenuItem
|
|
91
|
+
onClick={() => handleLocaleChange('en')}
|
|
92
|
+
className={cn(
|
|
93
|
+
'text-setup-text hover:text-setup-text hover:bg-setup-secondary/20',
|
|
94
|
+
currentLocale === 'en' && 'bg-setup-secondary/10'
|
|
95
|
+
)}
|
|
96
|
+
>
|
|
97
|
+
<span className='mr-2'>π¬π§</span>
|
|
98
|
+
English
|
|
99
|
+
{currentLocale === 'en' && (
|
|
100
|
+
<span className='ml-auto text-setup-secondary'>β</span>
|
|
101
|
+
)}
|
|
102
|
+
</DropdownMenuItem>
|
|
103
|
+
<DropdownMenuItem
|
|
104
|
+
onClick={() => handleLocaleChange('ar')}
|
|
105
|
+
className={cn(
|
|
106
|
+
'text-setup-text hover:text-setup-text hover:bg-setup-secondary/20',
|
|
107
|
+
currentLocale === 'ar' && 'bg-setup-secondary/10'
|
|
108
|
+
)}
|
|
109
|
+
>
|
|
110
|
+
<span className='mr-2'>πΈπ¦</span>
|
|
111
|
+
Ψ§ΩΨΉΨ±Ψ¨ΩΨ©
|
|
112
|
+
{currentLocale === 'ar' && (
|
|
113
|
+
<span className='ml-auto text-setup-secondary'>β</span>
|
|
114
|
+
)}
|
|
115
|
+
</DropdownMenuItem>
|
|
116
|
+
</DropdownMenuContent>
|
|
117
|
+
</DropdownMenu>
|
|
118
|
+
);
|
|
119
|
+
}`,
|
|
120
|
+
|
|
121
|
+
'components/layout/providers.tsx': `'use client';
|
|
122
|
+
|
|
123
|
+
import { ThemeProvider as NextThemeProvider } from 'next-themes';
|
|
124
|
+
import { Provider as ReduxProvider } from 'react-redux';
|
|
125
|
+
import { ReactNode } from 'react';
|
|
126
|
+
import { store } from '@/lib/shared/store';
|
|
127
|
+
import { NotificationProvider } from '@/lib/features/notifications/NotificationContext';
|
|
128
|
+
|
|
129
|
+
interface ProvidersProps {
|
|
130
|
+
children: ReactNode;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function Providers({ children }: ProvidersProps) {
|
|
134
|
+
return (
|
|
135
|
+
<ReduxProvider store={store}>
|
|
136
|
+
<NotificationProvider>
|
|
137
|
+
<NextThemeProvider
|
|
138
|
+
attribute='class'
|
|
139
|
+
defaultTheme='system'
|
|
140
|
+
enableSystem
|
|
141
|
+
disableTransitionOnChange
|
|
142
|
+
themes={['light', 'dark', 'system']}
|
|
143
|
+
>
|
|
144
|
+
{children}
|
|
145
|
+
</NextThemeProvider>
|
|
146
|
+
</NotificationProvider>
|
|
147
|
+
</ReduxProvider>
|
|
148
|
+
);
|
|
149
|
+
}`,
|
|
150
|
+
|
|
151
|
+
'components/shared/ConfirmationDialog.tsx': `'use client';
|
|
152
|
+
|
|
153
|
+
import React from 'react';
|
|
154
|
+
import { Button } from '@/components/ui/button';
|
|
155
|
+
import {
|
|
156
|
+
Dialog,
|
|
157
|
+
DialogContent,
|
|
158
|
+
DialogDescription,
|
|
159
|
+
DialogFooter,
|
|
160
|
+
DialogHeader,
|
|
161
|
+
DialogTitle,
|
|
162
|
+
} from '@/components/ui/dialog';
|
|
163
|
+
import type { VariantProps } from 'class-variance-authority';
|
|
164
|
+
import { buttonVariants } from '@/components/ui/button';
|
|
165
|
+
|
|
166
|
+
interface ConfirmationDialogProps {
|
|
167
|
+
isOpen: boolean;
|
|
168
|
+
onOpenChange: (open: boolean) => void;
|
|
169
|
+
onConfirm: () => void;
|
|
170
|
+
onCancel?: () => void;
|
|
171
|
+
title?: string;
|
|
172
|
+
description?: string;
|
|
173
|
+
confirmLabel?: string;
|
|
174
|
+
cancelLabel?: string;
|
|
175
|
+
confirmVariant?: VariantProps<typeof buttonVariants>['variant'];
|
|
176
|
+
isLoading?: boolean;
|
|
177
|
+
disabled?: boolean;
|
|
178
|
+
children?: React.ReactNode;
|
|
179
|
+
/**
|
|
180
|
+
* When true, shows the footer buttons even when children are provided.
|
|
181
|
+
* When false (default), children completely replace the default content.
|
|
182
|
+
*/
|
|
183
|
+
showFooterWithChildren?: boolean;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function ConfirmationDialog({
|
|
187
|
+
isOpen,
|
|
188
|
+
onOpenChange,
|
|
189
|
+
onConfirm,
|
|
190
|
+
onCancel,
|
|
191
|
+
title = 'Confirm Action',
|
|
192
|
+
description,
|
|
193
|
+
confirmLabel = 'Confirm',
|
|
194
|
+
cancelLabel = 'Cancel',
|
|
195
|
+
confirmVariant = 'destructive',
|
|
196
|
+
isLoading = false,
|
|
197
|
+
disabled = false,
|
|
198
|
+
children,
|
|
199
|
+
showFooterWithChildren = false,
|
|
200
|
+
}: ConfirmationDialogProps) {
|
|
201
|
+
const handleCancel = () => {
|
|
202
|
+
if (onCancel) {
|
|
203
|
+
onCancel();
|
|
204
|
+
} else {
|
|
205
|
+
onOpenChange(false);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const handleConfirm = () => {
|
|
210
|
+
onConfirm();
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const footerButtons = (
|
|
214
|
+
<DialogFooter>
|
|
215
|
+
<Button
|
|
216
|
+
variant='outline'
|
|
217
|
+
onClick={handleCancel}
|
|
218
|
+
disabled={isLoading || disabled}
|
|
219
|
+
>
|
|
220
|
+
{cancelLabel}
|
|
221
|
+
</Button>
|
|
222
|
+
<Button
|
|
223
|
+
variant={confirmVariant}
|
|
224
|
+
onClick={handleConfirm}
|
|
225
|
+
disabled={isLoading || disabled}
|
|
226
|
+
>
|
|
227
|
+
{isLoading ? 'Processing...' : confirmLabel}
|
|
228
|
+
</Button>
|
|
229
|
+
</DialogFooter>
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
return (
|
|
233
|
+
<Dialog open={isOpen} onOpenChange={onOpenChange}>
|
|
234
|
+
<DialogContent>
|
|
235
|
+
{children ? (
|
|
236
|
+
<>
|
|
237
|
+
{children}
|
|
238
|
+
{showFooterWithChildren && footerButtons}
|
|
239
|
+
</>
|
|
240
|
+
) : (
|
|
241
|
+
<>
|
|
242
|
+
<DialogHeader>
|
|
243
|
+
<DialogTitle>{title}</DialogTitle>
|
|
244
|
+
{description && (
|
|
245
|
+
<DialogDescription>{description}</DialogDescription>
|
|
246
|
+
)}
|
|
247
|
+
</DialogHeader>
|
|
248
|
+
{footerButtons}
|
|
249
|
+
</>
|
|
250
|
+
)}
|
|
251
|
+
</DialogContent>
|
|
252
|
+
</Dialog>
|
|
253
|
+
);
|
|
254
|
+
}`,
|
|
255
|
+
|
|
256
|
+
'hooks/use-mobile.tsx': `import * as React from 'react';
|
|
257
|
+
|
|
258
|
+
const MOBILE_BREAKPOINT = 768;
|
|
259
|
+
|
|
260
|
+
export function useIsMobile() {
|
|
261
|
+
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
|
|
262
|
+
undefined
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
React.useEffect(() => {
|
|
266
|
+
const mql = window.matchMedia(\`(max-width: \${MOBILE_BREAKPOINT - 1}px)\`);
|
|
267
|
+
const onChange = () => {
|
|
268
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
269
|
+
};
|
|
270
|
+
mql.addEventListener('change', onChange);
|
|
271
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
272
|
+
return () => mql.removeEventListener('change', onChange);
|
|
273
|
+
}, []);
|
|
274
|
+
|
|
275
|
+
return !!isMobile;
|
|
276
|
+
}`,
|
|
277
|
+
|
|
278
|
+
'hooks/use-mobile.ts': `export function useIsMobile() { return false; }`,
|
|
279
|
+
};
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
export const miscTemplates = {
|
|
2
|
+
'docs/EXPLAIN_METABINARIES_FILE_STRUCTURE.md': `# MetaBinaries: Advanced CRM Architecture Guide
|
|
3
|
+
|
|
4
|
+
This project is built using the **MetaBinaries Feature-Based Internal Architecture**. It is designed for maximum scalability, maintainability, and enterprise-grade performance.
|
|
5
|
+
|
|
6
|
+
## ποΈ Core Philosophy
|
|
7
|
+
|
|
8
|
+
The structure follows a **Feature-Driven Development (FDD)** approach. Instead of grouping files by type (e.g., all slices in one folder), we group them by **Domain/Feature**. This minimizes "folder jumping" and keeps related logic together.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## π Detailed File Structure Explained
|
|
13
|
+
|
|
14
|
+
### π Application Root (\`app/[locale]\`)
|
|
15
|
+
Next.js 15 App Router with dynamic locale segments.
|
|
16
|
+
- **\`layout.tsx\`**: The global entry point. Handles fonts, base providers, and internationalization context.
|
|
17
|
+
- **\`globals.css\`**: Tailwind CSS base styles and design system variables (HSL).
|
|
18
|
+
- **\`(auth)/\`**: Grouped routes for authentication.
|
|
19
|
+
- \`login/\`, \`sign-up/\`, \`forgot-password/\`: Stateless pages for user entry.
|
|
20
|
+
- **\`(workspace)/\`**: The internal CRM area (Protected).
|
|
21
|
+
- \`(home)/\`: Grouped route for the main dashboard.
|
|
22
|
+
- \`page.tsx\`: The main Dashboard page.
|
|
23
|
+
- \`_Components/\`: **Flat structure** for layout-specific components (Sidebar, Header, Search).
|
|
24
|
+
|
|
25
|
+
### π§ͺ Business Logic (\`lib/features/NAME\`)
|
|
26
|
+
This is where the "Brain" of your feature lives. For every new feature (e.g., "leads"), you should create:
|
|
27
|
+
- **\`NAME.api.ts\`**: Defines API endpoints using Axios.
|
|
28
|
+
- **\`NAME.service.ts\`**: Data transformation and business logic (Layer between API and State).
|
|
29
|
+
- **\`NAME.slice.ts\`**: Redux Toolkit slice for state management.
|
|
30
|
+
- **\`NAME.types.ts\`**: TypeScript interfaces/types specific to this feature.
|
|
31
|
+
- **\`NAME.utils.ts\`**: Helper functions used only within this feature.
|
|
32
|
+
- **\`useNAME.ts\`**: A custom React hook for easy access to state and actions from components.
|
|
33
|
+
|
|
34
|
+
### βοΈ Shared Logic (\`lib/shared\`)
|
|
35
|
+
- **\`axios.ts\`**: Pre-configured Axios instance with base URL and interceptors.
|
|
36
|
+
- **\`store.ts\`**: Centralized Redux store configuration.
|
|
37
|
+
- **\`utils/validation/\`**: Zod schemas for forms and API validation.
|
|
38
|
+
- \`common-rules.ts\`: Reusable rules (Email, Password strength).
|
|
39
|
+
- \`NAME.validation.ts\`: Feature-specific form schemas.
|
|
40
|
+
|
|
41
|
+
### π± UI Components (\`components/\`)
|
|
42
|
+
- **\`ui/\`**: Atomic components (Buttons, Inputs, Dialogs) from Shadcn UI. **Do not put business logic here.**
|
|
43
|
+
- **\`shared/\`**: Reusable higher-order components (Table wrappers, Confirmation dialogs).
|
|
44
|
+
- **\`layout/\`**: App-wide structural logic (AuthGuard, LanguageSwitcher, Providers).
|
|
45
|
+
|
|
46
|
+
### π Internationalization (\`i18n/\`)
|
|
47
|
+
- **\`routing.ts\`**: Configuration for supported locales (\`en\`, \`ar\`) and default locale.
|
|
48
|
+
- **\`navigation.ts\`**: Type-safe navigation wrappers (\`Link\`, \`useRouter\`, \`usePathname\`).
|
|
49
|
+
- **\`request.ts\`**: Server-side logic to load the correct JSON messages.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## π οΈ Usage Guides
|
|
54
|
+
|
|
55
|
+
### How to add a new Locale?
|
|
56
|
+
1. Open \`i18n/routing.ts\` and add the locale code to \`locales\`.
|
|
57
|
+
2. Create a new JSON file in \`messages/[code].json\`.
|
|
58
|
+
3. Update the matching in \`middleware.ts\`.
|
|
59
|
+
|
|
60
|
+
### How to create a new Feature?
|
|
61
|
+
1. Create a folder in \`lib/features/[featureName]\`.
|
|
62
|
+
2. Add the 6 core files (api, service, slice, types, utils, hook).
|
|
63
|
+
3. Register the new \`slice\` in \`lib/shared/store.ts\`.
|
|
64
|
+
4. Create the UI for it in \`app/[locale]/(workspace)/[featureName]\`.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## π‘οΈ Security & Quality
|
|
69
|
+
- **Rate Limiting**: Use \`rateLimitMiddleware\` in \`middleware.ts\` for sensitive routes.
|
|
70
|
+
- **Husky**: Automated checks ensure you never commit broken types or bad formatting.
|
|
71
|
+
- **Conventional Commits**: Keeps a clean version history for easier debugging.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
*Created by MetaBinaries CLI - Professional Next.js Scaffolding*`,
|
|
75
|
+
|
|
76
|
+
'README.md': projectName => `# ${projectName}
|
|
77
|
+
|
|
78
|
+
Built with [MetaBinaries](https://metabinaries.com) - The advanced CLI for CRM-style Next.js applications.
|
|
79
|
+
|
|
80
|
+
## π Features
|
|
81
|
+
|
|
82
|
+
- **Next.js 15 (App Router)** - High-performance web architecture.
|
|
83
|
+
- **Internationalization (i18n)** - Full support for English (LTR) and Arabic (RTL) using \`next-intl\`.
|
|
84
|
+
- **Feature-Based Architecture** - Scalable organization under \`lib/features/\`.
|
|
85
|
+
- **State Management** - Centralized store using **Redux Toolkit**.
|
|
86
|
+
- **Modern UI System** - Powered by **Tailwind CSS**, **Shadcn UI**, and **Radix UI**.
|
|
87
|
+
- **Developer Experience**:
|
|
88
|
+
- **Husky Hooks**: Automated pre-commit checks (Type-checking & Formatting).
|
|
89
|
+
- **Conventional Commits**: Standardized commit history.
|
|
90
|
+
- **Turbopack Build**: Optimized development and build speeds.
|
|
91
|
+
- **Security & Utilities**:
|
|
92
|
+
- **Rate Limiting**: Built-in request limiting for protected routes.
|
|
93
|
+
- **Axios Configuration**: Centralized API client with interceptors.
|
|
94
|
+
- **Zod Validation**: Robust schema validation for forms and APIs.
|
|
95
|
+
|
|
96
|
+
## π οΈ Tech Stack
|
|
97
|
+
|
|
98
|
+
- **Framework**: Next.js 15
|
|
99
|
+
- **Styling**: Tailwind CSS
|
|
100
|
+
- **State**: Redux Toolkit
|
|
101
|
+
- **Internationalization**: next-intl
|
|
102
|
+
- **Forms**: React Hook Form + Zod
|
|
103
|
+
- **Icons**: Lucide React
|
|
104
|
+
- **Notifications**: Sonner
|
|
105
|
+
- **Themes**: next-themes
|
|
106
|
+
- **Animations**: Framer Motion
|
|
107
|
+
|
|
108
|
+
## π Project Structure
|
|
109
|
+
|
|
110
|
+
\`\`\`
|
|
111
|
+
ββ app/[locale] # Root of the application (i18n)
|
|
112
|
+
β ββ (auth) # Authentication routes (Login, SignUp)
|
|
113
|
+
β ββ (workspace) # Protected CRM workspace routes
|
|
114
|
+
β ββ (home) # Main dashboard area
|
|
115
|
+
ββ components/
|
|
116
|
+
β ββ ui/ # Shadcn UI base components
|
|
117
|
+
β ββ shared/ # Reusable common components
|
|
118
|
+
β ββ layout/ # AuthGuards, ThemeProviders, etc.
|
|
119
|
+
ββ lib/
|
|
120
|
+
β ββ features/ # Business logic grouped by feature
|
|
121
|
+
β ββ shared/ # Centralized Axios, Redux Store
|
|
122
|
+
β ββ utils/ # Common helper functions
|
|
123
|
+
ββ i18n/ # Translation routing and configurations
|
|
124
|
+
ββ messages/ # JSON translation files (en/ar)
|
|
125
|
+
\`\`\`
|
|
126
|
+
|
|
127
|
+
## π Getting Started
|
|
128
|
+
|
|
129
|
+
### 1. Installation
|
|
130
|
+
\`\`\`bash
|
|
131
|
+
npm install
|
|
132
|
+
\`\`\`
|
|
133
|
+
|
|
134
|
+
### 2. Run Locally
|
|
135
|
+
\`\`\`bash
|
|
136
|
+
npm run dev
|
|
137
|
+
\`\`\`
|
|
138
|
+
|
|
139
|
+
### 3. Build & Production
|
|
140
|
+
\`\`\`bash
|
|
141
|
+
npm run build
|
|
142
|
+
\`\`\`
|
|
143
|
+
|
|
144
|
+
### π Scripts
|
|
145
|
+
|
|
146
|
+
- \`npm run dev\`: Start the development server.
|
|
147
|
+
- \`npm run build\`: Clean build, format check, and Next.js build.
|
|
148
|
+
- \`npm run format\`: Format entire codebase with Prettier.
|
|
149
|
+
- \`npm run type-check\`: Run TypeScript compiler checks.
|
|
150
|
+
|
|
151
|
+
## π€ Contributing
|
|
152
|
+
|
|
153
|
+
This project follows **Conventional Commits**. Please make sure your commit messages match the required format:
|
|
154
|
+
\`type(scope): subject\` (e.g., \`feat(auth): add google sign-in\`).
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
Generated by **MetaBinaries CLI**`,
|
|
158
|
+
|
|
159
|
+
'.husky/pre-commit': `echo "π Running pre-commit checks..."
|
|
160
|
+
echo ""
|
|
161
|
+
|
|
162
|
+
# Step 1: Run type check first (fail fast if there are type errors)
|
|
163
|
+
echo "π Running TypeScript type check..."
|
|
164
|
+
if ! npm run type-check; then
|
|
165
|
+
echo ""
|
|
166
|
+
echo "β Type check failed! Please fix type errors before committing."
|
|
167
|
+
echo ""
|
|
168
|
+
echo "To commit anyway (not recommended), use:"
|
|
169
|
+
echo " git commit --no-verify"
|
|
170
|
+
echo ""
|
|
171
|
+
exit 1
|
|
172
|
+
fi
|
|
173
|
+
echo "β
Type check passed!"
|
|
174
|
+
echo ""
|
|
175
|
+
|
|
176
|
+
# Step 2: Format code after type check passes
|
|
177
|
+
echo "π Formatting code with Prettier..."
|
|
178
|
+
if ! npm run format; then
|
|
179
|
+
echo ""
|
|
180
|
+
echo "β Formatting failed! Please fix formatting issues."
|
|
181
|
+
echo ""
|
|
182
|
+
exit 1
|
|
183
|
+
fi
|
|
184
|
+
echo "β
Code formatted successfully!"
|
|
185
|
+
echo ""
|
|
186
|
+
|
|
187
|
+
echo "β
All pre-commit checks passed! Proceeding with commit..."`,
|
|
188
|
+
|
|
189
|
+
'.husky/commit-msg': `# Validates commit message format
|
|
190
|
+
|
|
191
|
+
# Conventional commit format: type(scope): subject
|
|
192
|
+
# Note: Added optional ! for breaking changes (e.g., feat!: or fix!:)
|
|
193
|
+
commit_regex='^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert|hotfix|release)(!)?(\\(.+\\))?: .{1,}'
|
|
194
|
+
|
|
195
|
+
if ! grep -qE "$commit_regex" "$1"; then
|
|
196
|
+
echo "β Invalid commit message format!"
|
|
197
|
+
echo ""
|
|
198
|
+
echo "Commit message must follow conventional commit format:"
|
|
199
|
+
echo " type(scope): subject"
|
|
200
|
+
echo " or type!: subject (for breaking changes)"
|
|
201
|
+
echo ""
|
|
202
|
+
echo "Types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert, hotfix, release"
|
|
203
|
+
echo ""
|
|
204
|
+
echo "Examples:"
|
|
205
|
+
echo " feat: add user authentication"
|
|
206
|
+
echo " fix(api): resolve login bug"
|
|
207
|
+
echo " feat!: change API response format (breaking change)"
|
|
208
|
+
echo " docs: update README"
|
|
209
|
+
echo ""
|
|
210
|
+
exit 1
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
echo "β
Commit message format is valid!"`,
|
|
214
|
+
|
|
215
|
+
'.husky/pre-push': `echo "\ud83d\ude80 Running pre-push checks..."
|
|
216
|
+
echo ""
|
|
217
|
+
|
|
218
|
+
# Get current branch
|
|
219
|
+
current_branch=$(git rev-parse --abbrev-ref HEAD)
|
|
220
|
+
|
|
221
|
+
# Protect main/master branches from direct pushes
|
|
222
|
+
protected_branches="^(main|master|production)$"
|
|
223
|
+
if echo "$current_branch" | grep -qE "$protected_branches"; then
|
|
224
|
+
echo "β Direct pushes to $current_branch are not allowed!"
|
|
225
|
+
echo ""
|
|
226
|
+
echo "Please create a feature branch and open a pull request:"
|
|
227
|
+
echo " git checkout -b feature/your-feature-name"
|
|
228
|
+
echo " git push origin feature/your-feature-name"
|
|
229
|
+
echo ""
|
|
230
|
+
echo "Or if you really need to push directly, use:"
|
|
231
|
+
echo " git push --no-verify"
|
|
232
|
+
echo ""
|
|
233
|
+
exit 1
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
# Run tests if test script exists
|
|
237
|
+
if npm run --silent 2>&1 | grep -q "\\"test\\""; then
|
|
238
|
+
echo "π§ͺ Running tests..."
|
|
239
|
+
if ! npm test; then
|
|
240
|
+
echo ""
|
|
241
|
+
echo "β Tests failed! Please fix failing tests before pushing."
|
|
242
|
+
echo ""
|
|
243
|
+
echo "To push anyway (not recommended), use:"
|
|
244
|
+
echo " git push --no-verify"
|
|
245
|
+
echo ""
|
|
246
|
+
exit 1
|
|
247
|
+
fi
|
|
248
|
+
echo "β
All tests passed!"
|
|
249
|
+
echo ""
|
|
250
|
+
fi
|
|
251
|
+
|
|
252
|
+
echo "β
All pre-push checks passed! Proceeding with push..."`,
|
|
253
|
+
|
|
254
|
+
'.husky/prepare-commit-msg': `# Add branch name to commit message automatically
|
|
255
|
+
COMMIT_MSG_FILE=$1
|
|
256
|
+
COMMIT_SOURCE=$2
|
|
257
|
+
|
|
258
|
+
# Only add branch name for regular commits (not merge, squash, etc.)
|
|
259
|
+
if [ -z "$COMMIT_SOURCE" ]; then
|
|
260
|
+
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
|
261
|
+
|
|
262
|
+
# Skip if on main/master or if branch name already in message
|
|
263
|
+
if ! echo "$BRANCH_NAME" | grep -qE "^(main|master|HEAD)$"; then
|
|
264
|
+
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
|
|
265
|
+
|
|
266
|
+
# Only add if not already present
|
|
267
|
+
if ! echo "$COMMIT_MSG" | grep -q "$BRANCH_NAME"; then
|
|
268
|
+
# Extract ticket number from branch name (e.g., feature/PROJ-123-description -> PROJ-123)
|
|
269
|
+
TICKET=$(echo "$BRANCH_NAME" | grep -oE "[A-Z]+-[0-9]+" | head -1)
|
|
270
|
+
|
|
271
|
+
if [ -n "$TICKET" ]; then
|
|
272
|
+
echo "$COMMIT_MSG" | sed "1s/$/ [$TICKET]/" > "$COMMIT_MSG_FILE"
|
|
273
|
+
fi
|
|
274
|
+
fi
|
|
275
|
+
fi
|
|
276
|
+
fi`,
|
|
277
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export const dependencies = [
|
|
2
|
+
'@reduxjs/toolkit',
|
|
3
|
+
'react-redux',
|
|
4
|
+
'next-intl',
|
|
5
|
+
'axios',
|
|
6
|
+
'clsx',
|
|
7
|
+
'tailwind-merge',
|
|
8
|
+
'zod',
|
|
9
|
+
'react-hook-form',
|
|
10
|
+
'@hookform/resolvers',
|
|
11
|
+
'date-fns',
|
|
12
|
+
'lucide-react',
|
|
13
|
+
'sonner',
|
|
14
|
+
'next-themes',
|
|
15
|
+
'tailwindcss-animate',
|
|
16
|
+
'class-variance-authority',
|
|
17
|
+
'@radix-ui/react-avatar',
|
|
18
|
+
'@radix-ui/react-collapsible',
|
|
19
|
+
'@radix-ui/react-dialog',
|
|
20
|
+
'@radix-ui/react-dropdown-menu',
|
|
21
|
+
'@radix-ui/react-label',
|
|
22
|
+
'@radix-ui/react-navigation-menu',
|
|
23
|
+
'@radix-ui/react-popover',
|
|
24
|
+
'@radix-ui/react-scroll-area',
|
|
25
|
+
'@radix-ui/react-separator',
|
|
26
|
+
'@radix-ui/react-slot',
|
|
27
|
+
'@radix-ui/react-switch',
|
|
28
|
+
'@radix-ui/react-tooltip',
|
|
29
|
+
'framer-motion',
|
|
30
|
+
'input-otp',
|
|
31
|
+
'cmdk',
|
|
32
|
+
'vaul',
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
export const devDependencies = [
|
|
36
|
+
'@types/node',
|
|
37
|
+
'prettier',
|
|
38
|
+
'prettier-plugin-tailwindcss',
|
|
39
|
+
'husky',
|
|
40
|
+
'lint-staged',
|
|
41
|
+
'rimraf',
|
|
42
|
+
];
|