frontend-systems 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/.env.example +9 -0
- package/.github/workflows/deploy.yml +15 -0
- package/Dockerfile +13 -0
- package/README.md +113 -0
- package/docker-compose.yml +9 -0
- package/next.config.js +52 -0
- package/package.json +38 -0
- package/postcss.config.js +6 -0
- package/src/app/dashboard/layout.tsx +23 -0
- package/src/app/dashboard/page.tsx +97 -0
- package/src/app/globals.css +149 -0
- package/src/app/layout.tsx +40 -0
- package/src/app/page.tsx +107 -0
- package/src/components/layouts/Header.tsx +46 -0
- package/src/components/layouts/Sidebar.tsx +81 -0
- package/src/components/ui/StatsCard.tsx +43 -0
- package/src/lib/api.ts +249 -0
- package/src/lib/utils.ts +81 -0
- package/src/types/index.ts +105 -0
- package/tailwind.config.ts +90 -0
- package/tsconfig.json +41 -0
package/.env.example
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
name: Deploy to Render (Free Tier)
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [ main, master ]
|
|
5
|
+
jobs:
|
|
6
|
+
deploy:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
steps:
|
|
9
|
+
- uses: actions/checkout@v3
|
|
10
|
+
- name: Trigger Render Deploy
|
|
11
|
+
uses: sws2apps/render-deployment@main
|
|
12
|
+
with:
|
|
13
|
+
serviceId: ${{ secrets.RENDER_SERVICE_ID }}
|
|
14
|
+
apiKey: ${{ secrets.RENDER_API_KEY }}
|
|
15
|
+
multipleDeployment: false
|
package/Dockerfile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
FROM node:18-alpine AS builder
|
|
2
|
+
WORKDIR /app
|
|
3
|
+
COPY package*.json ./
|
|
4
|
+
RUN npm ci || npm install
|
|
5
|
+
COPY . .
|
|
6
|
+
RUN npm run build --if-present
|
|
7
|
+
|
|
8
|
+
FROM node:18-alpine
|
|
9
|
+
WORKDIR /app
|
|
10
|
+
COPY --from=builder /app ./
|
|
11
|
+
ENV NODE_ENV=production
|
|
12
|
+
EXPOSE 3000
|
|
13
|
+
CMD ["npm", "start"]
|
package/README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# 02_frontend_systems - Next.js Admin Dashboard
|
|
2
|
+
|
|
3
|
+
> Production-grade admin dashboard demonstrating modern React patterns, TypeScript, and enterprise UI architecture.
|
|
4
|
+
|
|
5
|
+
## 🎯 Overview
|
|
6
|
+
|
|
7
|
+
This module implements a comprehensive admin dashboard with:
|
|
8
|
+
|
|
9
|
+
- **Next.js 14** - App Router with Server Components
|
|
10
|
+
- **TypeScript** - Full type safety
|
|
11
|
+
- **TailwindCSS** - Utility-first styling
|
|
12
|
+
- **Authentication** - JWT-based auth flow
|
|
13
|
+
- **RBAC UI** - Role-based access control
|
|
14
|
+
- **CRUD Operations** - Full data management
|
|
15
|
+
|
|
16
|
+
## 📁 Structure
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
02_frontend_systems/
|
|
20
|
+
├── src/
|
|
21
|
+
│ ├── app/ # Next.js App Router
|
|
22
|
+
│ │ ├── layout.tsx # Root layout
|
|
23
|
+
│ │ ├── page.tsx # Home page
|
|
24
|
+
│ │ ├── dashboard/ # Dashboard pages
|
|
25
|
+
│ │ ├── users/ # User management
|
|
26
|
+
│ │ └── api/ # API routes
|
|
27
|
+
│ ├── components/ # React components
|
|
28
|
+
│ │ ├── ui/ # Base UI components
|
|
29
|
+
│ │ └── layouts/ # Layout components
|
|
30
|
+
│ ├── lib/ # Utilities
|
|
31
|
+
│ ├── hooks/ # Custom hooks
|
|
32
|
+
│ └── types/ # TypeScript types
|
|
33
|
+
├── public/ # Static assets
|
|
34
|
+
└── tests/ # Component tests
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 🚀 Quick Start
|
|
38
|
+
|
|
39
|
+
### Prerequisites
|
|
40
|
+
|
|
41
|
+
- Node.js 18+
|
|
42
|
+
- npm or yarn
|
|
43
|
+
|
|
44
|
+
### Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Install dependencies
|
|
48
|
+
npm install
|
|
49
|
+
|
|
50
|
+
# Set environment variables
|
|
51
|
+
cp .env.example .env.local
|
|
52
|
+
# Edit .env.local with your API URL
|
|
53
|
+
|
|
54
|
+
# Run development server
|
|
55
|
+
npm run dev
|
|
56
|
+
|
|
57
|
+
# Open http://localhost:3000
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Production Build
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npm run build
|
|
64
|
+
npm start
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## 🏗️ Architecture
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
71
|
+
│ PAGES (App Router) │
|
|
72
|
+
│ (Server Components, Route Handlers, Layouts) │
|
|
73
|
+
└─────────────────────────────────────────────────────────────┘
|
|
74
|
+
│
|
|
75
|
+
▼
|
|
76
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
77
|
+
│ CLIENT COMPONENTS │
|
|
78
|
+
│ (Interactive UI, Forms, State) │
|
|
79
|
+
└─────────────────────────────────────────────────────────────┘
|
|
80
|
+
│
|
|
81
|
+
▼
|
|
82
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
83
|
+
│ HOOKS & CONTEXT │
|
|
84
|
+
│ (useAuth, useApi, AppContext) │
|
|
85
|
+
└─────────────────────────────────────────────────────────────┘
|
|
86
|
+
│
|
|
87
|
+
▼
|
|
88
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
89
|
+
│ API CLIENT │
|
|
90
|
+
│ (Fetch wrapper, Type-safe calls) │
|
|
91
|
+
└─────────────────────────────────────────────────────────────┘
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## 🎨 Design System
|
|
95
|
+
|
|
96
|
+
- **Colors**: Dark mode with accent colors
|
|
97
|
+
- **Typography**: Inter font family
|
|
98
|
+
- **Spacing**: 4px grid system
|
|
99
|
+
- **Components**: Consistent, reusable UI elements
|
|
100
|
+
|
|
101
|
+
## 🧪 Testing
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Run tests
|
|
105
|
+
npm test
|
|
106
|
+
|
|
107
|
+
# Run with coverage
|
|
108
|
+
npm test -- --coverage
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## 📄 License
|
|
112
|
+
|
|
113
|
+
MIT
|
package/next.config.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** @type {import('next').NextConfig} */
|
|
2
|
+
const nextConfig = {
|
|
3
|
+
reactStrictMode: true,
|
|
4
|
+
|
|
5
|
+
// Environment variables available at build time
|
|
6
|
+
env: {
|
|
7
|
+
API_URL: process.env.API_URL || 'http://localhost:8000',
|
|
8
|
+
},
|
|
9
|
+
|
|
10
|
+
// Image optimization
|
|
11
|
+
images: {
|
|
12
|
+
domains: ['localhost'],
|
|
13
|
+
remotePatterns: [
|
|
14
|
+
{
|
|
15
|
+
protocol: 'https',
|
|
16
|
+
hostname: '**',
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
// Experimental features
|
|
22
|
+
experimental: {
|
|
23
|
+
serverActions: {
|
|
24
|
+
bodySizeLimit: '2mb',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
// Headers for security
|
|
29
|
+
async headers() {
|
|
30
|
+
return [
|
|
31
|
+
{
|
|
32
|
+
source: '/:path*',
|
|
33
|
+
headers: [
|
|
34
|
+
{
|
|
35
|
+
key: 'X-Frame-Options',
|
|
36
|
+
value: 'DENY',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
key: 'X-Content-Type-Options',
|
|
40
|
+
value: 'nosniff',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
key: 'Referrer-Policy',
|
|
44
|
+
value: 'origin-when-cross-origin',
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
module.exports = nextConfig;
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "frontend-systems",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"start": "next start",
|
|
9
|
+
"lint": "next lint",
|
|
10
|
+
"test": "jest",
|
|
11
|
+
"test:watch": "jest --watch"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"next": "^14.1.0",
|
|
15
|
+
"react": "^18.2.0",
|
|
16
|
+
"react-dom": "^18.2.0",
|
|
17
|
+
"clsx": "^2.1.0",
|
|
18
|
+
"tailwind-merge": "^2.2.0",
|
|
19
|
+
"lucide-react": "^0.312.0",
|
|
20
|
+
"zustand": "^4.5.0",
|
|
21
|
+
"date-fns": "^3.3.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^20.11.5",
|
|
25
|
+
"@types/react": "^18.2.48",
|
|
26
|
+
"@types/react-dom": "^18.2.18",
|
|
27
|
+
"typescript": "^5.3.3",
|
|
28
|
+
"tailwindcss": "^3.4.1",
|
|
29
|
+
"postcss": "^8.4.33",
|
|
30
|
+
"autoprefixer": "^10.4.17",
|
|
31
|
+
"eslint": "^8.56.0",
|
|
32
|
+
"eslint-config-next": "^14.1.0",
|
|
33
|
+
"@testing-library/react": "^14.1.2",
|
|
34
|
+
"@testing-library/jest-dom": "^6.3.0",
|
|
35
|
+
"jest": "^29.7.0",
|
|
36
|
+
"jest-environment-jsdom": "^29.7.0"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Sidebar } from '@/components/layouts/Sidebar';
|
|
2
|
+
import { Header } from '@/components/layouts/Header';
|
|
3
|
+
|
|
4
|
+
export default function DashboardLayout({
|
|
5
|
+
children,
|
|
6
|
+
}: {
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
}) {
|
|
9
|
+
return (
|
|
10
|
+
<div className="flex h-screen bg-dark-950">
|
|
11
|
+
{/* Sidebar */}
|
|
12
|
+
<Sidebar />
|
|
13
|
+
|
|
14
|
+
{/* Main Content */}
|
|
15
|
+
<div className="flex flex-1 flex-col overflow-hidden">
|
|
16
|
+
<Header />
|
|
17
|
+
<main className="flex-1 overflow-y-auto bg-dark-900 p-6">
|
|
18
|
+
{children}
|
|
19
|
+
</main>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { StatsCard } from '@/components/ui/StatsCard';
|
|
2
|
+
import {
|
|
3
|
+
DollarSign,
|
|
4
|
+
ShoppingCart,
|
|
5
|
+
Package,
|
|
6
|
+
Users,
|
|
7
|
+
TrendingUp,
|
|
8
|
+
TrendingDown,
|
|
9
|
+
} from 'lucide-react';
|
|
10
|
+
|
|
11
|
+
export default function DashboardPage() {
|
|
12
|
+
return (
|
|
13
|
+
<div className="space-y-6">
|
|
14
|
+
{/* Page Header */}
|
|
15
|
+
<div>
|
|
16
|
+
<h1 className="text-2xl font-bold text-white">Dashboard</h1>
|
|
17
|
+
<p className="mt-1 text-sm text-dark-400">
|
|
18
|
+
Overview of your business metrics and performance.
|
|
19
|
+
</p>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
{/* Stats Grid */}
|
|
23
|
+
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4">
|
|
24
|
+
<StatsCard
|
|
25
|
+
title="Total Revenue"
|
|
26
|
+
value="$45,231.89"
|
|
27
|
+
change={{ value: 12.5, trend: 'up' }}
|
|
28
|
+
icon={DollarSign}
|
|
29
|
+
/>
|
|
30
|
+
<StatsCard
|
|
31
|
+
title="Orders"
|
|
32
|
+
value="356"
|
|
33
|
+
change={{ value: 8.2, trend: 'up' }}
|
|
34
|
+
icon={ShoppingCart}
|
|
35
|
+
/>
|
|
36
|
+
<StatsCard
|
|
37
|
+
title="Products"
|
|
38
|
+
value="1,234"
|
|
39
|
+
change={{ value: 3.1, trend: 'down' }}
|
|
40
|
+
icon={Package}
|
|
41
|
+
/>
|
|
42
|
+
<StatsCard
|
|
43
|
+
title="Active Users"
|
|
44
|
+
value="573"
|
|
45
|
+
change={{ value: 18.7, trend: 'up' }}
|
|
46
|
+
icon={Users}
|
|
47
|
+
/>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
{/* Charts Section */}
|
|
51
|
+
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
|
52
|
+
{/* Revenue Chart Placeholder */}
|
|
53
|
+
<div className="card border-dark-700 p-6">
|
|
54
|
+
<h3 className="text-lg font-semibold text-white">Revenue Overview</h3>
|
|
55
|
+
<p className="text-sm text-dark-400">Monthly revenue for the current year</p>
|
|
56
|
+
<div className="mt-4 flex h-64 items-center justify-center rounded-lg bg-dark-800">
|
|
57
|
+
<span className="text-dark-500">Chart Component</span>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
{/* Orders Chart Placeholder */}
|
|
62
|
+
<div className="card border-dark-700 p-6">
|
|
63
|
+
<h3 className="text-lg font-semibold text-white">Order Statistics</h3>
|
|
64
|
+
<p className="text-sm text-dark-400">Order volume by status</p>
|
|
65
|
+
<div className="mt-4 flex h-64 items-center justify-center rounded-lg bg-dark-800">
|
|
66
|
+
<span className="text-dark-500">Chart Component</span>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
{/* Recent Activity */}
|
|
72
|
+
<div className="card border-dark-700 p-6">
|
|
73
|
+
<h3 className="text-lg font-semibold text-white">Recent Activity</h3>
|
|
74
|
+
<p className="text-sm text-dark-400">Latest actions in the system</p>
|
|
75
|
+
<div className="mt-4 space-y-4">
|
|
76
|
+
{[
|
|
77
|
+
{ action: 'New order #ORD-001', user: 'John Doe', time: '2 min ago' },
|
|
78
|
+
{ action: 'Product updated: Widget Pro', user: 'Jane Smith', time: '15 min ago' },
|
|
79
|
+
{ action: 'User registered: mike@example.com', user: 'System', time: '1 hour ago' },
|
|
80
|
+
{ action: 'Inventory low: Basic Gadget', user: 'System', time: '2 hours ago' },
|
|
81
|
+
].map((item, i) => (
|
|
82
|
+
<div
|
|
83
|
+
key={i}
|
|
84
|
+
className="flex items-center justify-between border-b border-dark-700 pb-4 last:border-0 last:pb-0"
|
|
85
|
+
>
|
|
86
|
+
<div>
|
|
87
|
+
<p className="font-medium text-white">{item.action}</p>
|
|
88
|
+
<p className="text-sm text-dark-400">By {item.user}</p>
|
|
89
|
+
</div>
|
|
90
|
+
<span className="text-sm text-dark-500">{item.time}</span>
|
|
91
|
+
</div>
|
|
92
|
+
))}
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
@tailwind base;
|
|
2
|
+
@tailwind components;
|
|
3
|
+
@tailwind utilities;
|
|
4
|
+
|
|
5
|
+
/* Custom base styles */
|
|
6
|
+
@layer base {
|
|
7
|
+
:root {
|
|
8
|
+
--background: 0 0% 100%;
|
|
9
|
+
--foreground: 222.2 84% 4.9%;
|
|
10
|
+
--muted: 210 40% 96.1%;
|
|
11
|
+
--muted-foreground: 215.4 16.3% 46.9%;
|
|
12
|
+
--border: 214.3 31.8% 91.4%;
|
|
13
|
+
--input: 214.3 31.8% 91.4%;
|
|
14
|
+
--ring: 221.2 83.2% 53.3%;
|
|
15
|
+
--radius: 0.5rem;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.dark {
|
|
19
|
+
--background: 222.2 84% 4.9%;
|
|
20
|
+
--foreground: 210 40% 98%;
|
|
21
|
+
--muted: 217.2 32.6% 17.5%;
|
|
22
|
+
--muted-foreground: 215 20.2% 65.1%;
|
|
23
|
+
--border: 217.2 32.6% 17.5%;
|
|
24
|
+
--input: 217.2 32.6% 17.5%;
|
|
25
|
+
--ring: 224.3 76.3% 48%;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
* {
|
|
29
|
+
@apply border-border;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
body {
|
|
33
|
+
@apply bg-background text-foreground;
|
|
34
|
+
font-feature-settings: "rlig" 1, "calt" 1;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* Custom scrollbar */
|
|
38
|
+
::-webkit-scrollbar {
|
|
39
|
+
width: 8px;
|
|
40
|
+
height: 8px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
::-webkit-scrollbar-track {
|
|
44
|
+
@apply bg-dark-100 dark:bg-dark-800;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
::-webkit-scrollbar-thumb {
|
|
48
|
+
@apply bg-dark-300 dark:bg-dark-600 rounded-full;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
::-webkit-scrollbar-thumb:hover {
|
|
52
|
+
@apply bg-dark-400 dark:bg-dark-500;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* Custom component styles */
|
|
57
|
+
@layer components {
|
|
58
|
+
.btn {
|
|
59
|
+
@apply inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors
|
|
60
|
+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring
|
|
61
|
+
disabled:pointer-events-none disabled:opacity-50;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.btn-primary {
|
|
65
|
+
@apply btn bg-primary-600 text-white hover:bg-primary-700 active:bg-primary-800;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.btn-secondary {
|
|
69
|
+
@apply btn bg-dark-200 text-dark-900 hover:bg-dark-300
|
|
70
|
+
dark:bg-dark-700 dark:text-dark-100 dark:hover:bg-dark-600;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.btn-danger {
|
|
74
|
+
@apply btn bg-error-600 text-white hover:bg-error-500;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.btn-ghost {
|
|
78
|
+
@apply btn bg-transparent hover:bg-dark-100 dark:hover:bg-dark-800;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.btn-sm {
|
|
82
|
+
@apply h-8 px-3 text-xs;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.btn-md {
|
|
86
|
+
@apply h-10 px-4;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.btn-lg {
|
|
90
|
+
@apply h-12 px-6 text-base;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.input {
|
|
94
|
+
@apply flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm
|
|
95
|
+
placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring
|
|
96
|
+
disabled:cursor-not-allowed disabled:opacity-50;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.card {
|
|
100
|
+
@apply rounded-lg border bg-white shadow-sm dark:bg-dark-900 dark:border-dark-700;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.card-header {
|
|
104
|
+
@apply flex flex-col space-y-1.5 p-6;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.card-title {
|
|
108
|
+
@apply text-lg font-semibold leading-none tracking-tight;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.card-content {
|
|
112
|
+
@apply p-6 pt-0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.badge {
|
|
116
|
+
@apply inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.badge-primary {
|
|
120
|
+
@apply badge bg-primary-100 text-primary-800 dark:bg-primary-900 dark:text-primary-200;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.badge-success {
|
|
124
|
+
@apply badge bg-success-50 text-success-600;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.badge-warning {
|
|
128
|
+
@apply badge bg-warning-50 text-warning-600;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.badge-error {
|
|
132
|
+
@apply badge bg-error-50 text-error-600;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* Utility classes */
|
|
137
|
+
@layer utilities {
|
|
138
|
+
.text-balance {
|
|
139
|
+
text-wrap: balance;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.glass {
|
|
143
|
+
@apply backdrop-blur-sm bg-white/50 dark:bg-dark-900/50;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.gradient-primary {
|
|
147
|
+
@apply bg-gradient-to-r from-primary-500 to-primary-700;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Metadata } from 'next';
|
|
2
|
+
import { Inter } from 'next/font/google';
|
|
3
|
+
import './globals.css';
|
|
4
|
+
|
|
5
|
+
const inter = Inter({
|
|
6
|
+
subsets: ['latin'],
|
|
7
|
+
display: 'swap',
|
|
8
|
+
variable: '--font-inter',
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export const metadata: Metadata = {
|
|
12
|
+
title: {
|
|
13
|
+
default: 'ERP Dashboard',
|
|
14
|
+
template: '%s | ERP Dashboard',
|
|
15
|
+
},
|
|
16
|
+
description: 'Enterprise Resource Planning Dashboard',
|
|
17
|
+
keywords: ['ERP', 'Dashboard', 'Enterprise', 'Management'],
|
|
18
|
+
authors: [{ name: 'Engineering Team' }],
|
|
19
|
+
viewport: 'width=device-width, initial-scale=1',
|
|
20
|
+
themeColor: [
|
|
21
|
+
{ media: '(prefers-color-scheme: light)', color: '#ffffff' },
|
|
22
|
+
{ media: '(prefers-color-scheme: dark)', color: '#111827' },
|
|
23
|
+
],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default function RootLayout({
|
|
27
|
+
children,
|
|
28
|
+
}: {
|
|
29
|
+
children: React.ReactNode;
|
|
30
|
+
}) {
|
|
31
|
+
return (
|
|
32
|
+
<html lang="en" className={inter.variable} suppressHydrationWarning>
|
|
33
|
+
<body className="min-h-screen bg-dark-50 font-sans antialiased dark:bg-dark-950">
|
|
34
|
+
<div className="relative flex min-h-screen flex-col">
|
|
35
|
+
{children}
|
|
36
|
+
</div>
|
|
37
|
+
</body>
|
|
38
|
+
</html>
|
|
39
|
+
);
|
|
40
|
+
}
|
package/src/app/page.tsx
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import Link from 'next/link';
|
|
2
|
+
import {
|
|
3
|
+
LayoutDashboard,
|
|
4
|
+
Users,
|
|
5
|
+
ShoppingCart,
|
|
6
|
+
Package,
|
|
7
|
+
Settings
|
|
8
|
+
} from 'lucide-react';
|
|
9
|
+
|
|
10
|
+
export default function HomePage() {
|
|
11
|
+
return (
|
|
12
|
+
<div className="min-h-screen bg-gradient-to-br from-dark-900 via-dark-950 to-primary-950">
|
|
13
|
+
{/* Navigation */}
|
|
14
|
+
<nav className="border-b border-dark-800 bg-dark-900/50 backdrop-blur-sm">
|
|
15
|
+
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
|
16
|
+
<div className="flex h-16 items-center justify-between">
|
|
17
|
+
<div className="flex items-center">
|
|
18
|
+
<span className="text-2xl font-bold text-white">
|
|
19
|
+
ERP<span className="text-primary-500">Dashboard</span>
|
|
20
|
+
</span>
|
|
21
|
+
</div>
|
|
22
|
+
<div className="flex items-center space-x-4">
|
|
23
|
+
<Link
|
|
24
|
+
href="/dashboard"
|
|
25
|
+
className="btn-primary btn-md"
|
|
26
|
+
>
|
|
27
|
+
Go to Dashboard
|
|
28
|
+
</Link>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</nav>
|
|
33
|
+
|
|
34
|
+
{/* Hero Section */}
|
|
35
|
+
<main className="mx-auto max-w-7xl px-4 py-24 sm:px-6 lg:px-8">
|
|
36
|
+
<div className="text-center">
|
|
37
|
+
<h1 className="text-4xl font-bold tracking-tight text-white sm:text-6xl">
|
|
38
|
+
Enterprise Resource
|
|
39
|
+
<span className="block text-primary-400">Planning System</span>
|
|
40
|
+
</h1>
|
|
41
|
+
<p className="mt-6 text-lg leading-8 text-dark-300">
|
|
42
|
+
A production-grade admin dashboard demonstrating modern React patterns,
|
|
43
|
+
TypeScript, and enterprise UI architecture.
|
|
44
|
+
</p>
|
|
45
|
+
<div className="mt-10 flex items-center justify-center gap-x-6">
|
|
46
|
+
<Link
|
|
47
|
+
href="/dashboard"
|
|
48
|
+
className="btn-primary btn-lg"
|
|
49
|
+
>
|
|
50
|
+
Get Started
|
|
51
|
+
</Link>
|
|
52
|
+
<Link
|
|
53
|
+
href="/dashboard/users"
|
|
54
|
+
className="btn-ghost btn-lg text-dark-300 hover:text-white"
|
|
55
|
+
>
|
|
56
|
+
View Users →
|
|
57
|
+
</Link>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
{/* Features Grid */}
|
|
62
|
+
<div className="mt-24 grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4">
|
|
63
|
+
<FeatureCard
|
|
64
|
+
icon={LayoutDashboard}
|
|
65
|
+
title="Dashboard"
|
|
66
|
+
description="Real-time metrics and analytics"
|
|
67
|
+
/>
|
|
68
|
+
<FeatureCard
|
|
69
|
+
icon={Users}
|
|
70
|
+
title="User Management"
|
|
71
|
+
description="RBAC and team organization"
|
|
72
|
+
/>
|
|
73
|
+
<FeatureCard
|
|
74
|
+
icon={ShoppingCart}
|
|
75
|
+
title="Orders"
|
|
76
|
+
description="Full order lifecycle management"
|
|
77
|
+
/>
|
|
78
|
+
<FeatureCard
|
|
79
|
+
icon={Package}
|
|
80
|
+
title="Inventory"
|
|
81
|
+
description="Stock tracking and alerts"
|
|
82
|
+
/>
|
|
83
|
+
</div>
|
|
84
|
+
</main>
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function FeatureCard({
|
|
90
|
+
icon: Icon,
|
|
91
|
+
title,
|
|
92
|
+
description,
|
|
93
|
+
}: {
|
|
94
|
+
icon: React.ComponentType<{ className?: string }>;
|
|
95
|
+
title: string;
|
|
96
|
+
description: string;
|
|
97
|
+
}) {
|
|
98
|
+
return (
|
|
99
|
+
<div className="card border-dark-800 bg-dark-900/50 p-6 transition-all hover:border-primary-500/50">
|
|
100
|
+
<div className="mb-4 inline-flex rounded-lg bg-primary-500/10 p-3">
|
|
101
|
+
<Icon className="h-6 w-6 text-primary-400" />
|
|
102
|
+
</div>
|
|
103
|
+
<h3 className="text-lg font-semibold text-white">{title}</h3>
|
|
104
|
+
<p className="mt-2 text-sm text-dark-400">{description}</p>
|
|
105
|
+
</div>
|
|
106
|
+
);
|
|
107
|
+
}
|