lean-spec 0.2.5-dev.20251124045153 → 0.2.5-dev.20251124054130
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/dist/{chunk-IGNO4GX2.js → chunk-6FKLWECL.js} +760 -550
- package/dist/chunk-6FKLWECL.js.map +1 -0
- package/dist/cli.js +6 -1
- package/dist/cli.js.map +1 -1
- package/dist/mcp-server.js +1 -1
- package/package.json +1 -1
- package/templates/examples/api-refactor/README.md +81 -0
- package/templates/examples/api-refactor/package.json +16 -0
- package/templates/examples/api-refactor/src/app.js +40 -0
- package/templates/examples/api-refactor/src/services/currencyService.js +43 -0
- package/templates/examples/api-refactor/src/services/timezoneService.js +41 -0
- package/templates/examples/api-refactor/src/services/weatherService.js +42 -0
- package/templates/examples/dark-theme/README.md +55 -0
- package/templates/examples/dark-theme/package.json +16 -0
- package/templates/examples/dark-theme/src/public/app.js +92 -0
- package/templates/examples/dark-theme/src/public/index.html +38 -0
- package/templates/examples/dark-theme/src/public/style.css +163 -0
- package/templates/examples/dark-theme/src/server.js +17 -0
- package/templates/examples/dashboard-widgets/README.md +70 -0
- package/templates/examples/dashboard-widgets/index.html +12 -0
- package/templates/examples/dashboard-widgets/package.json +22 -0
- package/templates/examples/dashboard-widgets/src/App.css +20 -0
- package/templates/examples/dashboard-widgets/src/App.jsx +16 -0
- package/templates/examples/dashboard-widgets/src/components/Dashboard.css +17 -0
- package/templates/examples/dashboard-widgets/src/components/Dashboard.jsx +15 -0
- package/templates/examples/dashboard-widgets/src/components/WidgetWrapper.css +23 -0
- package/templates/examples/dashboard-widgets/src/components/WidgetWrapper.jsx +16 -0
- package/templates/examples/dashboard-widgets/src/components/widgets/ChartWidget.css +33 -0
- package/templates/examples/dashboard-widgets/src/components/widgets/ChartWidget.jsx +28 -0
- package/templates/examples/dashboard-widgets/src/components/widgets/StatsWidget.css +24 -0
- package/templates/examples/dashboard-widgets/src/components/widgets/StatsWidget.jsx +22 -0
- package/templates/examples/dashboard-widgets/src/index.css +13 -0
- package/templates/examples/dashboard-widgets/src/main.jsx +10 -0
- package/templates/examples/dashboard-widgets/src/utils/mockData.js +30 -0
- package/templates/examples/dashboard-widgets/vite.config.js +6 -0
- package/dist/chunk-IGNO4GX2.js.map +0 -1
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
* {
|
|
2
|
+
margin: 0;
|
|
3
|
+
padding: 0;
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
body {
|
|
8
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
9
|
+
background-color: #f5f5f5;
|
|
10
|
+
color: #333;
|
|
11
|
+
line-height: 1.6;
|
|
12
|
+
padding: 20px;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.container {
|
|
16
|
+
max-width: 600px;
|
|
17
|
+
margin: 0 auto;
|
|
18
|
+
background: white;
|
|
19
|
+
border-radius: 12px;
|
|
20
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
21
|
+
padding: 30px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
header {
|
|
25
|
+
text-align: center;
|
|
26
|
+
margin-bottom: 30px;
|
|
27
|
+
padding-bottom: 20px;
|
|
28
|
+
border-bottom: 2px solid #e0e0e0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
h1 {
|
|
32
|
+
font-size: 2em;
|
|
33
|
+
color: #2c3e50;
|
|
34
|
+
margin-bottom: 8px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.subtitle {
|
|
38
|
+
color: #7f8c8d;
|
|
39
|
+
font-size: 0.95em;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.task-input {
|
|
43
|
+
display: flex;
|
|
44
|
+
gap: 10px;
|
|
45
|
+
margin-bottom: 25px;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
#taskInput {
|
|
49
|
+
flex: 1;
|
|
50
|
+
padding: 12px 16px;
|
|
51
|
+
border: 2px solid #e0e0e0;
|
|
52
|
+
border-radius: 8px;
|
|
53
|
+
font-size: 1em;
|
|
54
|
+
transition: border-color 0.2s;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#taskInput:focus {
|
|
58
|
+
outline: none;
|
|
59
|
+
border-color: #3498db;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
#addButton {
|
|
63
|
+
padding: 12px 24px;
|
|
64
|
+
background: #3498db;
|
|
65
|
+
color: white;
|
|
66
|
+
border: none;
|
|
67
|
+
border-radius: 8px;
|
|
68
|
+
font-size: 1em;
|
|
69
|
+
cursor: pointer;
|
|
70
|
+
transition: background 0.2s;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#addButton:hover {
|
|
74
|
+
background: #2980b9;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.task-list {
|
|
78
|
+
min-height: 200px;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
#taskList {
|
|
82
|
+
list-style: none;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.task-item {
|
|
86
|
+
padding: 15px;
|
|
87
|
+
margin-bottom: 10px;
|
|
88
|
+
background: #f8f9fa;
|
|
89
|
+
border-radius: 8px;
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
justify-content: space-between;
|
|
93
|
+
transition: background 0.2s;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.task-item:hover {
|
|
97
|
+
background: #e9ecef;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.task-item.completed {
|
|
101
|
+
opacity: 0.6;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.task-item.completed .task-text {
|
|
105
|
+
text-decoration: line-through;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.task-content {
|
|
109
|
+
display: flex;
|
|
110
|
+
align-items: center;
|
|
111
|
+
gap: 12px;
|
|
112
|
+
flex: 1;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.task-checkbox {
|
|
116
|
+
width: 20px;
|
|
117
|
+
height: 20px;
|
|
118
|
+
cursor: pointer;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.task-text {
|
|
122
|
+
font-size: 1em;
|
|
123
|
+
color: #2c3e50;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.delete-button {
|
|
127
|
+
padding: 6px 12px;
|
|
128
|
+
background: #e74c3c;
|
|
129
|
+
color: white;
|
|
130
|
+
border: none;
|
|
131
|
+
border-radius: 6px;
|
|
132
|
+
font-size: 0.85em;
|
|
133
|
+
cursor: pointer;
|
|
134
|
+
transition: background 0.2s;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.delete-button:hover {
|
|
138
|
+
background: #c0392b;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.stats {
|
|
142
|
+
margin-top: 20px;
|
|
143
|
+
padding-top: 20px;
|
|
144
|
+
border-top: 2px solid #e0e0e0;
|
|
145
|
+
text-align: center;
|
|
146
|
+
color: #7f8c8d;
|
|
147
|
+
font-size: 0.9em;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
footer {
|
|
151
|
+
margin-top: 30px;
|
|
152
|
+
padding-top: 20px;
|
|
153
|
+
border-top: 2px solid #e0e0e0;
|
|
154
|
+
text-align: center;
|
|
155
|
+
color: #95a5a6;
|
|
156
|
+
font-size: 0.85em;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.empty-state {
|
|
160
|
+
text-align: center;
|
|
161
|
+
padding: 40px 20px;
|
|
162
|
+
color: #95a5a6;
|
|
163
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = path.dirname(__filename);
|
|
7
|
+
|
|
8
|
+
const app = express();
|
|
9
|
+
const PORT = 3000;
|
|
10
|
+
|
|
11
|
+
// Serve static files from public directory
|
|
12
|
+
app.use(express.static(path.join(__dirname, 'public')));
|
|
13
|
+
|
|
14
|
+
app.listen(PORT, () => {
|
|
15
|
+
console.log(`✓ Task Manager running at http://localhost:${PORT}`);
|
|
16
|
+
console.log(`✓ Open your browser and try adding some tasks!`);
|
|
17
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Dashboard Widgets Demo
|
|
2
|
+
|
|
3
|
+
> **Tutorial**: [Managing Multiple Features](https://leanspec.dev/docs/tutorials/multiple-features)
|
|
4
|
+
|
|
5
|
+
## Scenario
|
|
6
|
+
|
|
7
|
+
You're building an analytics dashboard for a SaaS product. The dashboard has some basic widgets (stats cards and a simple chart), but the product team wants to add three new widgets:
|
|
8
|
+
- **Recent Activity Feed** - Show latest user actions
|
|
9
|
+
- **Performance Metrics** - Display system health indicators
|
|
10
|
+
- **Quick Actions Panel** - Common shortcuts for users
|
|
11
|
+
|
|
12
|
+
Each widget needs to be designed, implemented, and integrated into the dashboard grid.
|
|
13
|
+
|
|
14
|
+
## What's Here
|
|
15
|
+
|
|
16
|
+
A minimal React + Vite dashboard with:
|
|
17
|
+
- Grid layout for widgets
|
|
18
|
+
- Two existing widgets (Stats, Chart)
|
|
19
|
+
- Reusable widget wrapper component
|
|
20
|
+
- Mock data utilities
|
|
21
|
+
|
|
22
|
+
**Files:**
|
|
23
|
+
- `src/App.jsx` - Main dashboard component
|
|
24
|
+
- `src/components/Dashboard.jsx` - Dashboard grid layout
|
|
25
|
+
- `src/components/widgets/` - Existing widgets
|
|
26
|
+
- `src/utils/mockData.js` - Sample data generator
|
|
27
|
+
- `index.html` - Entry point
|
|
28
|
+
|
|
29
|
+
## Getting Started
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Install dependencies
|
|
33
|
+
npm install
|
|
34
|
+
|
|
35
|
+
# Start dev server
|
|
36
|
+
npm run dev
|
|
37
|
+
|
|
38
|
+
# Open http://localhost:5173 in your browser
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Your Mission
|
|
42
|
+
|
|
43
|
+
Add three new widgets to the dashboard. Follow the tutorial and ask your AI assistant:
|
|
44
|
+
|
|
45
|
+
> "Help me add three new widgets to this dashboard using LeanSpec: Recent Activity Feed, Performance Metrics, and Quick Actions Panel."
|
|
46
|
+
|
|
47
|
+
The AI will guide you through:
|
|
48
|
+
1. Creating specs for each widget (or one unified spec)
|
|
49
|
+
2. Designing the widget interfaces
|
|
50
|
+
3. Implementing components
|
|
51
|
+
4. Managing dependencies between widgets
|
|
52
|
+
5. Testing the integrated dashboard
|
|
53
|
+
|
|
54
|
+
## Current Structure
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
Dashboard
|
|
58
|
+
├── StatsWidget (implemented)
|
|
59
|
+
├── ChartWidget (implemented)
|
|
60
|
+
├── ActivityWidget (TODO)
|
|
61
|
+
├── MetricsWidget (TODO)
|
|
62
|
+
└── ActionsWidget (TODO)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Tips
|
|
66
|
+
|
|
67
|
+
- Each widget is self-contained with its own component
|
|
68
|
+
- Widgets use the `WidgetWrapper` component for consistent styling
|
|
69
|
+
- Mock data is in `utils/mockData.js` - add new generators as needed
|
|
70
|
+
- Consider which widgets share dependencies (e.g., both might need user data)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Dashboard Widgets Demo</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/src/main.jsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dashboard-widgets-demo",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "React Dashboard for LeanSpec Tutorial 2",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"keywords": ["leanspec", "tutorial", "demo"],
|
|
12
|
+
"author": "",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"react": "^18.2.0",
|
|
16
|
+
"react-dom": "^18.2.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@vitejs/plugin-react": "^4.2.1",
|
|
20
|
+
"vite": "^5.0.0"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import Dashboard from './components/Dashboard';
|
|
2
|
+
import './App.css';
|
|
3
|
+
|
|
4
|
+
function App() {
|
|
5
|
+
return (
|
|
6
|
+
<div className="app">
|
|
7
|
+
<header>
|
|
8
|
+
<h1>Analytics Dashboard</h1>
|
|
9
|
+
<p>Demo for LeanSpec Tutorial 2</p>
|
|
10
|
+
</header>
|
|
11
|
+
<Dashboard />
|
|
12
|
+
</div>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default App;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
.dashboard-grid {
|
|
2
|
+
display: grid;
|
|
3
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
4
|
+
gap: 20px;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
@media (min-width: 768px) {
|
|
8
|
+
.dashboard-grid {
|
|
9
|
+
grid-template-columns: repeat(2, 1fr);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@media (min-width: 1200px) {
|
|
14
|
+
.dashboard-grid {
|
|
15
|
+
grid-template-columns: repeat(3, 1fr);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import StatsWidget from './widgets/StatsWidget';
|
|
2
|
+
import ChartWidget from './widgets/ChartWidget';
|
|
3
|
+
import './Dashboard.css';
|
|
4
|
+
|
|
5
|
+
function Dashboard() {
|
|
6
|
+
return (
|
|
7
|
+
<div className="dashboard-grid">
|
|
8
|
+
<StatsWidget />
|
|
9
|
+
<ChartWidget />
|
|
10
|
+
{/* TODO: Add new widgets here */}
|
|
11
|
+
</div>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default Dashboard;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
.widget {
|
|
2
|
+
background: white;
|
|
3
|
+
border-radius: 8px;
|
|
4
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
5
|
+
overflow: hidden;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.widget-header {
|
|
9
|
+
padding: 15px 20px;
|
|
10
|
+
border-bottom: 1px solid #eee;
|
|
11
|
+
background: #fafafa;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.widget-header h3 {
|
|
15
|
+
margin: 0;
|
|
16
|
+
font-size: 16px;
|
|
17
|
+
font-weight: 600;
|
|
18
|
+
color: #333;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.widget-body {
|
|
22
|
+
padding: 20px;
|
|
23
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import './WidgetWrapper.css';
|
|
2
|
+
|
|
3
|
+
function WidgetWrapper({ title, children }) {
|
|
4
|
+
return (
|
|
5
|
+
<div className="widget">
|
|
6
|
+
<div className="widget-header">
|
|
7
|
+
<h3>{title}</h3>
|
|
8
|
+
</div>
|
|
9
|
+
<div className="widget-body">
|
|
10
|
+
{children}
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default WidgetWrapper;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
.chart {
|
|
2
|
+
height: 200px;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.chart-bars {
|
|
6
|
+
display: flex;
|
|
7
|
+
align-items: flex-end;
|
|
8
|
+
justify-content: space-around;
|
|
9
|
+
height: 100%;
|
|
10
|
+
gap: 8px;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.chart-bar-container {
|
|
14
|
+
flex: 1;
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
align-items: center;
|
|
18
|
+
height: 100%;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.chart-bar {
|
|
22
|
+
width: 100%;
|
|
23
|
+
background: linear-gradient(to top, #2563eb, #60a5fa);
|
|
24
|
+
border-radius: 4px 4px 0 0;
|
|
25
|
+
transition: height 0.3s ease;
|
|
26
|
+
min-height: 4px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.chart-label {
|
|
30
|
+
margin-top: 8px;
|
|
31
|
+
font-size: 11px;
|
|
32
|
+
color: #666;
|
|
33
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import WidgetWrapper from '../WidgetWrapper';
|
|
2
|
+
import { getChartData } from '../../utils/mockData';
|
|
3
|
+
import './ChartWidget.css';
|
|
4
|
+
|
|
5
|
+
function ChartWidget() {
|
|
6
|
+
const data = getChartData();
|
|
7
|
+
const maxValue = Math.max(...data.map(d => d.value));
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<WidgetWrapper title="Activity Trend">
|
|
11
|
+
<div className="chart">
|
|
12
|
+
<div className="chart-bars">
|
|
13
|
+
{data.map((item, index) => (
|
|
14
|
+
<div key={index} className="chart-bar-container">
|
|
15
|
+
<div
|
|
16
|
+
className="chart-bar"
|
|
17
|
+
style={{ height: `${(item.value / maxValue) * 100}%` }}
|
|
18
|
+
/>
|
|
19
|
+
<div className="chart-label">{item.label}</div>
|
|
20
|
+
</div>
|
|
21
|
+
))}
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</WidgetWrapper>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default ChartWidget;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
.stats-grid {
|
|
2
|
+
display: grid;
|
|
3
|
+
grid-template-columns: repeat(2, 1fr);
|
|
4
|
+
gap: 15px;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.stat-item {
|
|
8
|
+
text-align: center;
|
|
9
|
+
padding: 10px;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.stat-value {
|
|
13
|
+
font-size: 28px;
|
|
14
|
+
font-weight: bold;
|
|
15
|
+
color: #2563eb;
|
|
16
|
+
margin-bottom: 5px;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.stat-label {
|
|
20
|
+
font-size: 12px;
|
|
21
|
+
color: #666;
|
|
22
|
+
text-transform: uppercase;
|
|
23
|
+
letter-spacing: 0.5px;
|
|
24
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import WidgetWrapper from '../WidgetWrapper';
|
|
2
|
+
import { getStats } from '../../utils/mockData';
|
|
3
|
+
import './StatsWidget.css';
|
|
4
|
+
|
|
5
|
+
function StatsWidget() {
|
|
6
|
+
const stats = getStats();
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<WidgetWrapper title="Quick Stats">
|
|
10
|
+
<div className="stats-grid">
|
|
11
|
+
{stats.map((stat, index) => (
|
|
12
|
+
<div key={index} className="stat-item">
|
|
13
|
+
<div className="stat-value">{stat.value}</div>
|
|
14
|
+
<div className="stat-label">{stat.label}</div>
|
|
15
|
+
</div>
|
|
16
|
+
))}
|
|
17
|
+
</div>
|
|
18
|
+
</WidgetWrapper>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default StatsWidget;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
body {
|
|
2
|
+
margin: 0;
|
|
3
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
4
|
+
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
5
|
+
sans-serif;
|
|
6
|
+
-webkit-font-smoothing: antialiased;
|
|
7
|
+
-moz-osx-font-smoothing: grayscale;
|
|
8
|
+
background: #f5f5f5;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
* {
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock data generators for dashboard widgets
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function getStats() {
|
|
6
|
+
return [
|
|
7
|
+
{ label: 'Users', value: '2,453' },
|
|
8
|
+
{ label: 'Revenue', value: '$12.5K' },
|
|
9
|
+
{ label: 'Orders', value: '186' },
|
|
10
|
+
{ label: 'Growth', value: '+23%' },
|
|
11
|
+
];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getChartData() {
|
|
15
|
+
return [
|
|
16
|
+
{ label: 'Mon', value: 45 },
|
|
17
|
+
{ label: 'Tue', value: 62 },
|
|
18
|
+
{ label: 'Wed', value: 38 },
|
|
19
|
+
{ label: 'Thu', value: 71 },
|
|
20
|
+
{ label: 'Fri', value: 55 },
|
|
21
|
+
{ label: 'Sat', value: 28 },
|
|
22
|
+
{ label: 'Sun', value: 34 },
|
|
23
|
+
];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// TODO: Add more mock data generators for new widgets
|
|
27
|
+
// Example:
|
|
28
|
+
// export function getActivityFeed() { ... }
|
|
29
|
+
// export function getPerformanceMetrics() { ... }
|
|
30
|
+
// export function getQuickActions() { ... }
|