@dailyautomations/ui 1.0.1 → 1.2.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/CHANGELOG.md +100 -0
- package/INSTALLATION.md +238 -0
- package/MIGRATION.md +207 -0
- package/PAGE_STRUCTURES.md +718 -0
- package/README.md +125 -65
- package/dist/tokens/tailwind.preset.d.ts.map +1 -1
- package/dist/tokens/tailwind.preset.js.map +1 -1
- package/examples/README.md +150 -0
- package/examples/home-page.tsx +321 -0
- package/examples/index.html +12 -0
- package/examples/main.tsx +90 -0
- package/examples/pages/AboutPage.tsx +343 -0
- package/examples/pages/BlogPage.tsx +294 -0
- package/examples/pages/ContactPage.tsx +328 -0
- package/examples/pages/DashboardPage.tsx +355 -0
- package/examples/pages/ListPage.tsx +310 -0
- package/examples/pages/LoginPage.tsx +166 -0
- package/examples/pages/OnboardingPage.tsx +385 -0
- package/examples/pages/PricingPage.tsx +402 -0
- package/examples/pages/SettingsPage.tsx +417 -0
- package/examples/pages/SignupPage.tsx +194 -0
- package/examples/pages/index.ts +13 -0
- package/examples/styles.css +166 -0
- package/package.json +7 -1
- package/src/styles/globals.css +161 -7
- package/src/styles/theme.css +89 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Button } from "../../src/components/common/button"
|
|
5
|
+
import { Card, CardContent, CardHeader, CardTitle } from "../../src/components/common/card"
|
|
6
|
+
import { Input } from "../../src/components/common/input"
|
|
7
|
+
import {
|
|
8
|
+
Select,
|
|
9
|
+
SelectContent,
|
|
10
|
+
SelectItem,
|
|
11
|
+
SelectTrigger,
|
|
12
|
+
SelectValue,
|
|
13
|
+
} from "../../src/components/ui/select"
|
|
14
|
+
import { cn } from "../../src/utils/cn"
|
|
15
|
+
|
|
16
|
+
// Icons (inline SVGs for self-contained examples)
|
|
17
|
+
const Icons = {
|
|
18
|
+
home: (props: React.SVGProps<SVGSVGElement>) => (
|
|
19
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
20
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
|
|
21
|
+
</svg>
|
|
22
|
+
),
|
|
23
|
+
folder: (props: React.SVGProps<SVGSVGElement>) => (
|
|
24
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
25
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"/>
|
|
26
|
+
</svg>
|
|
27
|
+
),
|
|
28
|
+
document: (props: React.SVGProps<SVGSVGElement>) => (
|
|
29
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
30
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
|
|
31
|
+
</svg>
|
|
32
|
+
),
|
|
33
|
+
users: (props: React.SVGProps<SVGSVGElement>) => (
|
|
34
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
35
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"/>
|
|
36
|
+
</svg>
|
|
37
|
+
),
|
|
38
|
+
chart: (props: React.SVGProps<SVGSVGElement>) => (
|
|
39
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
40
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
|
|
41
|
+
</svg>
|
|
42
|
+
),
|
|
43
|
+
settings: (props: React.SVGProps<SVGSVGElement>) => (
|
|
44
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
45
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
|
|
46
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
|
47
|
+
</svg>
|
|
48
|
+
),
|
|
49
|
+
help: (props: React.SVGProps<SVGSVGElement>) => (
|
|
50
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
51
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
52
|
+
</svg>
|
|
53
|
+
),
|
|
54
|
+
search: (props: React.SVGProps<SVGSVGElement>) => (
|
|
55
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
56
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
|
|
57
|
+
</svg>
|
|
58
|
+
),
|
|
59
|
+
bell: (props: React.SVGProps<SVGSVGElement>) => (
|
|
60
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
61
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/>
|
|
62
|
+
</svg>
|
|
63
|
+
),
|
|
64
|
+
plus: (props: React.SVGProps<SVGSVGElement>) => (
|
|
65
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
66
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4"/>
|
|
67
|
+
</svg>
|
|
68
|
+
),
|
|
69
|
+
arrowUp: (props: React.SVGProps<SVGSVGElement>) => (
|
|
70
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
71
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 10l7-7m0 0l7 7m-7-7v18"/>
|
|
72
|
+
</svg>
|
|
73
|
+
),
|
|
74
|
+
check: (props: React.SVGProps<SVGSVGElement>) => (
|
|
75
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
76
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7"/>
|
|
77
|
+
</svg>
|
|
78
|
+
),
|
|
79
|
+
userPlus: (props: React.SVGProps<SVGSVGElement>) => (
|
|
80
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
81
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z"/>
|
|
82
|
+
</svg>
|
|
83
|
+
),
|
|
84
|
+
refresh: (props: React.SVGProps<SVGSVGElement>) => (
|
|
85
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
86
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
|
|
87
|
+
</svg>
|
|
88
|
+
),
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Sidebar Navigation Item
|
|
92
|
+
interface NavItemProps {
|
|
93
|
+
icon: React.ReactNode
|
|
94
|
+
label: string
|
|
95
|
+
href?: string
|
|
96
|
+
active?: boolean
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function NavItem({ icon, label, href = "#", active }: NavItemProps) {
|
|
100
|
+
return (
|
|
101
|
+
<a
|
|
102
|
+
href={href}
|
|
103
|
+
className={cn(
|
|
104
|
+
"flex items-center gap-3 px-3 py-2.5 rounded-md transition-colors",
|
|
105
|
+
active
|
|
106
|
+
? "bg-purple-500/10 text-white border-l-[3px] border-purple-500"
|
|
107
|
+
: "text-zinc-400 hover:bg-purple-500/10 hover:text-white"
|
|
108
|
+
)}
|
|
109
|
+
>
|
|
110
|
+
<span className="w-5 h-5">{icon}</span>
|
|
111
|
+
{label}
|
|
112
|
+
</a>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Metric Card
|
|
117
|
+
interface MetricCardProps {
|
|
118
|
+
title: string
|
|
119
|
+
value: string | number
|
|
120
|
+
change?: string
|
|
121
|
+
changeType?: "positive" | "negative" | "neutral"
|
|
122
|
+
children?: React.ReactNode
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function MetricCard({ title, value, change, changeType = "neutral", children }: MetricCardProps) {
|
|
126
|
+
return (
|
|
127
|
+
<Card>
|
|
128
|
+
<CardContent className="p-5">
|
|
129
|
+
<p className="text-sm text-zinc-400 mb-1">{title}</p>
|
|
130
|
+
<p className="text-3xl font-bold">{value}</p>
|
|
131
|
+
{change && (
|
|
132
|
+
<p className={cn(
|
|
133
|
+
"text-sm mt-2 flex items-center gap-1",
|
|
134
|
+
changeType === "positive" && "text-green-400",
|
|
135
|
+
changeType === "negative" && "text-red-400",
|
|
136
|
+
changeType === "neutral" && "text-zinc-400"
|
|
137
|
+
)}>
|
|
138
|
+
{changeType === "positive" && <Icons.arrowUp className="w-4 h-4" />}
|
|
139
|
+
{change}
|
|
140
|
+
</p>
|
|
141
|
+
)}
|
|
142
|
+
{children}
|
|
143
|
+
</CardContent>
|
|
144
|
+
</Card>
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Activity Item
|
|
149
|
+
interface ActivityItemProps {
|
|
150
|
+
icon: React.ReactNode
|
|
151
|
+
iconBg: string
|
|
152
|
+
title: string
|
|
153
|
+
time: string
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function ActivityItem({ icon, iconBg, title, time }: ActivityItemProps) {
|
|
157
|
+
return (
|
|
158
|
+
<div className="flex gap-3">
|
|
159
|
+
<div className={cn("w-8 h-8 rounded-full flex items-center justify-center shrink-0", iconBg)}>
|
|
160
|
+
{icon}
|
|
161
|
+
</div>
|
|
162
|
+
<div>
|
|
163
|
+
<p className="text-sm">{title}</p>
|
|
164
|
+
<p className="text-xs text-zinc-500 mt-0.5">{time}</p>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Main Dashboard Page Component
|
|
171
|
+
export function DashboardPage() {
|
|
172
|
+
return (
|
|
173
|
+
<div className="flex min-h-screen bg-[#0F0F14] text-white">
|
|
174
|
+
{/* Sidebar */}
|
|
175
|
+
<aside className="w-60 bg-[#1a1a24] border-r border-zinc-800 flex flex-col">
|
|
176
|
+
{/* Logo */}
|
|
177
|
+
<div className="h-16 flex items-center px-4 border-b border-zinc-800">
|
|
178
|
+
<span className="text-xl font-bold">
|
|
179
|
+
<span className="text-purple-500">daily</span>
|
|
180
|
+
<span className="text-orange-500">X</span>
|
|
181
|
+
</span>
|
|
182
|
+
</div>
|
|
183
|
+
|
|
184
|
+
{/* Navigation */}
|
|
185
|
+
<nav className="flex-1 p-3 space-y-1">
|
|
186
|
+
<NavItem icon={<Icons.home className="w-5 h-5" />} label="Dashboard" active />
|
|
187
|
+
<NavItem icon={<Icons.folder className="w-5 h-5" />} label="Projects" />
|
|
188
|
+
<NavItem icon={<Icons.document className="w-5 h-5" />} label="Documents" />
|
|
189
|
+
<NavItem icon={<Icons.users className="w-5 h-5" />} label="Team" />
|
|
190
|
+
<NavItem icon={<Icons.chart className="w-5 h-5" />} label="Analytics" />
|
|
191
|
+
|
|
192
|
+
<div className="pt-4 mt-4 border-t border-zinc-800">
|
|
193
|
+
<NavItem icon={<Icons.settings className="w-5 h-5" />} label="Settings" />
|
|
194
|
+
<NavItem icon={<Icons.help className="w-5 h-5" />} label="Help" />
|
|
195
|
+
</div>
|
|
196
|
+
</nav>
|
|
197
|
+
|
|
198
|
+
{/* User Menu */}
|
|
199
|
+
<div className="p-3 border-t border-zinc-800">
|
|
200
|
+
<div className="flex items-center gap-3 px-3 py-2 rounded-md hover:bg-purple-500/10 cursor-pointer transition-colors">
|
|
201
|
+
<div className="w-8 h-8 rounded-full bg-purple-500 flex items-center justify-center text-sm font-semibold">
|
|
202
|
+
JD
|
|
203
|
+
</div>
|
|
204
|
+
<div className="flex-1 min-w-0">
|
|
205
|
+
<p className="text-sm font-medium truncate">John Doe</p>
|
|
206
|
+
<p className="text-xs text-zinc-500 truncate">john@daily.com</p>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
</aside>
|
|
211
|
+
|
|
212
|
+
{/* Main Content */}
|
|
213
|
+
<div className="flex-1 flex flex-col">
|
|
214
|
+
{/* Topbar */}
|
|
215
|
+
<header className="h-16 bg-[#1a1a24] border-b border-zinc-800 flex items-center justify-between px-6">
|
|
216
|
+
<div className="relative">
|
|
217
|
+
<Icons.search className="w-5 h-5 absolute left-3 top-1/2 -translate-y-1/2 text-zinc-500" />
|
|
218
|
+
<Input
|
|
219
|
+
type="text"
|
|
220
|
+
placeholder="Search..."
|
|
221
|
+
className="w-64 pl-10"
|
|
222
|
+
/>
|
|
223
|
+
</div>
|
|
224
|
+
<Button variant="ghost" size="icon">
|
|
225
|
+
<Icons.bell className="w-5 h-5" />
|
|
226
|
+
</Button>
|
|
227
|
+
</header>
|
|
228
|
+
|
|
229
|
+
{/* Page Content */}
|
|
230
|
+
<main className="flex-1 p-8 overflow-y-auto">
|
|
231
|
+
{/* Page Header */}
|
|
232
|
+
<div className="flex items-center justify-between mb-8">
|
|
233
|
+
<div>
|
|
234
|
+
<h1 className="text-2xl font-bold">Welcome back, John</h1>
|
|
235
|
+
<p className="text-zinc-400 mt-1">
|
|
236
|
+
Here's what's happening with your projects today.
|
|
237
|
+
</p>
|
|
238
|
+
</div>
|
|
239
|
+
<Button>
|
|
240
|
+
<Icons.plus className="w-5 h-5" />
|
|
241
|
+
New Project
|
|
242
|
+
</Button>
|
|
243
|
+
</div>
|
|
244
|
+
|
|
245
|
+
{/* Metrics Row */}
|
|
246
|
+
<div className="grid grid-cols-4 gap-4 mb-8">
|
|
247
|
+
<MetricCard
|
|
248
|
+
title="Total Projects"
|
|
249
|
+
value={24}
|
|
250
|
+
change="+12% from last month"
|
|
251
|
+
changeType="positive"
|
|
252
|
+
/>
|
|
253
|
+
<MetricCard
|
|
254
|
+
title="Documents Processed"
|
|
255
|
+
value="1,284"
|
|
256
|
+
change="+8% from last month"
|
|
257
|
+
changeType="positive"
|
|
258
|
+
/>
|
|
259
|
+
<MetricCard
|
|
260
|
+
title="Team Members"
|
|
261
|
+
value={8}
|
|
262
|
+
change="2 pending invites"
|
|
263
|
+
changeType="neutral"
|
|
264
|
+
/>
|
|
265
|
+
<MetricCard title="Storage Used" value="78%">
|
|
266
|
+
<div className="mt-3 h-2 bg-zinc-800 rounded-full overflow-hidden">
|
|
267
|
+
<div className="h-full w-[78%] bg-purple-500 rounded-full" />
|
|
268
|
+
</div>
|
|
269
|
+
</MetricCard>
|
|
270
|
+
</div>
|
|
271
|
+
|
|
272
|
+
{/* Main Content Grid */}
|
|
273
|
+
<div className="grid grid-cols-3 gap-6">
|
|
274
|
+
{/* Chart Area */}
|
|
275
|
+
<Card className="col-span-2">
|
|
276
|
+
<CardHeader className="flex flex-row items-center justify-between">
|
|
277
|
+
<CardTitle>Activity Overview</CardTitle>
|
|
278
|
+
<Select defaultValue="7d">
|
|
279
|
+
<SelectTrigger className="w-[140px]">
|
|
280
|
+
<SelectValue />
|
|
281
|
+
</SelectTrigger>
|
|
282
|
+
<SelectContent>
|
|
283
|
+
<SelectItem value="7d">Last 7 days</SelectItem>
|
|
284
|
+
<SelectItem value="30d">Last 30 days</SelectItem>
|
|
285
|
+
<SelectItem value="90d">Last 90 days</SelectItem>
|
|
286
|
+
</SelectContent>
|
|
287
|
+
</Select>
|
|
288
|
+
</CardHeader>
|
|
289
|
+
<CardContent>
|
|
290
|
+
{/* Placeholder Chart */}
|
|
291
|
+
<div className="h-64 flex items-end justify-between gap-2 px-4">
|
|
292
|
+
{[40, 65, 45, 80, 55, 70, 90].map((height, i) => (
|
|
293
|
+
<div
|
|
294
|
+
key={i}
|
|
295
|
+
className={cn(
|
|
296
|
+
"flex-1 rounded-t transition-all",
|
|
297
|
+
i === 6 ? "bg-purple-500" : "bg-purple-500/20"
|
|
298
|
+
)}
|
|
299
|
+
style={{ height: `${height}%` }}
|
|
300
|
+
/>
|
|
301
|
+
))}
|
|
302
|
+
</div>
|
|
303
|
+
<div className="flex justify-between text-xs text-zinc-500 mt-2 px-4">
|
|
304
|
+
{["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].map((day) => (
|
|
305
|
+
<span key={day}>{day}</span>
|
|
306
|
+
))}
|
|
307
|
+
</div>
|
|
308
|
+
</CardContent>
|
|
309
|
+
</Card>
|
|
310
|
+
|
|
311
|
+
{/* Activity Feed */}
|
|
312
|
+
<Card>
|
|
313
|
+
<CardHeader>
|
|
314
|
+
<CardTitle>Recent Activity</CardTitle>
|
|
315
|
+
</CardHeader>
|
|
316
|
+
<CardContent className="space-y-4">
|
|
317
|
+
<ActivityItem
|
|
318
|
+
icon={<Icons.check className="w-4 h-4 text-green-400" />}
|
|
319
|
+
iconBg="bg-green-500/20"
|
|
320
|
+
title='Project "Website Redesign" completed'
|
|
321
|
+
time="2 minutes ago"
|
|
322
|
+
/>
|
|
323
|
+
<ActivityItem
|
|
324
|
+
icon={<Icons.plus className="w-4 h-4 text-purple-400" />}
|
|
325
|
+
iconBg="bg-purple-500/20"
|
|
326
|
+
title='New document added to "Q4 Reports"'
|
|
327
|
+
time="15 minutes ago"
|
|
328
|
+
/>
|
|
329
|
+
<ActivityItem
|
|
330
|
+
icon={<Icons.userPlus className="w-4 h-4 text-orange-400" />}
|
|
331
|
+
iconBg="bg-orange-500/20"
|
|
332
|
+
title="Sarah joined the team"
|
|
333
|
+
time="1 hour ago"
|
|
334
|
+
/>
|
|
335
|
+
<ActivityItem
|
|
336
|
+
icon={<Icons.refresh className="w-4 h-4 text-blue-400" />}
|
|
337
|
+
iconBg="bg-blue-500/20"
|
|
338
|
+
title="Sync completed for 47 documents"
|
|
339
|
+
time="3 hours ago"
|
|
340
|
+
/>
|
|
341
|
+
</CardContent>
|
|
342
|
+
<div className="px-6 pb-4">
|
|
343
|
+
<Button variant="link" className="w-full">
|
|
344
|
+
View all activity
|
|
345
|
+
</Button>
|
|
346
|
+
</div>
|
|
347
|
+
</Card>
|
|
348
|
+
</div>
|
|
349
|
+
</main>
|
|
350
|
+
</div>
|
|
351
|
+
</div>
|
|
352
|
+
)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export default DashboardPage
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Button } from "../../src/components/common/button"
|
|
5
|
+
import { Card } from "../../src/components/common/card"
|
|
6
|
+
import { Input } from "../../src/components/common/input"
|
|
7
|
+
import { Checkbox } from "../../src/components/common/checkbox"
|
|
8
|
+
import { cn } from "../../src/utils/cn"
|
|
9
|
+
|
|
10
|
+
// Status Badge Component
|
|
11
|
+
interface StatusBadgeProps {
|
|
12
|
+
status: "active" | "review" | "planning" | "archived"
|
|
13
|
+
children: React.ReactNode
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function StatusBadge({ status, children }: StatusBadgeProps) {
|
|
17
|
+
const styles = {
|
|
18
|
+
active: "bg-green-500/10 text-green-400",
|
|
19
|
+
review: "bg-amber-500/10 text-amber-400",
|
|
20
|
+
planning: "bg-purple-500/10 text-purple-400",
|
|
21
|
+
archived: "bg-zinc-500/10 text-zinc-400",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const dotStyles = {
|
|
25
|
+
active: "bg-green-400",
|
|
26
|
+
review: "bg-amber-400",
|
|
27
|
+
planning: "bg-purple-400",
|
|
28
|
+
archived: "bg-zinc-400",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<span className={cn(
|
|
33
|
+
"inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium",
|
|
34
|
+
styles[status]
|
|
35
|
+
)}>
|
|
36
|
+
<span className={cn("w-1.5 h-1.5 rounded-full", dotStyles[status])} />
|
|
37
|
+
{children}
|
|
38
|
+
</span>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Table Row Component
|
|
43
|
+
interface ProjectRowProps {
|
|
44
|
+
name: string
|
|
45
|
+
description: string
|
|
46
|
+
status: "active" | "review" | "planning" | "archived"
|
|
47
|
+
statusLabel: string
|
|
48
|
+
documents: number
|
|
49
|
+
updatedAt: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function ProjectRow({ name, description, status, statusLabel, documents, updatedAt }: ProjectRowProps) {
|
|
53
|
+
return (
|
|
54
|
+
<tr className="border-b border-zinc-800 hover:bg-zinc-800/50 transition-colors cursor-pointer">
|
|
55
|
+
<td className="p-4">
|
|
56
|
+
<Checkbox />
|
|
57
|
+
</td>
|
|
58
|
+
<td className="p-4">
|
|
59
|
+
<span className="font-medium hover:text-purple-400 transition-colors">{name}</span>
|
|
60
|
+
<p className="text-sm text-zinc-500 mt-0.5">{description}</p>
|
|
61
|
+
</td>
|
|
62
|
+
<td className="p-4">
|
|
63
|
+
<StatusBadge status={status}>{statusLabel}</StatusBadge>
|
|
64
|
+
</td>
|
|
65
|
+
<td className="p-4 text-zinc-400">{documents}</td>
|
|
66
|
+
<td className="p-4 text-zinc-400">{updatedAt}</td>
|
|
67
|
+
<td className="p-4">
|
|
68
|
+
<Button variant="ghost" size="icon" className="h-8 w-8">
|
|
69
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
70
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z"/>
|
|
71
|
+
</svg>
|
|
72
|
+
</Button>
|
|
73
|
+
</td>
|
|
74
|
+
</tr>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Icons
|
|
79
|
+
const Icons = {
|
|
80
|
+
search: (props: React.SVGProps<SVGSVGElement>) => (
|
|
81
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
82
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
|
|
83
|
+
</svg>
|
|
84
|
+
),
|
|
85
|
+
filter: (props: React.SVGProps<SVGSVGElement>) => (
|
|
86
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
87
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"/>
|
|
88
|
+
</svg>
|
|
89
|
+
),
|
|
90
|
+
sort: (props: React.SVGProps<SVGSVGElement>) => (
|
|
91
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
92
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 4h13M3 8h9m-9 4h6m4 0l4-4m0 0l4 4m-4-4v12"/>
|
|
93
|
+
</svg>
|
|
94
|
+
),
|
|
95
|
+
list: (props: React.SVGProps<SVGSVGElement>) => (
|
|
96
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
97
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 10h16M4 14h16M4 18h16"/>
|
|
98
|
+
</svg>
|
|
99
|
+
),
|
|
100
|
+
grid: (props: React.SVGProps<SVGSVGElement>) => (
|
|
101
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
102
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"/>
|
|
103
|
+
</svg>
|
|
104
|
+
),
|
|
105
|
+
plus: (props: React.SVGProps<SVGSVGElement>) => (
|
|
106
|
+
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24" {...props}>
|
|
107
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4"/>
|
|
108
|
+
</svg>
|
|
109
|
+
),
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Sample data
|
|
113
|
+
const projects: ProjectRowProps[] = [
|
|
114
|
+
{
|
|
115
|
+
name: "Website Redesign",
|
|
116
|
+
description: "Marketing website overhaul",
|
|
117
|
+
status: "active",
|
|
118
|
+
statusLabel: "Active",
|
|
119
|
+
documents: 47,
|
|
120
|
+
updatedAt: "2 hours ago",
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "Q4 Financial Reports",
|
|
124
|
+
description: "Quarterly analysis and forecasts",
|
|
125
|
+
status: "review",
|
|
126
|
+
statusLabel: "In Review",
|
|
127
|
+
documents: 124,
|
|
128
|
+
updatedAt: "Yesterday",
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: "Product Launch 2026",
|
|
132
|
+
description: "New product line preparation",
|
|
133
|
+
status: "planning",
|
|
134
|
+
statusLabel: "Planning",
|
|
135
|
+
documents: 18,
|
|
136
|
+
updatedAt: "3 days ago",
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: "Customer Research",
|
|
140
|
+
description: "User interviews and surveys",
|
|
141
|
+
status: "archived",
|
|
142
|
+
statusLabel: "Archived",
|
|
143
|
+
documents: 89,
|
|
144
|
+
updatedAt: "2 weeks ago",
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: "API Documentation",
|
|
148
|
+
description: "Developer docs and guides",
|
|
149
|
+
status: "active",
|
|
150
|
+
statusLabel: "Active",
|
|
151
|
+
documents: 203,
|
|
152
|
+
updatedAt: "5 hours ago",
|
|
153
|
+
},
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
// Main List Page Component
|
|
157
|
+
export function ListPage() {
|
|
158
|
+
const [viewMode, setViewMode] = React.useState<"list" | "grid">("list")
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<div className="min-h-screen bg-[#0F0F14] text-white p-8">
|
|
162
|
+
{/* Page Header */}
|
|
163
|
+
<div className="flex items-center justify-between mb-6">
|
|
164
|
+
<div>
|
|
165
|
+
<h1 className="text-2xl font-bold">
|
|
166
|
+
Projects <span className="text-zinc-500 font-normal">(24)</span>
|
|
167
|
+
</h1>
|
|
168
|
+
</div>
|
|
169
|
+
<Button>
|
|
170
|
+
<Icons.plus className="w-5 h-5" />
|
|
171
|
+
Create Project
|
|
172
|
+
</Button>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
{/* Toolbar */}
|
|
176
|
+
<div className="flex items-center justify-between mb-4 gap-4">
|
|
177
|
+
<div className="flex items-center gap-3">
|
|
178
|
+
<div className="relative">
|
|
179
|
+
<Icons.search className="w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-zinc-500" />
|
|
180
|
+
<Input
|
|
181
|
+
type="text"
|
|
182
|
+
placeholder="Search projects..."
|
|
183
|
+
className="w-64 pl-9"
|
|
184
|
+
/>
|
|
185
|
+
</div>
|
|
186
|
+
<Button variant="outline" size="sm">
|
|
187
|
+
<Icons.filter className="w-4 h-4" />
|
|
188
|
+
Filter
|
|
189
|
+
</Button>
|
|
190
|
+
<Button variant="outline" size="sm">
|
|
191
|
+
<Icons.sort className="w-4 h-4" />
|
|
192
|
+
Sort
|
|
193
|
+
</Button>
|
|
194
|
+
</div>
|
|
195
|
+
<div className="flex items-center gap-1 p-1 bg-zinc-900 rounded-md">
|
|
196
|
+
<button
|
|
197
|
+
onClick={() => setViewMode("list")}
|
|
198
|
+
className={cn(
|
|
199
|
+
"p-1.5 rounded",
|
|
200
|
+
viewMode === "list" ? "bg-purple-500/20 text-purple-400" : "text-zinc-500 hover:text-zinc-300"
|
|
201
|
+
)}
|
|
202
|
+
>
|
|
203
|
+
<Icons.list className="w-4 h-4" />
|
|
204
|
+
</button>
|
|
205
|
+
<button
|
|
206
|
+
onClick={() => setViewMode("grid")}
|
|
207
|
+
className={cn(
|
|
208
|
+
"p-1.5 rounded",
|
|
209
|
+
viewMode === "grid" ? "bg-purple-500/20 text-purple-400" : "text-zinc-500 hover:text-zinc-300"
|
|
210
|
+
)}
|
|
211
|
+
>
|
|
212
|
+
<Icons.grid className="w-4 h-4" />
|
|
213
|
+
</button>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
{/* Table */}
|
|
218
|
+
<Card className="overflow-hidden">
|
|
219
|
+
<table className="w-full">
|
|
220
|
+
<thead>
|
|
221
|
+
<tr className="border-b border-zinc-800">
|
|
222
|
+
<th className="w-12 p-4">
|
|
223
|
+
<Checkbox />
|
|
224
|
+
</th>
|
|
225
|
+
<th className="text-left p-4 text-sm font-medium text-zinc-400">Name</th>
|
|
226
|
+
<th className="text-left p-4 text-sm font-medium text-zinc-400">Status</th>
|
|
227
|
+
<th className="text-left p-4 text-sm font-medium text-zinc-400">Documents</th>
|
|
228
|
+
<th className="text-left p-4 text-sm font-medium text-zinc-400">Last Updated</th>
|
|
229
|
+
<th className="w-12 p-4"></th>
|
|
230
|
+
</tr>
|
|
231
|
+
</thead>
|
|
232
|
+
<tbody>
|
|
233
|
+
{projects.map((project) => (
|
|
234
|
+
<ProjectRow key={project.name} {...project} />
|
|
235
|
+
))}
|
|
236
|
+
</tbody>
|
|
237
|
+
</table>
|
|
238
|
+
</Card>
|
|
239
|
+
|
|
240
|
+
{/* Pagination */}
|
|
241
|
+
<div className="flex items-center justify-between mt-4">
|
|
242
|
+
<p className="text-sm text-zinc-500">Showing 1-5 of 24 projects</p>
|
|
243
|
+
<div className="flex items-center gap-1">
|
|
244
|
+
<Button variant="outline" size="sm" disabled>
|
|
245
|
+
Previous
|
|
246
|
+
</Button>
|
|
247
|
+
<Button variant="secondary" size="sm" className="w-9">
|
|
248
|
+
1
|
|
249
|
+
</Button>
|
|
250
|
+
<Button variant="outline" size="sm" className="w-9">
|
|
251
|
+
2
|
|
252
|
+
</Button>
|
|
253
|
+
<Button variant="outline" size="sm" className="w-9">
|
|
254
|
+
3
|
|
255
|
+
</Button>
|
|
256
|
+
<Button variant="outline" size="sm" className="w-9">
|
|
257
|
+
4
|
|
258
|
+
</Button>
|
|
259
|
+
<Button variant="outline" size="sm" className="w-9">
|
|
260
|
+
5
|
|
261
|
+
</Button>
|
|
262
|
+
<Button variant="outline" size="sm">
|
|
263
|
+
Next
|
|
264
|
+
</Button>
|
|
265
|
+
</div>
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Empty State Component
|
|
272
|
+
export function ListPageEmpty() {
|
|
273
|
+
return (
|
|
274
|
+
<div className="min-h-screen bg-[#0F0F14] text-white p-8">
|
|
275
|
+
{/* Page Header */}
|
|
276
|
+
<div className="flex items-center justify-between mb-6">
|
|
277
|
+
<div>
|
|
278
|
+
<h1 className="text-2xl font-bold">
|
|
279
|
+
Projects <span className="text-zinc-500 font-normal">(0)</span>
|
|
280
|
+
</h1>
|
|
281
|
+
</div>
|
|
282
|
+
<Button>
|
|
283
|
+
<Icons.plus className="w-5 h-5" />
|
|
284
|
+
Create Project
|
|
285
|
+
</Button>
|
|
286
|
+
</div>
|
|
287
|
+
|
|
288
|
+
{/* Empty State */}
|
|
289
|
+
<Card className="p-12">
|
|
290
|
+
<div className="text-center max-w-md mx-auto">
|
|
291
|
+
<div className="w-16 h-16 rounded-full bg-purple-500/10 flex items-center justify-center mx-auto mb-4">
|
|
292
|
+
<svg className="w-8 h-8 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
293
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"/>
|
|
294
|
+
</svg>
|
|
295
|
+
</div>
|
|
296
|
+
<h2 className="text-xl font-semibold mb-2">No projects yet</h2>
|
|
297
|
+
<p className="text-zinc-400 mb-6">
|
|
298
|
+
Create your first project to start organizing your documents and collaborating with your team.
|
|
299
|
+
</p>
|
|
300
|
+
<Button>
|
|
301
|
+
<Icons.plus className="w-5 h-5" />
|
|
302
|
+
Create Project
|
|
303
|
+
</Button>
|
|
304
|
+
</div>
|
|
305
|
+
</Card>
|
|
306
|
+
</div>
|
|
307
|
+
)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export default ListPage
|