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 ADDED
@@ -0,0 +1,9 @@
1
+ # Frontend Environment Variables
2
+
3
+ # API Configuration
4
+ NEXT_PUBLIC_API_URL=http://localhost:8000
5
+ API_URL=http://localhost:8000
6
+
7
+ # Authentication
8
+ NEXTAUTH_SECRET=your-secret-key-here
9
+ NEXTAUTH_URL=http://localhost:3000
@@ -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
@@ -0,0 +1,9 @@
1
+ version: '3.8'
2
+ services:
3
+ app:
4
+ build: .
5
+ ports:
6
+ - "8001:3000"
7
+ environment:
8
+ - NODE_ENV=production
9
+ restart: unless-stopped
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,6 @@
1
+ module.exports = {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ };
@@ -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
+ }
@@ -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
+ }