@burdenoff/microfe-proserviceworld 2026.525.1 → 2026.624.1
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/proserviceworld/pages/AdminReportsPage.js +28 -28
- package/dist/proserviceworld/pages/AdminReportsPage.js.map +1 -1
- package/dist/proserviceworld/pages/AdminUsersPage.js +19 -19
- package/dist/proserviceworld/pages/AdminUsersPage.js.map +1 -1
- package/dist/proserviceworld/pages/AnalyticsPage.js +27 -27
- package/dist/proserviceworld/pages/AnalyticsPage.js.map +1 -1
- package/dist/proserviceworld/pages/BookingsPage.js +24 -24
- package/dist/proserviceworld/pages/BookingsPage.js.map +1 -1
- package/dist/proserviceworld/pages/DashboardPage.js +33 -33
- package/dist/proserviceworld/pages/DashboardPage.js.map +1 -1
- package/dist/proserviceworld/pages/DocumentsPage.js +29 -29
- package/dist/proserviceworld/pages/DocumentsPage.js.map +1 -1
- package/dist/proserviceworld/pages/MessagesPage.js +15 -15
- package/dist/proserviceworld/pages/MessagesPage.js.map +1 -1
- package/dist/proserviceworld/pages/PaymentsPage.js +19 -19
- package/dist/proserviceworld/pages/PaymentsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ProjectsPage.js +36 -36
- package/dist/proserviceworld/pages/ProjectsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ProviderDetailPage.js +36 -36
- package/dist/proserviceworld/pages/ProviderDetailPage.js.map +1 -1
- package/dist/proserviceworld/pages/ProvidersPage.js +19 -19
- package/dist/proserviceworld/pages/ProvidersPage.js.map +1 -1
- package/dist/proserviceworld/pages/ReviewsPage.js +22 -22
- package/dist/proserviceworld/pages/ReviewsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ServiceDetailsPage.js +35 -35
- package/dist/proserviceworld/pages/ServiceDetailsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ServicesPage.js +14 -14
- package/dist/proserviceworld/pages/ServicesPage.js.map +1 -1
- package/dist/proserviceworld/pages/VerificationPage.js +23 -23
- package/dist/proserviceworld/pages/VerificationPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -69,7 +69,7 @@ function O() {
|
|
|
69
69
|
className: "text-2xl font-semibold",
|
|
70
70
|
children: "Projects"
|
|
71
71
|
}), /* @__PURE__ */ _("p", {
|
|
72
|
-
className: "text-sm text-
|
|
72
|
+
className: "text-sm text-text-secondary",
|
|
73
73
|
children: "Track progress and manage your professional service projects."
|
|
74
74
|
})] }), /* @__PURE__ */ v(b, {
|
|
75
75
|
size: "sm",
|
|
@@ -82,7 +82,7 @@ function O() {
|
|
|
82
82
|
children: [
|
|
83
83
|
/* @__PURE__ */ v("div", {
|
|
84
84
|
className: "relative",
|
|
85
|
-
children: [/* @__PURE__ */ _(f, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-
|
|
85
|
+
children: [/* @__PURE__ */ _(f, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ _(T, {
|
|
86
86
|
placeholder: "Search projects...",
|
|
87
87
|
value: k,
|
|
88
88
|
onChange: (e) => A(e.target.value),
|
|
@@ -100,14 +100,14 @@ function O() {
|
|
|
100
100
|
"cancelled"
|
|
101
101
|
].map((e) => /* @__PURE__ */ _("button", {
|
|
102
102
|
onClick: () => M(e),
|
|
103
|
-
className: `rounded px-2 py-1 text-xs font-medium transition-colors ${j === e ? "bg-primary text-primary-
|
|
103
|
+
className: `rounded px-2 py-1 text-xs font-medium transition-colors ${j === e ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken text-text-secondary hover:bg-bg-sunken/80"}`,
|
|
104
104
|
children: e === "all" ? "All" : E[e].label
|
|
105
105
|
}, e))
|
|
106
106
|
}),
|
|
107
107
|
/* @__PURE__ */ _("div", {
|
|
108
108
|
className: "space-y-2",
|
|
109
109
|
children: L.length === 0 ? /* @__PURE__ */ _("p", {
|
|
110
|
-
className: "py-8 text-center text-sm text-
|
|
110
|
+
className: "py-8 text-center text-sm text-text-secondary",
|
|
111
111
|
children: "No projects found."
|
|
112
112
|
}) : L.map((e) => {
|
|
113
113
|
let t = E[e.status];
|
|
@@ -115,14 +115,14 @@ function O() {
|
|
|
115
115
|
onClick: () => {
|
|
116
116
|
P(e.id), I("overview");
|
|
117
117
|
},
|
|
118
|
-
className: `w-full rounded-lg border p-3 text-left transition-colors ${N === e.id ? "border-primary bg-primary/5" : "hover:bg-
|
|
118
|
+
className: `w-full rounded-lg border p-3 text-left transition-colors ${N === e.id ? "border-primary bg-action-primary-bg/5" : "hover:bg-bg-sunken/50"}`,
|
|
119
119
|
children: [
|
|
120
120
|
/* @__PURE__ */ v("div", {
|
|
121
121
|
className: "flex items-center justify-between",
|
|
122
122
|
children: [/* @__PURE__ */ _("h3", {
|
|
123
123
|
className: "font-medium text-sm truncate pr-2",
|
|
124
124
|
children: e.title
|
|
125
|
-
}), /* @__PURE__ */ _(s, { className: "h-4 w-4 shrink-0 text-
|
|
125
|
+
}), /* @__PURE__ */ _(s, { className: "h-4 w-4 shrink-0 text-text-secondary" })]
|
|
126
126
|
}),
|
|
127
127
|
/* @__PURE__ */ v("div", {
|
|
128
128
|
className: "mt-2 flex items-center gap-2",
|
|
@@ -138,12 +138,12 @@ function O() {
|
|
|
138
138
|
/* @__PURE__ */ v("div", {
|
|
139
139
|
className: "mt-2",
|
|
140
140
|
children: [/* @__PURE__ */ v("div", {
|
|
141
|
-
className: "flex items-center justify-between text-xs text-
|
|
141
|
+
className: "flex items-center justify-between text-xs text-text-secondary",
|
|
142
142
|
children: [/* @__PURE__ */ _("span", { children: "Progress" }), /* @__PURE__ */ v("span", { children: [e.progress, "%"] })]
|
|
143
143
|
}), /* @__PURE__ */ _("div", {
|
|
144
|
-
className: "mt-1 h-1.5 overflow-hidden rounded-full bg-
|
|
144
|
+
className: "mt-1 h-1.5 overflow-hidden rounded-full bg-bg-sunken",
|
|
145
145
|
children: /* @__PURE__ */ _("div", {
|
|
146
|
-
className: "h-1.5 rounded-full bg-primary transition-all",
|
|
146
|
+
className: "h-1.5 rounded-full bg-action-primary-bg transition-all",
|
|
147
147
|
style: { width: `${e.progress}%` }
|
|
148
148
|
})
|
|
149
149
|
})]
|
|
@@ -179,7 +179,7 @@ function O() {
|
|
|
179
179
|
children: R.title
|
|
180
180
|
}),
|
|
181
181
|
/* @__PURE__ */ _("p", {
|
|
182
|
-
className: "text-sm text-
|
|
182
|
+
className: "text-sm text-text-secondary",
|
|
183
183
|
children: R.description
|
|
184
184
|
})
|
|
185
185
|
]
|
|
@@ -187,7 +187,7 @@ function O() {
|
|
|
187
187
|
className: "text-right",
|
|
188
188
|
children: [
|
|
189
189
|
/* @__PURE__ */ _("p", {
|
|
190
|
-
className: "text-sm text-
|
|
190
|
+
className: "text-sm text-text-secondary",
|
|
191
191
|
children: "Budget"
|
|
192
192
|
}),
|
|
193
193
|
/* @__PURE__ */ v("p", {
|
|
@@ -195,7 +195,7 @@ function O() {
|
|
|
195
195
|
children: ["$", R.budget.toLocaleString()]
|
|
196
196
|
}),
|
|
197
197
|
/* @__PURE__ */ v("p", {
|
|
198
|
-
className: "text-xs text-
|
|
198
|
+
className: "text-xs text-text-secondary",
|
|
199
199
|
children: [
|
|
200
200
|
"Spent: $",
|
|
201
201
|
R.spent.toLocaleString(),
|
|
@@ -212,16 +212,16 @@ function O() {
|
|
|
212
212
|
children: [/* @__PURE__ */ v("div", {
|
|
213
213
|
className: "flex items-center justify-between text-sm",
|
|
214
214
|
children: [/* @__PURE__ */ _("span", {
|
|
215
|
-
className: "text-
|
|
215
|
+
className: "text-text-secondary",
|
|
216
216
|
children: "Overall Progress"
|
|
217
217
|
}), /* @__PURE__ */ v("span", {
|
|
218
218
|
className: "font-medium",
|
|
219
219
|
children: [R.progress, "%"]
|
|
220
220
|
})]
|
|
221
221
|
}), /* @__PURE__ */ _("div", {
|
|
222
|
-
className: "mt-1 h-3 overflow-hidden rounded-full bg-
|
|
222
|
+
className: "mt-1 h-3 overflow-hidden rounded-full bg-bg-sunken",
|
|
223
223
|
children: /* @__PURE__ */ _("div", {
|
|
224
|
-
className: "h-3 rounded-full bg-primary transition-all",
|
|
224
|
+
className: "h-3 rounded-full bg-action-primary-bg transition-all",
|
|
225
225
|
style: { width: `${R.progress}%` }
|
|
226
226
|
})
|
|
227
227
|
})]
|
|
@@ -230,7 +230,7 @@ function O() {
|
|
|
230
230
|
className: "mt-4 flex flex-wrap gap-4 text-sm",
|
|
231
231
|
children: [
|
|
232
232
|
/* @__PURE__ */ v("span", {
|
|
233
|
-
className: "flex items-center gap-1 text-
|
|
233
|
+
className: "flex items-center gap-1 text-text-secondary",
|
|
234
234
|
children: [
|
|
235
235
|
/* @__PURE__ */ _(a, { className: "h-4 w-4" }),
|
|
236
236
|
" ",
|
|
@@ -241,7 +241,7 @@ function O() {
|
|
|
241
241
|
]
|
|
242
242
|
}),
|
|
243
243
|
/* @__PURE__ */ v("span", {
|
|
244
|
-
className: "flex items-center gap-1 text-
|
|
244
|
+
className: "flex items-center gap-1 text-text-secondary",
|
|
245
245
|
children: [
|
|
246
246
|
/* @__PURE__ */ _(h, { className: "h-4 w-4" }),
|
|
247
247
|
" ",
|
|
@@ -250,7 +250,7 @@ function O() {
|
|
|
250
250
|
]
|
|
251
251
|
}),
|
|
252
252
|
/* @__PURE__ */ v("span", {
|
|
253
|
-
className: "flex items-center gap-1 text-
|
|
253
|
+
className: "flex items-center gap-1 text-text-secondary",
|
|
254
254
|
children: [
|
|
255
255
|
/* @__PURE__ */ _(m, { className: "h-4 w-4" }),
|
|
256
256
|
" ",
|
|
@@ -262,7 +262,7 @@ function O() {
|
|
|
262
262
|
}),
|
|
263
263
|
R.tags.length > 0 && /* @__PURE__ */ v("div", {
|
|
264
264
|
className: "mt-3 flex items-center gap-2",
|
|
265
|
-
children: [/* @__PURE__ */ _(p, { className: "h-4 w-4 text-
|
|
265
|
+
children: [/* @__PURE__ */ _(p, { className: "h-4 w-4 text-text-secondary" }), R.tags.map((e) => /* @__PURE__ */ _(y, {
|
|
266
266
|
variant: "outline",
|
|
267
267
|
className: "text-xs",
|
|
268
268
|
children: e
|
|
@@ -291,7 +291,7 @@ function O() {
|
|
|
291
291
|
}
|
|
292
292
|
].map(({ key: e, label: t }) => /* @__PURE__ */ _("button", {
|
|
293
293
|
onClick: () => I(e),
|
|
294
|
-
className: `border-b-2 px-4 py-2 text-sm font-medium transition-colors ${F === e ? "border-primary text-primary" : "border-transparent text-
|
|
294
|
+
className: `border-b-2 px-4 py-2 text-sm font-medium transition-colors ${F === e ? "border-primary text-primary" : "border-transparent text-text-secondary hover:text-text-primary"}`,
|
|
295
295
|
children: t
|
|
296
296
|
}, e))
|
|
297
297
|
}),
|
|
@@ -306,7 +306,7 @@ function O() {
|
|
|
306
306
|
/* @__PURE__ */ v("div", {
|
|
307
307
|
className: "flex justify-between text-sm",
|
|
308
308
|
children: [/* @__PURE__ */ _("span", {
|
|
309
|
-
className: "text-
|
|
309
|
+
className: "text-text-secondary",
|
|
310
310
|
children: "Total Budget"
|
|
311
311
|
}), /* @__PURE__ */ v("span", {
|
|
312
312
|
className: "font-medium",
|
|
@@ -316,7 +316,7 @@ function O() {
|
|
|
316
316
|
/* @__PURE__ */ v("div", {
|
|
317
317
|
className: "flex justify-between text-sm",
|
|
318
318
|
children: [/* @__PURE__ */ _("span", {
|
|
319
|
-
className: "text-
|
|
319
|
+
className: "text-text-secondary",
|
|
320
320
|
children: "Spent"
|
|
321
321
|
}), /* @__PURE__ */ v("span", {
|
|
322
322
|
className: "font-medium",
|
|
@@ -326,7 +326,7 @@ function O() {
|
|
|
326
326
|
/* @__PURE__ */ v("div", {
|
|
327
327
|
className: "flex justify-between text-sm",
|
|
328
328
|
children: [/* @__PURE__ */ _("span", {
|
|
329
|
-
className: "text-
|
|
329
|
+
className: "text-text-secondary",
|
|
330
330
|
children: "Remaining"
|
|
331
331
|
}), /* @__PURE__ */ v("span", {
|
|
332
332
|
className: "font-medium text-green-600",
|
|
@@ -334,9 +334,9 @@ function O() {
|
|
|
334
334
|
})]
|
|
335
335
|
}),
|
|
336
336
|
/* @__PURE__ */ _("div", {
|
|
337
|
-
className: "h-2 overflow-hidden rounded-full bg-
|
|
337
|
+
className: "h-2 overflow-hidden rounded-full bg-bg-sunken",
|
|
338
338
|
children: /* @__PURE__ */ _("div", {
|
|
339
|
-
className: "h-2 rounded-full bg-primary",
|
|
339
|
+
className: "h-2 rounded-full bg-action-primary-bg",
|
|
340
340
|
style: { width: `${Math.round(R.spent / R.budget * 100)}%` }
|
|
341
341
|
})
|
|
342
342
|
})
|
|
@@ -356,7 +356,7 @@ function O() {
|
|
|
356
356
|
return /* @__PURE__ */ v("div", {
|
|
357
357
|
className: "flex items-center justify-between text-sm",
|
|
358
358
|
children: [/* @__PURE__ */ _("span", {
|
|
359
|
-
className: "text-
|
|
359
|
+
className: "text-text-secondary capitalize",
|
|
360
360
|
children: e.replace("_", " ")
|
|
361
361
|
}), /* @__PURE__ */ _("span", {
|
|
362
362
|
className: "font-medium",
|
|
@@ -373,7 +373,7 @@ function O() {
|
|
|
373
373
|
children: R.milestones.map((e, t) => /* @__PURE__ */ v("div", {
|
|
374
374
|
className: "relative flex gap-4 pb-8",
|
|
375
375
|
children: [
|
|
376
|
-
t !== R.milestones.length - 1 && /* @__PURE__ */ _("div", { className: "absolute left-[11px] top-6 h-full w-0.5 bg-
|
|
376
|
+
t !== R.milestones.length - 1 && /* @__PURE__ */ _("div", { className: "absolute left-[11px] top-6 h-full w-0.5 bg-bg-sunken" }),
|
|
377
377
|
/* @__PURE__ */ v("div", {
|
|
378
378
|
className: `relative z-10 mt-1 h-6 w-6 shrink-0 rounded-full ${{
|
|
379
379
|
completed: "bg-green-500",
|
|
@@ -398,11 +398,11 @@ function O() {
|
|
|
398
398
|
})]
|
|
399
399
|
}),
|
|
400
400
|
/* @__PURE__ */ _("p", {
|
|
401
|
-
className: "mt-1 text-sm text-
|
|
401
|
+
className: "mt-1 text-sm text-text-secondary",
|
|
402
402
|
children: e.description
|
|
403
403
|
}),
|
|
404
404
|
/* @__PURE__ */ v("div", {
|
|
405
|
-
className: "mt-2 flex flex-wrap gap-3 text-xs text-
|
|
405
|
+
className: "mt-2 flex flex-wrap gap-3 text-xs text-text-secondary",
|
|
406
406
|
children: [/* @__PURE__ */ v("span", {
|
|
407
407
|
className: "flex items-center gap-1",
|
|
408
408
|
children: [
|
|
@@ -457,13 +457,13 @@ function O() {
|
|
|
457
457
|
})]
|
|
458
458
|
}),
|
|
459
459
|
/* @__PURE__ */ _("p", {
|
|
460
|
-
className: "mt-1 text-sm text-
|
|
460
|
+
className: "mt-1 text-sm text-text-secondary",
|
|
461
461
|
children: e.description
|
|
462
462
|
}),
|
|
463
463
|
e.deliverables.length > 0 && /* @__PURE__ */ v("div", {
|
|
464
464
|
className: "mt-3",
|
|
465
465
|
children: [/* @__PURE__ */ _("p", {
|
|
466
|
-
className: "text-xs font-medium text-
|
|
466
|
+
className: "text-xs font-medium text-text-secondary",
|
|
467
467
|
children: "Deliverables"
|
|
468
468
|
}), /* @__PURE__ */ _("div", {
|
|
469
469
|
className: "mt-1 flex flex-wrap gap-1",
|
|
@@ -475,7 +475,7 @@ function O() {
|
|
|
475
475
|
})]
|
|
476
476
|
}),
|
|
477
477
|
/* @__PURE__ */ v("div", {
|
|
478
|
-
className: "mt-3 flex flex-wrap gap-4 text-xs text-
|
|
478
|
+
className: "mt-3 flex flex-wrap gap-4 text-xs text-text-secondary",
|
|
479
479
|
children: [
|
|
480
480
|
/* @__PURE__ */ v("span", {
|
|
481
481
|
className: "flex items-center gap-1",
|
|
@@ -506,7 +506,7 @@ function O() {
|
|
|
506
506
|
]
|
|
507
507
|
}),
|
|
508
508
|
e.notes && /* @__PURE__ */ _("p", {
|
|
509
|
-
className: "mt-2 rounded bg-
|
|
509
|
+
className: "mt-2 rounded bg-bg-sunken/50 p-2 text-xs text-text-secondary italic",
|
|
510
510
|
children: e.notes
|
|
511
511
|
})
|
|
512
512
|
]
|
|
@@ -522,19 +522,19 @@ function O() {
|
|
|
522
522
|
children: R.teamMembers.map((e, t) => /* @__PURE__ */ v("div", {
|
|
523
523
|
className: "flex items-center gap-3 rounded-lg border p-3",
|
|
524
524
|
children: [/* @__PURE__ */ _("div", {
|
|
525
|
-
className: "flex h-10 w-10 items-center justify-center rounded-full bg-primary/10 font-semibold text-primary",
|
|
525
|
+
className: "flex h-10 w-10 items-center justify-center rounded-full bg-action-primary-bg/10 font-semibold text-primary",
|
|
526
526
|
children: e.name.charAt(0)
|
|
527
527
|
}), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
|
|
528
528
|
className: "font-medium",
|
|
529
529
|
children: e.name
|
|
530
530
|
}), /* @__PURE__ */ _("p", {
|
|
531
|
-
className: "text-sm text-
|
|
531
|
+
className: "text-sm text-text-secondary",
|
|
532
532
|
children: e.role
|
|
533
533
|
})] })]
|
|
534
534
|
}, t))
|
|
535
535
|
}) })] })
|
|
536
536
|
] }) : /* @__PURE__ */ _(x, { children: /* @__PURE__ */ _(S, {
|
|
537
|
-
className: "py-12 text-center text-
|
|
537
|
+
className: "py-12 text-center text-text-secondary",
|
|
538
538
|
children: "Select a project from the sidebar to view details."
|
|
539
539
|
}) })
|
|
540
540
|
})]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectsPage.js","names":[],"sources":["../../../src/proserviceworld/pages/ProjectsPage.tsx"],"sourcesContent":["import { useState, useMemo } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Input,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Search,\n Plus,\n Calendar,\n DollarSign,\n Users,\n CheckCircle2,\n Clock,\n AlertTriangle,\n Target,\n Tag,\n ChevronRight,\n BarChart3,\n Flag,\n Milestone as MilestoneIcon,\n} from 'lucide-react';\nimport { useProServiceWorldStore } from '../store/proserviceworldStore';\nimport type { ProjectStatus, Priority } from '../types';\n\ntype DetailTab = 'overview' | 'timeline' | 'milestones' | 'team';\n\nconst statusConfig: Record<ProjectStatus, { label: string; color: string; bgColor: string }> = {\n planning: {\n label: 'Planning',\n color: 'text-yellow-600',\n bgColor: 'bg-yellow-100 dark:bg-yellow-950',\n },\n active: { label: 'Active', color: 'text-blue-600', bgColor: 'bg-blue-100 dark:bg-blue-950' },\n on_hold: {\n label: 'On Hold',\n color: 'text-orange-600',\n bgColor: 'bg-orange-100 dark:bg-orange-950',\n },\n completed: {\n label: 'Completed',\n color: 'text-green-600',\n bgColor: 'bg-green-100 dark:bg-green-950',\n },\n cancelled: { label: 'Cancelled', color: 'text-red-600', bgColor: 'bg-red-100 dark:bg-red-950' },\n};\n\nconst priorityConfig: Record<\n Priority,\n { label: string; variant: 'default' | 'secondary' | 'outline' | 'destructive' }\n> = {\n low: { label: 'Low', variant: 'outline' },\n medium: { label: 'Medium', variant: 'secondary' },\n high: { label: 'High', variant: 'default' },\n critical: { label: 'Critical', variant: 'destructive' },\n};\n\nexport function ProjectsPage() {\n const { projects } = useProServiceWorldStore();\n const [searchQuery, setSearchQuery] = useState('');\n const [statusFilter, setStatusFilter] = useState<ProjectStatus | 'all'>('all');\n const [selectedProjectId, setSelectedProjectId] = useState<string>(projects[0]?.id ?? '');\n const [detailTab, setDetailTab] = useState<DetailTab>('overview');\n\n const filteredProjects = useMemo(() => {\n let result = [...projects];\n if (statusFilter !== 'all') result = result.filter((p) => p.status === statusFilter);\n if (searchQuery.trim()) {\n const q = searchQuery.toLowerCase();\n result = result.filter(\n (p) => p.title.toLowerCase().includes(q) || p.category.toLowerCase().includes(q)\n );\n }\n return result;\n }, [projects, statusFilter, searchQuery]);\n\n const selectedProject = projects.find((p) => p.id === selectedProjectId);\n\n const detailTabs: { key: DetailTab; label: string }[] = [\n { key: 'overview', label: 'Overview' },\n { key: 'timeline', label: 'Timeline' },\n { key: 'milestones', label: 'Milestones' },\n { key: 'team', label: 'Team' },\n ];\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Projects</h1>\n <p className=\"text-sm text-muted-foreground\">\n Track progress and manage your professional service projects.\n </p>\n </div>\n <Button size=\"sm\">\n <Plus className=\"mr-2 h-4 w-4\" /> New Project\n </Button>\n </div>\n\n <div className=\"grid gap-6 lg:grid-cols-3\">\n {/* Project sidebar */}\n <div className=\"space-y-4\">\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground\" />\n <Input\n placeholder=\"Search projects...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"pl-10\"\n />\n </div>\n\n <div className=\"flex flex-wrap gap-1\">\n {(['all', 'active', 'planning', 'completed', 'on_hold', 'cancelled'] as const).map(\n (status) => (\n <button\n key={status}\n onClick={() => setStatusFilter(status)}\n className={`rounded px-2 py-1 text-xs font-medium transition-colors ${\n statusFilter === status\n ? 'bg-primary text-primary-foreground'\n : 'bg-muted text-muted-foreground hover:bg-muted/80'\n }`}\n >\n {status === 'all' ? 'All' : statusConfig[status].label}\n </button>\n )\n )}\n </div>\n\n <div className=\"space-y-2\">\n {filteredProjects.length === 0 ? (\n <p className=\"py-8 text-center text-sm text-muted-foreground\">No projects found.</p>\n ) : (\n filteredProjects.map((project) => {\n const cfg = statusConfig[project.status];\n return (\n <button\n key={project.id}\n onClick={() => {\n setSelectedProjectId(project.id);\n setDetailTab('overview');\n }}\n className={`w-full rounded-lg border p-3 text-left transition-colors ${\n selectedProjectId === project.id\n ? 'border-primary bg-primary/5'\n : 'hover:bg-muted/50'\n }`}\n >\n <div className=\"flex items-center justify-between\">\n <h3 className=\"font-medium text-sm truncate pr-2\">{project.title}</h3>\n <ChevronRight className=\"h-4 w-4 shrink-0 text-muted-foreground\" />\n </div>\n <div className=\"mt-2 flex items-center gap-2\">\n <Badge variant={priorityConfig[project.priority].variant} className=\"text-xs\">\n {project.priority}\n </Badge>\n <span className={`text-xs ${cfg.color}`}>{cfg.label}</span>\n </div>\n <div className=\"mt-2\">\n <div className=\"flex items-center justify-between text-xs text-muted-foreground\">\n <span>Progress</span>\n <span>{project.progress}%</span>\n </div>\n <div className=\"mt-1 h-1.5 overflow-hidden rounded-full bg-muted\">\n <div\n className=\"h-1.5 rounded-full bg-primary transition-all\"\n style={{ width: `${project.progress}%` }}\n />\n </div>\n </div>\n </button>\n );\n })\n )}\n </div>\n </div>\n\n {/* Detail panel */}\n <div className=\"space-y-4 lg:col-span-2\">\n {selectedProject ? (\n <>\n {/* Project header card */}\n <Card>\n <CardContent className=\"pt-6\">\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2\">\n <Badge variant={priorityConfig[selectedProject.priority].variant}>\n {selectedProject.priority} priority\n </Badge>\n <span\n className={`text-sm font-medium ${statusConfig[selectedProject.status].color}`}\n >\n {statusConfig[selectedProject.status].label}\n </span>\n </div>\n <h2 className=\"text-xl font-bold\">{selectedProject.title}</h2>\n <p className=\"text-sm text-muted-foreground\">{selectedProject.description}</p>\n </div>\n <div className=\"text-right\">\n <p className=\"text-sm text-muted-foreground\">Budget</p>\n <p className=\"text-xl font-bold\">\n ${selectedProject.budget.toLocaleString()}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n Spent: ${selectedProject.spent.toLocaleString()} (\n {Math.round((selectedProject.spent / selectedProject.budget) * 100)}%)\n </p>\n </div>\n </div>\n\n {/* Progress bar */}\n <div className=\"mt-4\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-muted-foreground\">Overall Progress</span>\n <span className=\"font-medium\">{selectedProject.progress}%</span>\n </div>\n <div className=\"mt-1 h-3 overflow-hidden rounded-full bg-muted\">\n <div\n className=\"h-3 rounded-full bg-primary transition-all\"\n style={{ width: `${selectedProject.progress}%` }}\n />\n </div>\n </div>\n\n {/* Quick stats */}\n <div className=\"mt-4 flex flex-wrap gap-4 text-sm\">\n <span className=\"flex items-center gap-1 text-muted-foreground\">\n <Calendar className=\"h-4 w-4\" /> {selectedProject.startDate} -{' '}\n {selectedProject.endDate}\n </span>\n <span className=\"flex items-center gap-1 text-muted-foreground\">\n <Users className=\"h-4 w-4\" /> {selectedProject.teamMembers.length} members\n </span>\n <span className=\"flex items-center gap-1 text-muted-foreground\">\n <Target className=\"h-4 w-4\" /> {selectedProject.milestones.length} milestones\n </span>\n </div>\n\n {/* Tags */}\n {selectedProject.tags.length > 0 && (\n <div className=\"mt-3 flex items-center gap-2\">\n <Tag className=\"h-4 w-4 text-muted-foreground\" />\n {selectedProject.tags.map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Detail tabs */}\n <div className=\"flex gap-1 border-b\">\n {detailTabs.map(({ key, label }) => (\n <button\n key={key}\n onClick={() => setDetailTab(key)}\n className={`border-b-2 px-4 py-2 text-sm font-medium transition-colors ${\n detailTab === key\n ? 'border-primary text-primary'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n >\n {label}\n </button>\n ))}\n </div>\n\n {/* Overview tab */}\n {detailTab === 'overview' && (\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base flex items-center gap-2\">\n <DollarSign className=\"h-4 w-4\" /> Budget Overview\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-muted-foreground\">Total Budget</span>\n <span className=\"font-medium\">\n ${selectedProject.budget.toLocaleString()}\n </span>\n </div>\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-muted-foreground\">Spent</span>\n <span className=\"font-medium\">\n ${selectedProject.spent.toLocaleString()}\n </span>\n </div>\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-muted-foreground\">Remaining</span>\n <span className=\"font-medium text-green-600\">\n ${(selectedProject.budget - selectedProject.spent).toLocaleString()}\n </span>\n </div>\n <div className=\"h-2 overflow-hidden rounded-full bg-muted\">\n <div\n className=\"h-2 rounded-full bg-primary\"\n style={{\n width: `${Math.round((selectedProject.spent / selectedProject.budget) * 100)}%`,\n }}\n />\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4\" /> Milestone Summary\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {['completed', 'in_progress', 'pending', 'overdue'].map((status) => {\n const count = selectedProject.milestones.filter(\n (m) => m.status === status\n ).length;\n return (\n <div key={status} className=\"flex items-center justify-between text-sm\">\n <span className=\"text-muted-foreground capitalize\">\n {status.replace('_', ' ')}\n </span>\n <span className=\"font-medium\">{count}</span>\n </div>\n );\n })}\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Timeline tab */}\n {detailTab === 'timeline' && (\n <Card>\n <CardContent className=\"pt-6\">\n <div className=\"relative space-y-0\">\n {selectedProject.milestones.map((milestone, idx) => {\n const isLast = idx === selectedProject.milestones.length - 1;\n const statusColors: Record<string, string> = {\n completed: 'bg-green-500',\n in_progress: 'bg-blue-500',\n pending: 'bg-gray-300',\n overdue: 'bg-red-500',\n };\n return (\n <div key={milestone.id} className=\"relative flex gap-4 pb-8\">\n {/* Timeline line */}\n {!isLast && (\n <div className=\"absolute left-[11px] top-6 h-full w-0.5 bg-muted\" />\n )}\n {/* Dot */}\n <div\n className={`relative z-10 mt-1 h-6 w-6 shrink-0 rounded-full ${statusColors[milestone.status]} flex items-center justify-center`}\n >\n {milestone.status === 'completed' && (\n <CheckCircle2 className=\"h-4 w-4 text-white\" />\n )}\n {milestone.status === 'in_progress' && (\n <Clock className=\"h-4 w-4 text-white\" />\n )}\n </div>\n {/* Content */}\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2\">\n <h4 className=\"font-medium\">{milestone.title}</h4>\n <Badge\n variant={priorityConfig[milestone.priority].variant}\n className=\"text-xs\"\n >\n {milestone.priority}\n </Badge>\n </div>\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {milestone.description}\n </p>\n <div className=\"mt-2 flex flex-wrap gap-3 text-xs text-muted-foreground\">\n <span className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" /> Due: {milestone.dueDate}\n </span>\n {milestone.completedAt && (\n <span className=\"flex items-center gap-1 text-green-600\">\n <CheckCircle2 className=\"h-3 w-3\" /> Completed:{' '}\n {milestone.completedAt}\n </span>\n )}\n </div>\n </div>\n </div>\n );\n })}\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Milestones tab */}\n {detailTab === 'milestones' && (\n <div className=\"space-y-3\">\n {selectedProject.milestones.map((milestone) => {\n const statusIcons: Record<string, typeof CheckCircle2> = {\n completed: CheckCircle2,\n in_progress: Clock,\n pending: Target,\n overdue: AlertTriangle,\n };\n const statusColors: Record<string, string> = {\n completed: 'text-green-600',\n in_progress: 'text-blue-600',\n pending: 'text-gray-400',\n overdue: 'text-red-600',\n };\n const Icon = statusIcons[milestone.status] ?? Target;\n return (\n <Card key={milestone.id}>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-start gap-3\">\n <Icon className={`mt-0.5 h-5 w-5 ${statusColors[milestone.status]}`} />\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2\">\n <h4 className=\"font-medium\">{milestone.title}</h4>\n <Badge\n variant={priorityConfig[milestone.priority].variant}\n className=\"text-xs\"\n >\n {milestone.priority}\n </Badge>\n </div>\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {milestone.description}\n </p>\n\n {/* Deliverables */}\n {milestone.deliverables.length > 0 && (\n <div className=\"mt-3\">\n <p className=\"text-xs font-medium text-muted-foreground\">\n Deliverables\n </p>\n <div className=\"mt-1 flex flex-wrap gap-1\">\n {milestone.deliverables.map((d) => (\n <Badge key={d} variant=\"outline\" className=\"text-xs\">\n {d}\n </Badge>\n ))}\n </div>\n </div>\n )}\n\n <div className=\"mt-3 flex flex-wrap gap-4 text-xs text-muted-foreground\">\n <span className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" /> Due: {milestone.dueDate}\n </span>\n {milestone.completedAt && (\n <span className=\"flex items-center gap-1 text-green-600\">\n <CheckCircle2 className=\"h-3 w-3\" /> {milestone.completedAt}\n </span>\n )}\n {milestone.dependencies.length > 0 && (\n <span className=\"flex items-center gap-1\">\n <Flag className=\"h-3 w-3\" /> {milestone.dependencies.length}{' '}\n dependencies\n </span>\n )}\n </div>\n\n {milestone.notes && (\n <p className=\"mt-2 rounded bg-muted/50 p-2 text-xs text-muted-foreground italic\">\n {milestone.notes}\n </p>\n )}\n </div>\n </div>\n </CardContent>\n </Card>\n );\n })}\n </div>\n )}\n\n {/* Team tab */}\n {detailTab === 'team' && (\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Team Members</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {selectedProject.teamMembers.map((member, idx) => (\n <div key={idx} className=\"flex items-center gap-3 rounded-lg border p-3\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-full bg-primary/10 font-semibold text-primary\">\n {member.name.charAt(0)}\n </div>\n <div>\n <p className=\"font-medium\">{member.name}</p>\n <p className=\"text-sm text-muted-foreground\">{member.role}</p>\n </div>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n )}\n </>\n ) : (\n <Card>\n <CardContent className=\"py-12 text-center text-muted-foreground\">\n Select a project from the sidebar to view details.\n </CardContent>\n </Card>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProjectsPage.js","names":[],"sources":["../../../src/proserviceworld/pages/ProjectsPage.tsx"],"sourcesContent":["import { useState, useMemo } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Input,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Search,\n Plus,\n Calendar,\n DollarSign,\n Users,\n CheckCircle2,\n Clock,\n AlertTriangle,\n Target,\n Tag,\n ChevronRight,\n BarChart3,\n Flag,\n Milestone as MilestoneIcon,\n} from 'lucide-react';\nimport { useProServiceWorldStore } from '../store/proserviceworldStore';\nimport type { ProjectStatus, Priority } from '../types';\n\ntype DetailTab = 'overview' | 'timeline' | 'milestones' | 'team';\n\nconst statusConfig: Record<ProjectStatus, { label: string; color: string; bgColor: string }> = {\n planning: {\n label: 'Planning',\n color: 'text-yellow-600',\n bgColor: 'bg-yellow-100 dark:bg-yellow-950',\n },\n active: { label: 'Active', color: 'text-blue-600', bgColor: 'bg-blue-100 dark:bg-blue-950' },\n on_hold: {\n label: 'On Hold',\n color: 'text-orange-600',\n bgColor: 'bg-orange-100 dark:bg-orange-950',\n },\n completed: {\n label: 'Completed',\n color: 'text-green-600',\n bgColor: 'bg-green-100 dark:bg-green-950',\n },\n cancelled: { label: 'Cancelled', color: 'text-red-600', bgColor: 'bg-red-100 dark:bg-red-950' },\n};\n\nconst priorityConfig: Record<\n Priority,\n { label: string; variant: 'default' | 'secondary' | 'outline' | 'destructive' }\n> = {\n low: { label: 'Low', variant: 'outline' },\n medium: { label: 'Medium', variant: 'secondary' },\n high: { label: 'High', variant: 'default' },\n critical: { label: 'Critical', variant: 'destructive' },\n};\n\nexport function ProjectsPage() {\n const { projects } = useProServiceWorldStore();\n const [searchQuery, setSearchQuery] = useState('');\n const [statusFilter, setStatusFilter] = useState<ProjectStatus | 'all'>('all');\n const [selectedProjectId, setSelectedProjectId] = useState<string>(projects[0]?.id ?? '');\n const [detailTab, setDetailTab] = useState<DetailTab>('overview');\n\n const filteredProjects = useMemo(() => {\n let result = [...projects];\n if (statusFilter !== 'all') result = result.filter((p) => p.status === statusFilter);\n if (searchQuery.trim()) {\n const q = searchQuery.toLowerCase();\n result = result.filter(\n (p) => p.title.toLowerCase().includes(q) || p.category.toLowerCase().includes(q)\n );\n }\n return result;\n }, [projects, statusFilter, searchQuery]);\n\n const selectedProject = projects.find((p) => p.id === selectedProjectId);\n\n const detailTabs: { key: DetailTab; label: string }[] = [\n { key: 'overview', label: 'Overview' },\n { key: 'timeline', label: 'Timeline' },\n { key: 'milestones', label: 'Milestones' },\n { key: 'team', label: 'Team' },\n ];\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Projects</h1>\n <p className=\"text-sm text-text-secondary\">\n Track progress and manage your professional service projects.\n </p>\n </div>\n <Button size=\"sm\">\n <Plus className=\"mr-2 h-4 w-4\" /> New Project\n </Button>\n </div>\n\n <div className=\"grid gap-6 lg:grid-cols-3\">\n {/* Project sidebar */}\n <div className=\"space-y-4\">\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n placeholder=\"Search projects...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"pl-10\"\n />\n </div>\n\n <div className=\"flex flex-wrap gap-1\">\n {(['all', 'active', 'planning', 'completed', 'on_hold', 'cancelled'] as const).map(\n (status) => (\n <button\n key={status}\n onClick={() => setStatusFilter(status)}\n className={`rounded px-2 py-1 text-xs font-medium transition-colors ${\n statusFilter === status\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-secondary hover:bg-bg-sunken/80'\n }`}\n >\n {status === 'all' ? 'All' : statusConfig[status].label}\n </button>\n )\n )}\n </div>\n\n <div className=\"space-y-2\">\n {filteredProjects.length === 0 ? (\n <p className=\"py-8 text-center text-sm text-text-secondary\">No projects found.</p>\n ) : (\n filteredProjects.map((project) => {\n const cfg = statusConfig[project.status];\n return (\n <button\n key={project.id}\n onClick={() => {\n setSelectedProjectId(project.id);\n setDetailTab('overview');\n }}\n className={`w-full rounded-lg border p-3 text-left transition-colors ${\n selectedProjectId === project.id\n ? 'border-primary bg-action-primary-bg/5'\n : 'hover:bg-bg-sunken/50'\n }`}\n >\n <div className=\"flex items-center justify-between\">\n <h3 className=\"font-medium text-sm truncate pr-2\">{project.title}</h3>\n <ChevronRight className=\"h-4 w-4 shrink-0 text-text-secondary\" />\n </div>\n <div className=\"mt-2 flex items-center gap-2\">\n <Badge variant={priorityConfig[project.priority].variant} className=\"text-xs\">\n {project.priority}\n </Badge>\n <span className={`text-xs ${cfg.color}`}>{cfg.label}</span>\n </div>\n <div className=\"mt-2\">\n <div className=\"flex items-center justify-between text-xs text-text-secondary\">\n <span>Progress</span>\n <span>{project.progress}%</span>\n </div>\n <div className=\"mt-1 h-1.5 overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-1.5 rounded-full bg-action-primary-bg transition-all\"\n style={{ width: `${project.progress}%` }}\n />\n </div>\n </div>\n </button>\n );\n })\n )}\n </div>\n </div>\n\n {/* Detail panel */}\n <div className=\"space-y-4 lg:col-span-2\">\n {selectedProject ? (\n <>\n {/* Project header card */}\n <Card>\n <CardContent className=\"pt-6\">\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2\">\n <Badge variant={priorityConfig[selectedProject.priority].variant}>\n {selectedProject.priority} priority\n </Badge>\n <span\n className={`text-sm font-medium ${statusConfig[selectedProject.status].color}`}\n >\n {statusConfig[selectedProject.status].label}\n </span>\n </div>\n <h2 className=\"text-xl font-bold\">{selectedProject.title}</h2>\n <p className=\"text-sm text-text-secondary\">{selectedProject.description}</p>\n </div>\n <div className=\"text-right\">\n <p className=\"text-sm text-text-secondary\">Budget</p>\n <p className=\"text-xl font-bold\">\n ${selectedProject.budget.toLocaleString()}\n </p>\n <p className=\"text-xs text-text-secondary\">\n Spent: ${selectedProject.spent.toLocaleString()} (\n {Math.round((selectedProject.spent / selectedProject.budget) * 100)}%)\n </p>\n </div>\n </div>\n\n {/* Progress bar */}\n <div className=\"mt-4\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">Overall Progress</span>\n <span className=\"font-medium\">{selectedProject.progress}%</span>\n </div>\n <div className=\"mt-1 h-3 overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-3 rounded-full bg-action-primary-bg transition-all\"\n style={{ width: `${selectedProject.progress}%` }}\n />\n </div>\n </div>\n\n {/* Quick stats */}\n <div className=\"mt-4 flex flex-wrap gap-4 text-sm\">\n <span className=\"flex items-center gap-1 text-text-secondary\">\n <Calendar className=\"h-4 w-4\" /> {selectedProject.startDate} -{' '}\n {selectedProject.endDate}\n </span>\n <span className=\"flex items-center gap-1 text-text-secondary\">\n <Users className=\"h-4 w-4\" /> {selectedProject.teamMembers.length} members\n </span>\n <span className=\"flex items-center gap-1 text-text-secondary\">\n <Target className=\"h-4 w-4\" /> {selectedProject.milestones.length} milestones\n </span>\n </div>\n\n {/* Tags */}\n {selectedProject.tags.length > 0 && (\n <div className=\"mt-3 flex items-center gap-2\">\n <Tag className=\"h-4 w-4 text-text-secondary\" />\n {selectedProject.tags.map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n </div>\n )}\n </CardContent>\n </Card>\n\n {/* Detail tabs */}\n <div className=\"flex gap-1 border-b\">\n {detailTabs.map(({ key, label }) => (\n <button\n key={key}\n onClick={() => setDetailTab(key)}\n className={`border-b-2 px-4 py-2 text-sm font-medium transition-colors ${\n detailTab === key\n ? 'border-primary text-primary'\n : 'border-transparent text-text-secondary hover:text-text-primary'\n }`}\n >\n {label}\n </button>\n ))}\n </div>\n\n {/* Overview tab */}\n {detailTab === 'overview' && (\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base flex items-center gap-2\">\n <DollarSign className=\"h-4 w-4\" /> Budget Overview\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">Total Budget</span>\n <span className=\"font-medium\">\n ${selectedProject.budget.toLocaleString()}\n </span>\n </div>\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">Spent</span>\n <span className=\"font-medium\">\n ${selectedProject.spent.toLocaleString()}\n </span>\n </div>\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">Remaining</span>\n <span className=\"font-medium text-green-600\">\n ${(selectedProject.budget - selectedProject.spent).toLocaleString()}\n </span>\n </div>\n <div className=\"h-2 overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-2 rounded-full bg-action-primary-bg\"\n style={{\n width: `${Math.round((selectedProject.spent / selectedProject.budget) * 100)}%`,\n }}\n />\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4\" /> Milestone Summary\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {['completed', 'in_progress', 'pending', 'overdue'].map((status) => {\n const count = selectedProject.milestones.filter(\n (m) => m.status === status\n ).length;\n return (\n <div key={status} className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary capitalize\">\n {status.replace('_', ' ')}\n </span>\n <span className=\"font-medium\">{count}</span>\n </div>\n );\n })}\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Timeline tab */}\n {detailTab === 'timeline' && (\n <Card>\n <CardContent className=\"pt-6\">\n <div className=\"relative space-y-0\">\n {selectedProject.milestones.map((milestone, idx) => {\n const isLast = idx === selectedProject.milestones.length - 1;\n const statusColors: Record<string, string> = {\n completed: 'bg-green-500',\n in_progress: 'bg-blue-500',\n pending: 'bg-gray-300',\n overdue: 'bg-red-500',\n };\n return (\n <div key={milestone.id} className=\"relative flex gap-4 pb-8\">\n {/* Timeline line */}\n {!isLast && (\n <div className=\"absolute left-[11px] top-6 h-full w-0.5 bg-bg-sunken\" />\n )}\n {/* Dot */}\n <div\n className={`relative z-10 mt-1 h-6 w-6 shrink-0 rounded-full ${statusColors[milestone.status]} flex items-center justify-center`}\n >\n {milestone.status === 'completed' && (\n <CheckCircle2 className=\"h-4 w-4 text-white\" />\n )}\n {milestone.status === 'in_progress' && (\n <Clock className=\"h-4 w-4 text-white\" />\n )}\n </div>\n {/* Content */}\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2\">\n <h4 className=\"font-medium\">{milestone.title}</h4>\n <Badge\n variant={priorityConfig[milestone.priority].variant}\n className=\"text-xs\"\n >\n {milestone.priority}\n </Badge>\n </div>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {milestone.description}\n </p>\n <div className=\"mt-2 flex flex-wrap gap-3 text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" /> Due: {milestone.dueDate}\n </span>\n {milestone.completedAt && (\n <span className=\"flex items-center gap-1 text-green-600\">\n <CheckCircle2 className=\"h-3 w-3\" /> Completed:{' '}\n {milestone.completedAt}\n </span>\n )}\n </div>\n </div>\n </div>\n );\n })}\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Milestones tab */}\n {detailTab === 'milestones' && (\n <div className=\"space-y-3\">\n {selectedProject.milestones.map((milestone) => {\n const statusIcons: Record<string, typeof CheckCircle2> = {\n completed: CheckCircle2,\n in_progress: Clock,\n pending: Target,\n overdue: AlertTriangle,\n };\n const statusColors: Record<string, string> = {\n completed: 'text-green-600',\n in_progress: 'text-blue-600',\n pending: 'text-gray-400',\n overdue: 'text-red-600',\n };\n const Icon = statusIcons[milestone.status] ?? Target;\n return (\n <Card key={milestone.id}>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-start gap-3\">\n <Icon className={`mt-0.5 h-5 w-5 ${statusColors[milestone.status]}`} />\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-2\">\n <h4 className=\"font-medium\">{milestone.title}</h4>\n <Badge\n variant={priorityConfig[milestone.priority].variant}\n className=\"text-xs\"\n >\n {milestone.priority}\n </Badge>\n </div>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {milestone.description}\n </p>\n\n {/* Deliverables */}\n {milestone.deliverables.length > 0 && (\n <div className=\"mt-3\">\n <p className=\"text-xs font-medium text-text-secondary\">\n Deliverables\n </p>\n <div className=\"mt-1 flex flex-wrap gap-1\">\n {milestone.deliverables.map((d) => (\n <Badge key={d} variant=\"outline\" className=\"text-xs\">\n {d}\n </Badge>\n ))}\n </div>\n </div>\n )}\n\n <div className=\"mt-3 flex flex-wrap gap-4 text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" /> Due: {milestone.dueDate}\n </span>\n {milestone.completedAt && (\n <span className=\"flex items-center gap-1 text-green-600\">\n <CheckCircle2 className=\"h-3 w-3\" /> {milestone.completedAt}\n </span>\n )}\n {milestone.dependencies.length > 0 && (\n <span className=\"flex items-center gap-1\">\n <Flag className=\"h-3 w-3\" /> {milestone.dependencies.length}{' '}\n dependencies\n </span>\n )}\n </div>\n\n {milestone.notes && (\n <p className=\"mt-2 rounded bg-bg-sunken/50 p-2 text-xs text-text-secondary italic\">\n {milestone.notes}\n </p>\n )}\n </div>\n </div>\n </CardContent>\n </Card>\n );\n })}\n </div>\n )}\n\n {/* Team tab */}\n {detailTab === 'team' && (\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Team Members</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {selectedProject.teamMembers.map((member, idx) => (\n <div key={idx} className=\"flex items-center gap-3 rounded-lg border p-3\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-full bg-action-primary-bg/10 font-semibold text-primary\">\n {member.name.charAt(0)}\n </div>\n <div>\n <p className=\"font-medium\">{member.name}</p>\n <p className=\"text-sm text-text-secondary\">{member.role}</p>\n </div>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n )}\n </>\n ) : (\n <Card>\n <CardContent className=\"py-12 text-center text-text-secondary\">\n Select a project from the sidebar to view details.\n </CardContent>\n </Card>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|