@burdenoff/microfe-crewfoundry 2026.704.4 → 2026.704.5
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/crewfoundry/pages/CommunicationsTeamsPagePage.js +1 -1
- package/dist/crewfoundry/pages/CommunicationsTeamsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardDashboardPagePage.js +4 -4
- package/dist/crewfoundry/pages/DashboardDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardEnhancedDashboardPagePage.js +4 -4
- package/dist/crewfoundry/pages/DashboardEnhancedDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringCreateJobPagePage.js +1 -1
- package/dist/crewfoundry/pages/HiringCreateJobPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js +4 -4
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobsPagePage.js +2 -2
- package/dist/crewfoundry/pages/HiringJobsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js +2 -1
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningAssessmentsPagePage.js +1 -1
- package/dist/crewfoundry/pages/LearningAssessmentsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCertificatesPagePage.js +1 -1
- package/dist/crewfoundry/pages/LearningCertificatesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js +3 -3
- package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js +2 -2
- package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js +1 -1
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPagePage.js +1 -1
- package/dist/crewfoundry/pages/LearningLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.js +3 -3
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js +2 -2
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js +1 -1
- package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.js +3 -3
- package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js +4 -4
- package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js +1 -1
- package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsCreateProjectPagePage.js +1 -1
- package/dist/crewfoundry/pages/ProjectsCreateProjectPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsProjectDetailsPagePage.js +4 -4
- package/dist/crewfoundry/pages/ProjectsProjectDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js +2 -2
- package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js +5 -5
- package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js +1 -1
- package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamSettingsPagePage.js +4 -4
- package/dist/crewfoundry/pages/TeamsTeamSettingsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamsPagePage.js +1 -1
- package/dist/crewfoundry/pages/TeamsTeamsPagePage.js.map +1 -1
- package/dist/crewfoundry/store/crewfoundryStore.js.map +1 -1
- package/package.json +1 -1
|
@@ -38,21 +38,21 @@ function v() {
|
|
|
38
38
|
label: "Message the team",
|
|
39
39
|
description: "Coordinate on scope, blockers, and next milestones.",
|
|
40
40
|
icon: /* @__PURE__ */ c(i, { className: "h-4 w-4" }),
|
|
41
|
-
onClick: () => v("
|
|
41
|
+
onClick: () => v("../marketplace/messaging-page")
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
44
|
id: "bookings",
|
|
45
45
|
label: "Review bookings",
|
|
46
46
|
description: "Check the engagements tied to this project.",
|
|
47
47
|
icon: /* @__PURE__ */ c(n, { className: "h-4 w-4" }),
|
|
48
|
-
onClick: () => v("
|
|
48
|
+
onClick: () => v("../marketplace/booking-management-page")
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
51
|
id: "earnings",
|
|
52
52
|
label: "Track earnings",
|
|
53
53
|
description: "See payouts and balances connected to your work.",
|
|
54
54
|
icon: /* @__PURE__ */ c(r, { className: "h-4 w-4" }),
|
|
55
|
-
onClick: () => v("
|
|
55
|
+
onClick: () => v("../marketplace/earnings-page")
|
|
56
56
|
}
|
|
57
57
|
]
|
|
58
58
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarketplaceProjectDetailsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceProjectDetailsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Card,\n CardContent,\n EmphasisPanel,\n GradientText,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { Calendar, DollarSign, MessageSquare, Users } from 'lucide-react';\nimport { useLocation, useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\n\nexport function MarketplaceProjectDetailsPagePage() {\n const navigate = useNavigate();\n const location = useLocation();\n const { projects } = useCrewFoundryStore();\n const selectedId = new URLSearchParams(location.search).get('id');\n const project = projects.find((p) => p.id === selectedId) ?? projects[0];\n if (!project)\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Project not found\"\n description=\"This project is no longer available.\"\n />\n </div>\n );\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{project.name}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">{project.description}</p>\n <PagePurpose className=\"mt-2\">\n The full picture of a marketplace project — scope, budget, timeline, and progress — so\n you can track delivery and stay aligned with the team doing the work.\n </PagePurpose>\n </div>\n <Badge variant={project.status === 'active' ? 'default' : 'secondary'}>\n {formatEnumLabel(project.status)}\n </Badge>\n </div>\n\n <NextSteps\n storageKey=\"crewfoundry-marketplace-project-details\"\n steps={[\n {\n id: 'message',\n label: 'Message the team',\n description: 'Coordinate on scope, blockers, and next milestones.',\n icon: <MessageSquare className=\"h-4 w-4\" />,\n onClick: () => navigate('
|
|
1
|
+
{"version":3,"file":"MarketplaceProjectDetailsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceProjectDetailsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Card,\n CardContent,\n EmphasisPanel,\n GradientText,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { Calendar, DollarSign, MessageSquare, Users } from 'lucide-react';\nimport { useLocation, useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\n\nexport function MarketplaceProjectDetailsPagePage() {\n const navigate = useNavigate();\n const location = useLocation();\n const { projects } = useCrewFoundryStore();\n const selectedId = new URLSearchParams(location.search).get('id');\n const project = projects.find((p) => p.id === selectedId) ?? projects[0];\n if (!project)\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Project not found\"\n description=\"This project is no longer available.\"\n />\n </div>\n );\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{project.name}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">{project.description}</p>\n <PagePurpose className=\"mt-2\">\n The full picture of a marketplace project — scope, budget, timeline, and progress — so\n you can track delivery and stay aligned with the team doing the work.\n </PagePurpose>\n </div>\n <Badge variant={project.status === 'active' ? 'default' : 'secondary'}>\n {formatEnumLabel(project.status)}\n </Badge>\n </div>\n\n <NextSteps\n storageKey=\"crewfoundry-marketplace-project-details\"\n steps={[\n {\n id: 'message',\n label: 'Message the team',\n description: 'Coordinate on scope, blockers, and next milestones.',\n icon: <MessageSquare className=\"h-4 w-4\" />,\n onClick: () => navigate('../marketplace/messaging-page'),\n },\n {\n id: 'bookings',\n label: 'Review bookings',\n description: 'Check the engagements tied to this project.',\n icon: <Calendar className=\"h-4 w-4\" />,\n onClick: () => navigate('../marketplace/booking-management-page'),\n },\n {\n id: 'earnings',\n label: 'Track earnings',\n description: 'See payouts and balances connected to your work.',\n icon: <DollarSign className=\"h-4 w-4\" />,\n onClick: () => navigate('../marketplace/earnings-page'),\n },\n ]}\n />\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n <Card>\n <CardContent className=\"p-4 text-center\">\n <DollarSign className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">${(project.budget / 1000).toFixed(0)}K</p>\n <p className=\"text-xs text-text-secondary\">Budget</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <DollarSign className=\"h-5 w-5 mx-auto text-status-success-text mb-1\" />\n <p className=\"text-lg font-bold\">${(project.spent / 1000).toFixed(0)}K</p>\n <p className=\"text-xs text-text-secondary\">Spent</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Users className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{project.teamName}</p>\n <p className=\"text-xs text-text-secondary\">Team</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Calendar className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{project.endDate}</p>\n <p className=\"text-xs text-text-secondary\">Deadline</p>\n </CardContent>\n </Card>\n </div>\n <EmphasisPanel>\n <div className=\"mb-2 text-lg font-semibold\">Progress</div>\n <div className=\"space-y-2\">\n <div className=\"flex justify-between text-sm\">\n <span>\n {project.completedTasks} of {project.tasks} tasks\n </span>\n <span className=\"font-medium\">{project.progress}%</span>\n </div>\n <div className=\"h-3 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-3 bg-action-primary-bg rounded\"\n style={{ width: `${project.progress}%` }}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-4 mt-4\">\n <div>\n <p className=\"text-xs text-text-secondary\">Manager</p>\n <p className=\"text-sm font-medium\">{project.managerName}</p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Priority</p>\n <Badge\n variant={\n project.priority === 'critical'\n ? 'destructive'\n : project.priority === 'high'\n ? 'default'\n : 'secondary'\n }\n >\n {formatEnumLabel(project.priority)}\n </Badge>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Start Date</p>\n <p className=\"text-sm\">{project.startDate}</p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">End Date</p>\n <p className=\"text-sm\">{project.endDate}</p>\n </div>\n </div>\n </EmphasisPanel>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -28,7 +28,7 @@ function y() {
|
|
|
28
28
|
className: "flex gap-2",
|
|
29
29
|
children: [/* @__PURE__ */ l(d, {
|
|
30
30
|
variant: "outline",
|
|
31
|
-
onClick: () => y("
|
|
31
|
+
onClick: () => y("../marketplace/messaging-page"),
|
|
32
32
|
children: [/* @__PURE__ */ c(r, { className: "h-4 w-4 mr-1" }), "Contact"]
|
|
33
33
|
}), /* @__PURE__ */ c(d, {
|
|
34
34
|
disabled: !0,
|
|
@@ -46,21 +46,21 @@ function y() {
|
|
|
46
46
|
label: "Message the provider",
|
|
47
47
|
description: "Ask questions before you commit to hiring.",
|
|
48
48
|
icon: /* @__PURE__ */ c(r, { className: "h-4 w-4" }),
|
|
49
|
-
onClick: () => y("
|
|
49
|
+
onClick: () => y("../marketplace/messaging-page")
|
|
50
50
|
},
|
|
51
51
|
{
|
|
52
52
|
id: "compare",
|
|
53
53
|
label: "Compare other services",
|
|
54
54
|
description: "Browse similar offerings on the marketplace.",
|
|
55
55
|
icon: /* @__PURE__ */ c(a, { className: "h-4 w-4" }),
|
|
56
|
-
onClick: () => y("
|
|
56
|
+
onClick: () => y("../marketplace/marketplace-services-page")
|
|
57
57
|
},
|
|
58
58
|
{
|
|
59
59
|
id: "bookings",
|
|
60
60
|
label: "Manage your bookings",
|
|
61
61
|
description: "Track engagements you have already arranged.",
|
|
62
62
|
icon: /* @__PURE__ */ c(t, { className: "h-4 w-4" }),
|
|
63
|
-
onClick: () => y("
|
|
63
|
+
onClick: () => y("../marketplace/booking-management-page")
|
|
64
64
|
}
|
|
65
65
|
]
|
|
66
66
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarketplaceServiceDetailsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceServiceDetailsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Button,\n Card,\n CardContent,\n EmphasisPanel,\n GradientText,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { Clock, DollarSign, MessageSquare, Star, Users } from 'lucide-react';\nimport { useLocation, useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\nexport function MarketplaceServiceDetailsPagePage() {\n const navigate = useNavigate();\n const location = useLocation();\n const { marketplaceServices } = useCrewFoundryStore();\n const selectedId = new URLSearchParams(location.search).get('id');\n const svc = marketplaceServices.find((s) => s.id === selectedId) ?? marketplaceServices[0];\n if (!svc)\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Service not found\"\n description=\"This service is no longer available.\"\n />\n </div>\n );\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{svc.title}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">by {svc.provider}</p>\n <PagePurpose className=\"mt-2\">\n See exactly what this service includes, what it costs, and how others rated it — use it\n to compare offerings and hire the right provider for your project.\n </PagePurpose>\n </div>\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" onClick={() => navigate('
|
|
1
|
+
{"version":3,"file":"MarketplaceServiceDetailsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceServiceDetailsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Button,\n Card,\n CardContent,\n EmphasisPanel,\n GradientText,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { Clock, DollarSign, MessageSquare, Star, Users } from 'lucide-react';\nimport { useLocation, useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\nexport function MarketplaceServiceDetailsPagePage() {\n const navigate = useNavigate();\n const location = useLocation();\n const { marketplaceServices } = useCrewFoundryStore();\n const selectedId = new URLSearchParams(location.search).get('id');\n const svc = marketplaceServices.find((s) => s.id === selectedId) ?? marketplaceServices[0];\n if (!svc)\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Service not found\"\n description=\"This service is no longer available.\"\n />\n </div>\n );\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{svc.title}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">by {svc.provider}</p>\n <PagePurpose className=\"mt-2\">\n See exactly what this service includes, what it costs, and how others rated it — use it\n to compare offerings and hire the right provider for your project.\n </PagePurpose>\n </div>\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" onClick={() => navigate('../marketplace/messaging-page')}>\n <MessageSquare className=\"h-4 w-4 mr-1\" />\n Contact\n </Button>\n {/* No hire/engagement mutation exists yet, so this stays an honest\n disabled control rather than a silently-dead primary CTA. */}\n <Button disabled title=\"Hiring is coming soon\" aria-label=\"Hire now — coming soon\">\n Hire Now · Coming soon\n </Button>\n </div>\n </div>\n\n <NextSteps\n storageKey=\"crewfoundry-service-details\"\n steps={[\n {\n id: 'contact',\n label: 'Message the provider',\n description: 'Ask questions before you commit to hiring.',\n icon: <MessageSquare className=\"h-4 w-4\" />,\n onClick: () => navigate('../marketplace/messaging-page'),\n },\n {\n id: 'compare',\n label: 'Compare other services',\n description: 'Browse similar offerings on the marketplace.',\n icon: <Users className=\"h-4 w-4\" />,\n onClick: () => navigate('../marketplace/marketplace-services-page'),\n },\n {\n id: 'bookings',\n label: 'Manage your bookings',\n description: 'Track engagements you have already arranged.',\n icon: <Clock className=\"h-4 w-4\" />,\n onClick: () => navigate('../marketplace/booking-management-page'),\n },\n ]}\n />\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n <Card>\n <CardContent className=\"p-4 text-center\">\n <DollarSign className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">${svc.price.toLocaleString()}</p>\n <p className=\"text-xs text-text-secondary\">per {svc.priceType}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Star className=\"h-5 w-5 mx-auto text-status-warning-text mb-1\" />\n <p className=\"text-lg font-bold\">{svc.rating}/5</p>\n <p className=\"text-xs text-text-secondary\">{svc.reviews} reviews</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Clock className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{svc.deliveryTime}</p>\n <p className=\"text-xs text-text-secondary\">Delivery</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Users className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{svc.category}</p>\n <p className=\"text-xs text-text-secondary\">Category</p>\n </CardContent>\n </Card>\n </div>\n <EmphasisPanel>\n <div className=\"mb-2 text-lg font-semibold\">About this Service</div>\n <p className=\"text-sm text-text-secondary\">{svc.description}</p>\n <div className=\"flex flex-wrap gap-1 mt-3\">\n {svc.skills.map((s) => (\n <Badge key={s} variant=\"outline\">\n {s}\n </Badge>\n ))}\n </div>\n </EmphasisPanel>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -92,7 +92,7 @@ function S() {
|
|
|
92
92
|
children: [/* @__PURE__ */ s(m, { children: "Recent Invoices" }), C.length > b && /* @__PURE__ */ s(u, {
|
|
93
93
|
variant: "ghost",
|
|
94
94
|
size: "sm",
|
|
95
|
-
onClick: () => S("
|
|
95
|
+
onClick: () => S("../payments/invoices-page"),
|
|
96
96
|
children: "View all"
|
|
97
97
|
})]
|
|
98
98
|
}), /* @__PURE__ */ s(f, { children: /* @__PURE__ */ s(y, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PaymentsPaymentDashboardPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/PaymentsPaymentDashboardPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n ResponsiveTable,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { CreditCard, DollarSign, Receipt, TrendingUp } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore, type Invoice } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\n\nconst RECENT_LIMIT = 5;\n\nconst statusVariant = (status: Invoice['status']) =>\n status === 'paid' ? 'default' : status === 'overdue' ? 'destructive' : 'secondary';\n\nexport function PaymentsPaymentDashboardPagePage() {\n const navigate = useNavigate();\n const { invoices, payrollRecords } = useCrewFoundryStore();\n const totalInvoiced = invoices.reduce((s, i) => s + i.amount, 0);\n const totalPaid = invoices.filter((i) => i.status === 'paid').reduce((s, i) => s + i.amount, 0);\n const totalPayroll = payrollRecords.reduce((s, r) => s + r.grossPay, 0);\n\n const recentInvoices = [...invoices]\n .sort((a, b) => (b.issueDate ?? '').localeCompare(a.issueDate ?? ''))\n .slice(0, RECENT_LIMIT);\n\n const columns: ResponsiveTableColumn<Invoice>[] = [\n {\n key: 'number',\n header: 'Invoice',\n priority: 'primary',\n cell: (inv) => <span className=\"font-medium\">{inv.number}</span>,\n },\n {\n key: 'client',\n header: 'Client',\n priority: 'secondary',\n cell: (inv) => <span className=\"text-text-secondary\">{inv.clientName}</span>,\n },\n {\n key: 'amount',\n header: 'Amount',\n priority: 'secondary',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (inv) => <span>${inv.amount.toLocaleString()}</span>,\n },\n {\n key: 'dueDate',\n header: 'Due Date',\n priority: 'hidden-on-mobile',\n cell: (inv) => <span className=\"text-text-secondary\">{inv.dueDate}</span>,\n },\n {\n key: 'status',\n header: 'Status',\n priority: 'secondary',\n cell: (inv) => (\n <Badge variant={statusVariant(inv.status)} className=\"text-[10px]\">\n {formatEnumLabel(inv.status)}\n </Badge>\n ),\n },\n ];\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Payment Dashboard</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Financial transactions and payment overview</p>\n <PagePurpose className=\"mt-1\">\n A snapshot of all money movement — incoming payments, payouts, and balances so you\n understand your financial position at a glance.\n </PagePurpose>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Total Invoiced</CardTitle>\n <Receipt className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">${totalInvoiced.toLocaleString()}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Collected</CardTitle>\n <DollarSign className=\"h-4 w-4 text-status-success-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">${totalPaid.toLocaleString()}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Payroll Processed</CardTitle>\n <CreditCard className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">${totalPayroll.toLocaleString()}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Outstanding</CardTitle>\n <TrendingUp className=\"h-4 w-4 text-status-warning-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(totalInvoiced - totalPaid).toLocaleString()}\n </div>\n </CardContent>\n </GlassCard>\n </div>\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0\">\n <CardTitle>Recent Invoices</CardTitle>\n {invoices.length > RECENT_LIMIT && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => navigate('
|
|
1
|
+
{"version":3,"file":"PaymentsPaymentDashboardPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/PaymentsPaymentDashboardPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n ResponsiveTable,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { CreditCard, DollarSign, Receipt, TrendingUp } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore, type Invoice } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\n\nconst RECENT_LIMIT = 5;\n\nconst statusVariant = (status: Invoice['status']) =>\n status === 'paid' ? 'default' : status === 'overdue' ? 'destructive' : 'secondary';\n\nexport function PaymentsPaymentDashboardPagePage() {\n const navigate = useNavigate();\n const { invoices, payrollRecords } = useCrewFoundryStore();\n const totalInvoiced = invoices.reduce((s, i) => s + i.amount, 0);\n const totalPaid = invoices.filter((i) => i.status === 'paid').reduce((s, i) => s + i.amount, 0);\n const totalPayroll = payrollRecords.reduce((s, r) => s + r.grossPay, 0);\n\n const recentInvoices = [...invoices]\n .sort((a, b) => (b.issueDate ?? '').localeCompare(a.issueDate ?? ''))\n .slice(0, RECENT_LIMIT);\n\n const columns: ResponsiveTableColumn<Invoice>[] = [\n {\n key: 'number',\n header: 'Invoice',\n priority: 'primary',\n cell: (inv) => <span className=\"font-medium\">{inv.number}</span>,\n },\n {\n key: 'client',\n header: 'Client',\n priority: 'secondary',\n cell: (inv) => <span className=\"text-text-secondary\">{inv.clientName}</span>,\n },\n {\n key: 'amount',\n header: 'Amount',\n priority: 'secondary',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (inv) => <span>${inv.amount.toLocaleString()}</span>,\n },\n {\n key: 'dueDate',\n header: 'Due Date',\n priority: 'hidden-on-mobile',\n cell: (inv) => <span className=\"text-text-secondary\">{inv.dueDate}</span>,\n },\n {\n key: 'status',\n header: 'Status',\n priority: 'secondary',\n cell: (inv) => (\n <Badge variant={statusVariant(inv.status)} className=\"text-[10px]\">\n {formatEnumLabel(inv.status)}\n </Badge>\n ),\n },\n ];\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Payment Dashboard</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Financial transactions and payment overview</p>\n <PagePurpose className=\"mt-1\">\n A snapshot of all money movement — incoming payments, payouts, and balances so you\n understand your financial position at a glance.\n </PagePurpose>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Total Invoiced</CardTitle>\n <Receipt className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">${totalInvoiced.toLocaleString()}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Collected</CardTitle>\n <DollarSign className=\"h-4 w-4 text-status-success-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">${totalPaid.toLocaleString()}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Payroll Processed</CardTitle>\n <CreditCard className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">${totalPayroll.toLocaleString()}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Outstanding</CardTitle>\n <TrendingUp className=\"h-4 w-4 text-status-warning-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(totalInvoiced - totalPaid).toLocaleString()}\n </div>\n </CardContent>\n </GlassCard>\n </div>\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0\">\n <CardTitle>Recent Invoices</CardTitle>\n {invoices.length > RECENT_LIMIT && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => navigate('../payments/invoices-page')}\n >\n View all\n </Button>\n )}\n </CardHeader>\n <CardContent>\n <ResponsiveTable\n data={recentInvoices}\n rowKey={(inv) => inv.id}\n columns={columns}\n emptyState={\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No invoices yet\"\n description=\"Invoices you create will show up here.\"\n />\n }\n />\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":";;;;;;;AAmBA,IAAM,IAAe,GAEf,KAAiB,MACrB,MAAW,SAAS,YAAY,MAAW,YAAY,gBAAgB;AAEzE,SAAgB,IAAmC;CACjD,IAAM,IAAW,GAAa,EACxB,EAAE,aAAU,sBAAmB,GAAqB,EACpD,IAAgB,EAAS,QAAQ,GAAG,MAAM,IAAI,EAAE,QAAQ,EAAE,EAC1D,IAAY,EAAS,QAAQ,MAAM,EAAE,WAAW,OAAO,CAAC,QAAQ,GAAG,MAAM,IAAI,EAAE,QAAQ,EAAE,EACzF,IAAe,EAAe,QAAQ,GAAG,MAAM,IAAI,EAAE,UAAU,EAAE,EAEjE,IAAiB,CAAC,GAAG,EAAS,CACjC,MAAM,GAAG,OAAO,EAAE,aAAa,IAAI,cAAc,EAAE,aAAa,GAAG,CAAC,CACpE,MAAM,GAAG,EAAa;AAyCzB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD,EAAA,UAAA;IACE,kBAAC,MAAD;KAAI,WAAU;eACZ,kBAAC,GAAD,EAAA,UAAc,qBAAgC,CAAA;KAC3C,CAAA;IACL,kBAAC,KAAD;KAAG,WAAU;eAA8B;KAA+C,CAAA;IAC1F,kBAAC,GAAD;KAAa,WAAU;eAAO;KAGhB,CAAA;IACV,EAAA,CAAA;GACN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MAAW,WAAU;MAAQ,MAAA;MAAK,WAAU;gBAA5C,CACE,kBAAC,GAAD;OAAY,WAAU;iBAAtB,CACE,kBAAC,GAAD;QAAW,WAAU;kBAAsB;QAA0B,CAAA,EACrE,kBAAC,GAAD,EAAS,WAAU,+BAAgC,CAAA,CACxC;UACb,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CAAiD,KAAE,EAAc,gBAAgB,CAAO;UAC5E,CAAA,CACJ;;KACZ,kBAAC,GAAD;MAAW,WAAU;MAAS,WAAU;gBAAxC,CACE,kBAAC,GAAD;OAAY,WAAU;iBAAtB,CACE,kBAAC,GAAD;QAAW,WAAU;kBAAsB;QAAqB,CAAA,EAChE,kBAAC,GAAD,EAAY,WAAU,oCAAqC,CAAA,CAChD;UACb,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CAAiD,KAAE,EAAU,gBAAgB,CAAO;UACxE,CAAA,CACJ;;KACZ,kBAAC,GAAD;MAAW,WAAU;MAAS,WAAU;gBAAxC,CACE,kBAAC,GAAD;OAAY,WAAU;iBAAtB,CACE,kBAAC,GAAD;QAAW,WAAU;kBAAsB;QAA6B,CAAA,EACxE,kBAAC,GAAD,EAAY,WAAU,+BAAgC,CAAA,CAC3C;UACb,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CAAiD,KAAE,EAAa,gBAAgB,CAAO;UAC3E,CAAA,CACJ;;KACZ,kBAAC,GAAD;MAAW,WAAU;MAAS,WAAU;gBAAxC,CACE,kBAAC,GAAD;OAAY,WAAU;iBAAtB,CACE,kBAAC,GAAD;QAAW,WAAU;kBAAsB;QAAuB,CAAA,EAClE,kBAAC,GAAD,EAAY,WAAU,oCAAqC,CAAA,CAChD;UACb,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CAAiD,MAC5C,IAAgB,GAAW,gBAAgB,CAC1C;UACM,CAAA,CACJ;;KACR;;GACN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;IAAY,WAAU;cAAtB,CACE,kBAAC,GAAD,EAAA,UAAW,mBAA2B,CAAA,EACrC,EAAS,SAAS,KACjB,kBAAC,GAAD;KACE,SAAQ;KACR,MAAK;KACL,eAAe,EAAS,4BAA4B;eACrD;KAEQ,CAAA,CAEA;OACb,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;IACE,MAAM;IACN,SAAS,MAAQ,EAAI;IACZ,SA5G+B;KAChD;MACE,KAAK;MACL,QAAQ;MACR,UAAU;MACV,OAAO,MAAQ,kBAAC,QAAD;OAAM,WAAU;iBAAe,EAAI;OAAc,CAAA;MACjE;KACD;MACE,KAAK;MACL,QAAQ;MACR,UAAU;MACV,OAAO,MAAQ,kBAAC,QAAD;OAAM,WAAU;iBAAuB,EAAI;OAAkB,CAAA;MAC7E;KACD;MACE,KAAK;MACL,QAAQ;MACR,UAAU;MACV,iBAAiB;MACjB,eAAe;MACf,OAAO,MAAQ,kBAAC,QAAD,EAAA,UAAA,CAAM,KAAE,EAAI,OAAO,gBAAgB,CAAQ,EAAA,CAAA;MAC3D;KACD;MACE,KAAK;MACL,QAAQ;MACR,UAAU;MACV,OAAO,MAAQ,kBAAC,QAAD;OAAM,WAAU;iBAAuB,EAAI;OAAe,CAAA;MAC1E;KACD;MACE,KAAK;MACL,QAAQ;MACR,UAAU;MACV,OAAO,MACL,kBAAC,GAAD;OAAO,SAAS,EAAc,EAAI,OAAO;OAAE,WAAU;iBAClD,EAAgB,EAAI,OAAO;OACtB,CAAA;MAEX;KACF;IAwES,YACE,kBAAC,GAAD;KACE,cAAa;KACb,OAAM;KACN,aAAY;KACZ,CAAA;IAEJ,CAAA,EACU,CAAA,CACT,EAAA,CAAA;GACH"}
|
|
@@ -28,7 +28,7 @@ function m() {
|
|
|
28
28
|
};
|
|
29
29
|
j(!0);
|
|
30
30
|
try {
|
|
31
|
-
await g(r), h(e === "planning" ? "Draft project saved" : "Project created", "success"), m("
|
|
31
|
+
await g(r), h(e === "planning" ? "Draft project saved" : "Project created", "success"), m("../projects/projects-page");
|
|
32
32
|
} catch (e) {
|
|
33
33
|
h(e instanceof Error ? e.message : "Unable to create project", "error");
|
|
34
34
|
} finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectsCreateProjectPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/ProjectsCreateProjectPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n Button,\n EmphasisPanel,\n GradientText,\n Input,\n PagePurpose,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { FolderKanban, Save } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore, type Project } from '../store/crewfoundryStore';\n\nexport function ProjectsCreateProjectPagePage() {\n const navigate = useNavigate();\n const { showToast } = useGlobalUi();\n const addProject = useCrewFoundryStore((state) => state.addProject);\n const teams = useCrewFoundryStore((state) => state.teams);\n\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [startDate, setStartDate] = useState('');\n const [endDate, setEndDate] = useState('');\n const [budget, setBudget] = useState('');\n const [teamId, setTeamId] = useState('');\n const [submitting, setSubmitting] = useState(false);\n\n const handleCreate = async (status: Project['status']) => {\n if (submitting) return;\n if (name.trim() === '') {\n showToast('Project Name is required', 'warning');\n return;\n }\n const selectedTeam = teams.find((team) => team.id === teamId);\n const budgetValue = budget.trim() === '' ? undefined : Number(budget);\n // CreateCrewProjectInput only accepts a known field subset; build exactly\n // that shape. The store replaces this optimistic row with the server's\n // complete Project, so display-only fields need not be provided here.\n const payload: Partial<Project> = {\n name: name.trim(),\n description: description.trim(),\n status,\n priority: 'medium',\n ...(startDate ? { startDate } : {}),\n ...(endDate ? { endDate } : {}),\n ...(budgetValue !== undefined && !Number.isNaN(budgetValue) ? { budget: budgetValue } : {}),\n ...(selectedTeam ? { teamId: selectedTeam.id, teamName: selectedTeam.name } : {}),\n };\n setSubmitting(true);\n try {\n await addProject(payload as Project);\n showToast(status === 'planning' ? 'Draft project saved' : 'Project created', 'success');\n navigate('../../projects/projects-page');\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Unable to create project', 'error');\n } finally {\n setSubmitting(false);\n }\n };\n\n const selectClass =\n 'flex h-10 w-full rounded-input border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary transition-colors duration-200 focus-visible:border-border-focus focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50';\n\n return (\n <div className=\"space-y-6 p-6 max-w-3xl\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Create Project</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Set up a new project for your team</p>\n <PagePurpose className=\"mt-1\">\n Start a new project — define its scope, budget, and team here so you can begin tracking\n work toward a clear goal.\n </PagePurpose>\n </div>\n <EmphasisPanel>\n <div className=\"mb-4 flex items-center gap-2 text-lg font-semibold\">\n <FolderKanban className=\"h-5 w-5\" />\n Project Details\n </div>\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <label htmlFor=\"cpName\" className=\"text-sm font-medium\">\n Project Name\n </label>\n <Input\n id=\"cpName\"\n placeholder=\"e.g. HR Analytics Dashboard v3\"\n value={name}\n disabled={submitting}\n onChange={(e) => setName(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <label htmlFor=\"cpDesc\" className=\"text-sm font-medium\">\n Description\n </label>\n <Textarea\n id=\"cpDesc\"\n placeholder=\"Describe the project objectives and scope...\"\n rows={3}\n value={description}\n disabled={submitting}\n onChange={(e) => setDescription(e.target.value)}\n />\n </div>\n {teams.length > 0 ? (\n <div className=\"space-y-2\">\n <label htmlFor=\"cpTeam\" className=\"text-sm font-medium\">\n Team\n </label>\n <select\n id=\"cpTeam\"\n className={selectClass}\n value={teamId}\n disabled={submitting}\n onChange={(e) => setTeamId(e.target.value)}\n >\n <option value=\"\">Unassigned</option>\n {teams.map((team) => (\n <option key={team.id} value={team.id}>\n {team.name}\n </option>\n ))}\n </select>\n </div>\n ) : null}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <label htmlFor=\"cpStart\" className=\"text-sm font-medium\">\n Start Date\n </label>\n <Input\n id=\"cpStart\"\n type=\"date\"\n value={startDate}\n disabled={submitting}\n onChange={(e) => setStartDate(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <label htmlFor=\"cpEnd\" className=\"text-sm font-medium\">\n End Date\n </label>\n <Input\n id=\"cpEnd\"\n type=\"date\"\n value={endDate}\n disabled={submitting}\n onChange={(e) => setEndDate(e.target.value)}\n />\n </div>\n </div>\n <div className=\"space-y-2\">\n <label htmlFor=\"cpBudget\" className=\"text-sm font-medium\">\n Budget ($)\n </label>\n <Input\n id=\"cpBudget\"\n type=\"number\"\n placeholder=\"e.g. 250000\"\n value={budget}\n disabled={submitting}\n onChange={(e) => setBudget(e.target.value)}\n />\n </div>\n </div>\n </EmphasisPanel>\n <div className=\"flex justify-end gap-2\">\n <Button variant=\"outline\" disabled={submitting} onClick={() => handleCreate('planning')}>\n Save as Draft\n </Button>\n <Button disabled={submitting} onClick={() => handleCreate('active')}>\n <Save className=\"h-4 w-4 mr-1\" />\n {submitting ? 'Saving…' : 'Create Project'}\n </Button>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;AAcA,SAAgB,IAAgC;CAC9C,IAAM,IAAW,GAAa,EACxB,EAAE,iBAAc,GAAa,EAC7B,IAAa,GAAqB,MAAU,EAAM,WAAW,EAC7D,IAAQ,GAAqB,MAAU,EAAM,MAAM,EAEnD,CAAC,GAAM,KAAW,EAAS,GAAG,EAC9B,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAW,KAAgB,EAAS,GAAG,EACxC,CAAC,GAAS,KAAc,EAAS,GAAG,EACpC,CAAC,GAAQ,KAAa,EAAS,GAAG,EAClC,CAAC,GAAQ,KAAa,EAAS,GAAG,EAClC,CAAC,GAAY,KAAiB,EAAS,GAAM,EAE7C,IAAe,OAAO,MAA8B;AACxD,MAAI,EAAY;AAChB,MAAI,EAAK,MAAM,KAAK,IAAI;AACtB,KAAU,4BAA4B,UAAU;AAChD;;EAEF,IAAM,IAAe,EAAM,MAAM,MAAS,EAAK,OAAO,EAAO,EACvD,IAAc,EAAO,MAAM,KAAK,KAAK,KAAA,IAAY,OAAO,EAAO,EAI/D,IAA4B;GAChC,MAAM,EAAK,MAAM;GACjB,aAAa,EAAY,MAAM;GAC/B;GACA,UAAU;GACV,GAAI,IAAY,EAAE,cAAW,GAAG,EAAE;GAClC,GAAI,IAAU,EAAE,YAAS,GAAG,EAAE;GAC9B,GAAI,MAAgB,KAAA,KAAa,CAAC,OAAO,MAAM,EAAY,GAAG,EAAE,QAAQ,GAAa,GAAG,EAAE;GAC1F,GAAI,IAAe;IAAE,QAAQ,EAAa;IAAI,UAAU,EAAa;IAAM,GAAG,EAAE;GACjF;AACD,IAAc,GAAK;AACnB,MAAI;AAGF,GAFA,MAAM,EAAW,EAAmB,EACpC,EAAU,MAAW,aAAa,wBAAwB,mBAAmB,UAAU,EACvF,EAAS,+BAA+B;WACjC,GAAO;AACd,KAAU,aAAiB,QAAQ,EAAM,UAAU,4BAA4B,QAAQ;YAC/E;AACR,KAAc,GAAM;;;AAOxB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD,EAAA,UAAA;IACE,kBAAC,MAAD;KAAI,WAAU;eACZ,kBAAC,GAAD,EAAA,UAAc,kBAA6B,CAAA;KACxC,CAAA;IACL,kBAAC,KAAD;KAAG,WAAU;eAA8B;KAAsC,CAAA;IACjF,kBAAC,GAAD;KAAa,WAAU;eAAO;KAGhB,CAAA;IACV,EAAA,CAAA;GACN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA,EAAA,kBAEhC;OACN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,SAAD;OAAO,SAAQ;OAAS,WAAU;iBAAsB;OAEhD,CAAA,EACR,kBAAC,GAAD;OACE,IAAG;OACH,aAAY;OACZ,OAAO;OACP,UAAU;OACV,WAAW,MAAM,EAAQ,EAAE,OAAO,MAAM;OACxC,CAAA,CACE;;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,SAAD;OAAO,SAAQ;OAAS,WAAU;iBAAsB;OAEhD,CAAA,EACR,kBAAC,GAAD;OACE,IAAG;OACH,aAAY;OACZ,MAAM;OACN,OAAO;OACP,UAAU;OACV,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;OAC/C,CAAA,CACE;;KACL,EAAM,SAAS,IACd,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,SAAD;OAAO,SAAQ;OAAS,WAAU;iBAAsB;OAEhD,CAAA,EACR,kBAAC,UAAD;OACE,IAAG;OACH,WApDZ;OAqDY,OAAO;OACP,UAAU;OACV,WAAW,MAAM,EAAU,EAAE,OAAO,MAAM;iBAL5C,CAOE,kBAAC,UAAD;QAAQ,OAAM;kBAAG;QAAmB,CAAA,EACnC,EAAM,KAAK,MACV,kBAAC,UAAD;QAAsB,OAAO,EAAK;kBAC/B,EAAK;QACC,EAFI,EAAK,GAET,CACT,CACK;SACL;UACJ;KACJ,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,SAAQ;QAAU,WAAU;kBAAsB;QAEjD,CAAA,EACR,kBAAC,GAAD;QACE,IAAG;QACH,MAAK;QACL,OAAO;QACP,UAAU;QACV,WAAW,MAAM,EAAa,EAAE,OAAO,MAAM;QAC7C,CAAA,CACE;UACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QAAO,SAAQ;QAAQ,WAAU;kBAAsB;QAE/C,CAAA,EACR,kBAAC,GAAD;QACE,IAAG;QACH,MAAK;QACL,OAAO;QACP,UAAU;QACV,WAAW,MAAM,EAAW,EAAE,OAAO,MAAM;QAC3C,CAAA,CACE;SACF;;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,SAAD;OAAO,SAAQ;OAAW,WAAU;iBAAsB;OAElD,CAAA,EACR,kBAAC,GAAD;OACE,IAAG;OACH,MAAK;OACL,aAAY;OACZ,OAAO;OACP,UAAU;OACV,WAAW,MAAM,EAAU,EAAE,OAAO,MAAM;OAC1C,CAAA,CACE;;KACF;MACQ,EAAA,CAAA;GAChB,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAQ,SAAQ;KAAU,UAAU;KAAY,eAAe,EAAa,WAAW;eAAE;KAEhF,CAAA,EACT,kBAAC,GAAD;KAAQ,UAAU;KAAY,eAAe,EAAa,SAAS;eAAnE,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAChC,IAAa,YAAY,iBACnB;OACL;;GACF"}
|
|
1
|
+
{"version":3,"file":"ProjectsCreateProjectPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/ProjectsCreateProjectPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n Button,\n EmphasisPanel,\n GradientText,\n Input,\n PagePurpose,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { FolderKanban, Save } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore, type Project } from '../store/crewfoundryStore';\n\nexport function ProjectsCreateProjectPagePage() {\n const navigate = useNavigate();\n const { showToast } = useGlobalUi();\n const addProject = useCrewFoundryStore((state) => state.addProject);\n const teams = useCrewFoundryStore((state) => state.teams);\n\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [startDate, setStartDate] = useState('');\n const [endDate, setEndDate] = useState('');\n const [budget, setBudget] = useState('');\n const [teamId, setTeamId] = useState('');\n const [submitting, setSubmitting] = useState(false);\n\n const handleCreate = async (status: Project['status']) => {\n if (submitting) return;\n if (name.trim() === '') {\n showToast('Project Name is required', 'warning');\n return;\n }\n const selectedTeam = teams.find((team) => team.id === teamId);\n const budgetValue = budget.trim() === '' ? undefined : Number(budget);\n // CreateCrewProjectInput only accepts a known field subset; build exactly\n // that shape. The store replaces this optimistic row with the server's\n // complete Project, so display-only fields need not be provided here.\n const payload: Partial<Project> = {\n name: name.trim(),\n description: description.trim(),\n status,\n priority: 'medium',\n ...(startDate ? { startDate } : {}),\n ...(endDate ? { endDate } : {}),\n ...(budgetValue !== undefined && !Number.isNaN(budgetValue) ? { budget: budgetValue } : {}),\n ...(selectedTeam ? { teamId: selectedTeam.id, teamName: selectedTeam.name } : {}),\n };\n setSubmitting(true);\n try {\n await addProject(payload as Project);\n showToast(status === 'planning' ? 'Draft project saved' : 'Project created', 'success');\n navigate('../projects/projects-page');\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Unable to create project', 'error');\n } finally {\n setSubmitting(false);\n }\n };\n\n const selectClass =\n 'flex h-10 w-full rounded-input border border-border-default bg-bg-surface px-3 py-2 text-sm text-text-primary transition-colors duration-200 focus-visible:border-border-focus focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50';\n\n return (\n <div className=\"space-y-6 p-6 max-w-3xl\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Create Project</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Set up a new project for your team</p>\n <PagePurpose className=\"mt-1\">\n Start a new project — define its scope, budget, and team here so you can begin tracking\n work toward a clear goal.\n </PagePurpose>\n </div>\n <EmphasisPanel>\n <div className=\"mb-4 flex items-center gap-2 text-lg font-semibold\">\n <FolderKanban className=\"h-5 w-5\" />\n Project Details\n </div>\n <div className=\"space-y-4\">\n <div className=\"space-y-2\">\n <label htmlFor=\"cpName\" className=\"text-sm font-medium\">\n Project Name\n </label>\n <Input\n id=\"cpName\"\n placeholder=\"e.g. HR Analytics Dashboard v3\"\n value={name}\n disabled={submitting}\n onChange={(e) => setName(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <label htmlFor=\"cpDesc\" className=\"text-sm font-medium\">\n Description\n </label>\n <Textarea\n id=\"cpDesc\"\n placeholder=\"Describe the project objectives and scope...\"\n rows={3}\n value={description}\n disabled={submitting}\n onChange={(e) => setDescription(e.target.value)}\n />\n </div>\n {teams.length > 0 ? (\n <div className=\"space-y-2\">\n <label htmlFor=\"cpTeam\" className=\"text-sm font-medium\">\n Team\n </label>\n <select\n id=\"cpTeam\"\n className={selectClass}\n value={teamId}\n disabled={submitting}\n onChange={(e) => setTeamId(e.target.value)}\n >\n <option value=\"\">Unassigned</option>\n {teams.map((team) => (\n <option key={team.id} value={team.id}>\n {team.name}\n </option>\n ))}\n </select>\n </div>\n ) : null}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div className=\"space-y-2\">\n <label htmlFor=\"cpStart\" className=\"text-sm font-medium\">\n Start Date\n </label>\n <Input\n id=\"cpStart\"\n type=\"date\"\n value={startDate}\n disabled={submitting}\n onChange={(e) => setStartDate(e.target.value)}\n />\n </div>\n <div className=\"space-y-2\">\n <label htmlFor=\"cpEnd\" className=\"text-sm font-medium\">\n End Date\n </label>\n <Input\n id=\"cpEnd\"\n type=\"date\"\n value={endDate}\n disabled={submitting}\n onChange={(e) => setEndDate(e.target.value)}\n />\n </div>\n </div>\n <div className=\"space-y-2\">\n <label htmlFor=\"cpBudget\" className=\"text-sm font-medium\">\n Budget ($)\n </label>\n <Input\n id=\"cpBudget\"\n type=\"number\"\n placeholder=\"e.g. 250000\"\n value={budget}\n disabled={submitting}\n onChange={(e) => setBudget(e.target.value)}\n />\n </div>\n </div>\n </EmphasisPanel>\n <div className=\"flex justify-end gap-2\">\n <Button variant=\"outline\" disabled={submitting} onClick={() => handleCreate('planning')}>\n Save as Draft\n </Button>\n <Button disabled={submitting} onClick={() => handleCreate('active')}>\n <Save className=\"h-4 w-4 mr-1\" />\n {submitting ? 'Saving…' : 'Create Project'}\n </Button>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -151,21 +151,21 @@ function F() {
|
|
|
151
151
|
label: "View all projects",
|
|
152
152
|
description: "See how this project compares across your portfolio.",
|
|
153
153
|
icon: /* @__PURE__ */ m(s, { className: "h-4 w-4" }),
|
|
154
|
-
onClick: () => F("
|
|
154
|
+
onClick: () => F("../projects/projects-page")
|
|
155
155
|
},
|
|
156
156
|
{
|
|
157
157
|
id: "team",
|
|
158
158
|
label: "Check the team",
|
|
159
159
|
description: "Review who is working on this and their workload.",
|
|
160
160
|
icon: /* @__PURE__ */ m(d, { className: "h-4 w-4" }),
|
|
161
|
-
onClick: () => F("
|
|
161
|
+
onClick: () => F("../teams/teams-page")
|
|
162
162
|
},
|
|
163
163
|
{
|
|
164
164
|
id: "new-project",
|
|
165
165
|
label: "Start a new project",
|
|
166
166
|
description: "Kick off the next piece of work.",
|
|
167
167
|
icon: /* @__PURE__ */ m(l, { className: "h-4 w-4" }),
|
|
168
|
-
onClick: () => F("
|
|
168
|
+
onClick: () => F("../projects/create-project-page")
|
|
169
169
|
}
|
|
170
170
|
]
|
|
171
171
|
}),
|
|
@@ -336,7 +336,7 @@ function F() {
|
|
|
336
336
|
if (!W) {
|
|
337
337
|
G(!0);
|
|
338
338
|
try {
|
|
339
|
-
await B(q.id), L("Project deleted", "success"), U(!1), F("
|
|
339
|
+
await B(q.id), L("Project deleted", "success"), U(!1), F("../projects/projects-page");
|
|
340
340
|
} catch (e) {
|
|
341
341
|
L(e instanceof Error ? e.message : "Unable to delete project", "error");
|
|
342
342
|
} finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectsProjectDetailsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/ProjectsProjectDetailsPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n EmphasisPanel,\n GradientText,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { Calendar, DollarSign, FolderKanban, Pencil, Target, Trash2, Users } from 'lucide-react';\nimport { useNavigate, useSearchParams } from 'react-router-dom';\nimport {\n FormModal,\n useFormModal,\n type FieldConfig,\n type FormValues,\n} from '../components/forms/FormModal';\nimport { useCrewFoundryStore, type Project } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\n\nconst PROJECT_EDIT_FIELDS: FieldConfig[] = [\n { name: 'name', label: 'Project Name', type: 'text', required: true, colSpan: 2 },\n { name: 'description', label: 'Description', type: 'textarea', colSpan: 2 },\n {\n name: 'status',\n label: 'Status',\n type: 'select',\n options: [\n { label: 'Planning', value: 'planning' },\n { label: 'Active', value: 'active' },\n { label: 'On Hold', value: 'on_hold' },\n { label: 'Completed', value: 'completed' },\n { label: 'Cancelled', value: 'cancelled' },\n ],\n },\n {\n name: 'priority',\n label: 'Priority',\n type: 'select',\n options: [\n { label: 'Low', value: 'low' },\n { label: 'Medium', value: 'medium' },\n { label: 'High', value: 'high' },\n { label: 'Critical', value: 'critical' },\n ],\n },\n { name: 'budget', label: 'Budget ($)', type: 'number' },\n { name: 'progress', label: 'Progress (%)', type: 'number' },\n];\n\nexport function ProjectsProjectDetailsPagePage() {\n const navigate = useNavigate();\n const [searchParams] = useSearchParams();\n const { showToast } = useGlobalUi();\n const projects = useCrewFoundryStore((state) => state.projects);\n const updateProject = useCrewFoundryStore((state) => state.updateProject);\n const removeProject = useCrewFoundryStore((state) => state.removeProject);\n const editModal = useFormModal();\n const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false);\n const [deleting, setDeleting] = useState(false);\n const projectId = searchParams.get('id');\n const project = projects.find((p) => p.id === projectId) ?? projects[0];\n if (!project)\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Project not found\"\n description=\"This project is no longer available.\"\n />\n </div>\n );\n\n const budgetPct = project.budget > 0 ? Math.round((project.spent / project.budget) * 100) : 0;\n const budgetOverrun = project.budget > 0 && project.spent / project.budget > 0.9;\n\n const editInitialValues: FormValues = {\n name: project.name,\n description: project.description,\n status: project.status,\n priority: project.priority,\n budget: project.budget,\n progress: project.progress,\n };\n\n const handleEdit = async (values: FormValues) => {\n const updates: Partial<Project> = {\n name: String(values.name ?? '').trim(),\n description: String(values.description ?? '').trim(),\n status: String(values.status ?? project.status) as Project['status'],\n priority: String(values.priority ?? project.priority) as Project['priority'],\n };\n if (typeof values.budget === 'number' && !Number.isNaN(values.budget)) {\n updates.budget = values.budget;\n }\n if (typeof values.progress === 'number' && !Number.isNaN(values.progress)) {\n updates.progress = values.progress;\n }\n await updateProject(project.id, updates);\n showToast('Project updated', 'success');\n };\n\n const handleDelete = async () => {\n if (deleting) return;\n setDeleting(true);\n try {\n await removeProject(project.id);\n showToast('Project deleted', 'success');\n setConfirmDeleteOpen(false);\n navigate('../../projects/projects-page');\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Unable to delete project', 'error');\n } finally {\n setDeleting(false);\n }\n };\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{project.name}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">{project.description}</p>\n </div>\n <div className=\"flex items-center gap-2\">\n <div className=\"hidden gap-1 sm:flex\">\n <Badge variant={project.priority === 'critical' ? 'destructive' : 'default'}>\n {formatEnumLabel(project.priority)}\n </Badge>\n <Badge variant={project.status === 'active' ? 'default' : 'secondary'}>\n {formatEnumLabel(project.status)}\n </Badge>\n </div>\n <Button variant=\"outline\" size=\"sm\" onClick={() => editModal.openModal()}>\n <Pencil className=\"h-4 w-4 mr-1\" />\n Edit\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Delete project\"\n className=\"h-9 w-9 p-0 text-text-secondary hover:text-status-error-text\"\n onClick={() => setConfirmDeleteOpen(true)}\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n <PagePurpose>\n Track a single project end-to-end — progress, budget, team, and timeline in one place — so\n you can spot risks early and keep delivery on schedule and on budget.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"crewfoundry-project-details\"\n steps={[\n {\n id: 'all-projects',\n label: 'View all projects',\n description: 'See how this project compares across your portfolio.',\n icon: <FolderKanban className=\"h-4 w-4\" />,\n onClick: () => navigate('../../projects/projects-page'),\n },\n {\n id: 'team',\n label: 'Check the team',\n description: 'Review who is working on this and their workload.',\n icon: <Users className=\"h-4 w-4\" />,\n onClick: () => navigate('../../teams/teams-page'),\n },\n {\n id: 'new-project',\n label: 'Start a new project',\n description: 'Kick off the next piece of work.',\n icon: <Target className=\"h-4 w-4\" />,\n onClick: () => navigate('../../projects/create-project-page'),\n },\n ]}\n />\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Target className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{project.progress}%</p>\n <p className=\"text-xs text-text-secondary\">Progress</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <DollarSign className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">${(project.budget / 1000).toFixed(0)}K</p>\n <p className=\"text-xs text-text-secondary\">Budget</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Users className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{project.teamName}</p>\n <p className=\"text-xs text-text-secondary\">Team</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Calendar className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{project.endDate}</p>\n <p className=\"text-xs text-text-secondary\">Deadline</p>\n </CardContent>\n </Card>\n </div>\n <EmphasisPanel>\n <div className=\"mb-2 text-lg font-semibold\">Task Progress</div>\n <div className=\"space-y-2\">\n <div className=\"flex justify-between text-sm\">\n <span>\n {project.completedTasks} of {project.tasks} tasks completed\n </span>\n <span className=\"font-medium\">{project.progress}%</span>\n </div>\n <div className=\"h-3 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-3 bg-action-primary-bg rounded\"\n style={{ width: `${project.progress}%` }}\n />\n </div>\n </div>\n </EmphasisPanel>\n <Card>\n <CardHeader>\n <CardTitle>Budget Tracking</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-2\">\n <div className=\"flex justify-between text-sm\">\n <span>\n Spent ${(project.spent / 1000).toFixed(0)}K of ${(project.budget / 1000).toFixed(0)}\n K\n </span>\n <span className=\"font-medium\">{budgetPct}%</span>\n </div>\n <div className=\"h-3 bg-bg-sunken rounded overflow-hidden\">\n <div\n className={`h-3 rounded ${budgetOverrun ? 'bg-status-error-text' : 'bg-status-success-text'}`}\n style={{ width: `${budgetPct}%` }}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-4 mt-4 text-sm\">\n <div>\n <p className=\"text-xs text-text-secondary\">Manager</p>\n <p className=\"font-medium\">{project.managerName}</p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Timeline</p>\n <p>\n {project.startDate} — {project.endDate}\n </p>\n </div>\n </div>\n </CardContent>\n </Card>\n\n <FormModal\n open={editModal.open}\n onOpenChange={editModal.onOpenChange}\n title=\"Edit Project\"\n description={`Update the details for ${project.name}.`}\n fields={PROJECT_EDIT_FIELDS}\n initialValues={editInitialValues}\n submitLabel=\"Save Changes\"\n onSubmit={handleEdit}\n />\n\n <ResponsiveDialog open={confirmDeleteOpen} onOpenChange={setConfirmDeleteOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-[440px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Delete {project.name}?</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n This permanently removes the project and its tracking data. This cannot be undone.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setConfirmDeleteOpen(false)}\n disabled={deleting}\n >\n Cancel\n </Button>\n <Button type=\"button\" variant=\"destructive\" onClick={handleDelete} disabled={deleting}>\n {deleting ? 'Deleting…' : 'Delete Project'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProjectsProjectDetailsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/ProjectsProjectDetailsPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n EmphasisPanel,\n GradientText,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { Calendar, DollarSign, FolderKanban, Pencil, Target, Trash2, Users } from 'lucide-react';\nimport { useNavigate, useSearchParams } from 'react-router-dom';\nimport {\n FormModal,\n useFormModal,\n type FieldConfig,\n type FormValues,\n} from '../components/forms/FormModal';\nimport { useCrewFoundryStore, type Project } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\n\nconst PROJECT_EDIT_FIELDS: FieldConfig[] = [\n { name: 'name', label: 'Project Name', type: 'text', required: true, colSpan: 2 },\n { name: 'description', label: 'Description', type: 'textarea', colSpan: 2 },\n {\n name: 'status',\n label: 'Status',\n type: 'select',\n options: [\n { label: 'Planning', value: 'planning' },\n { label: 'Active', value: 'active' },\n { label: 'On Hold', value: 'on_hold' },\n { label: 'Completed', value: 'completed' },\n { label: 'Cancelled', value: 'cancelled' },\n ],\n },\n {\n name: 'priority',\n label: 'Priority',\n type: 'select',\n options: [\n { label: 'Low', value: 'low' },\n { label: 'Medium', value: 'medium' },\n { label: 'High', value: 'high' },\n { label: 'Critical', value: 'critical' },\n ],\n },\n { name: 'budget', label: 'Budget ($)', type: 'number' },\n { name: 'progress', label: 'Progress (%)', type: 'number' },\n];\n\nexport function ProjectsProjectDetailsPagePage() {\n const navigate = useNavigate();\n const [searchParams] = useSearchParams();\n const { showToast } = useGlobalUi();\n const projects = useCrewFoundryStore((state) => state.projects);\n const updateProject = useCrewFoundryStore((state) => state.updateProject);\n const removeProject = useCrewFoundryStore((state) => state.removeProject);\n const editModal = useFormModal();\n const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false);\n const [deleting, setDeleting] = useState(false);\n const projectId = searchParams.get('id');\n const project = projects.find((p) => p.id === projectId) ?? projects[0];\n if (!project)\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Project not found\"\n description=\"This project is no longer available.\"\n />\n </div>\n );\n\n const budgetPct = project.budget > 0 ? Math.round((project.spent / project.budget) * 100) : 0;\n const budgetOverrun = project.budget > 0 && project.spent / project.budget > 0.9;\n\n const editInitialValues: FormValues = {\n name: project.name,\n description: project.description,\n status: project.status,\n priority: project.priority,\n budget: project.budget,\n progress: project.progress,\n };\n\n const handleEdit = async (values: FormValues) => {\n const updates: Partial<Project> = {\n name: String(values.name ?? '').trim(),\n description: String(values.description ?? '').trim(),\n status: String(values.status ?? project.status) as Project['status'],\n priority: String(values.priority ?? project.priority) as Project['priority'],\n };\n if (typeof values.budget === 'number' && !Number.isNaN(values.budget)) {\n updates.budget = values.budget;\n }\n if (typeof values.progress === 'number' && !Number.isNaN(values.progress)) {\n updates.progress = values.progress;\n }\n await updateProject(project.id, updates);\n showToast('Project updated', 'success');\n };\n\n const handleDelete = async () => {\n if (deleting) return;\n setDeleting(true);\n try {\n await removeProject(project.id);\n showToast('Project deleted', 'success');\n setConfirmDeleteOpen(false);\n navigate('../projects/projects-page');\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Unable to delete project', 'error');\n } finally {\n setDeleting(false);\n }\n };\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{project.name}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">{project.description}</p>\n </div>\n <div className=\"flex items-center gap-2\">\n <div className=\"hidden gap-1 sm:flex\">\n <Badge variant={project.priority === 'critical' ? 'destructive' : 'default'}>\n {formatEnumLabel(project.priority)}\n </Badge>\n <Badge variant={project.status === 'active' ? 'default' : 'secondary'}>\n {formatEnumLabel(project.status)}\n </Badge>\n </div>\n <Button variant=\"outline\" size=\"sm\" onClick={() => editModal.openModal()}>\n <Pencil className=\"h-4 w-4 mr-1\" />\n Edit\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Delete project\"\n className=\"h-9 w-9 p-0 text-text-secondary hover:text-status-error-text\"\n onClick={() => setConfirmDeleteOpen(true)}\n >\n <Trash2 className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n <PagePurpose>\n Track a single project end-to-end — progress, budget, team, and timeline in one place — so\n you can spot risks early and keep delivery on schedule and on budget.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"crewfoundry-project-details\"\n steps={[\n {\n id: 'all-projects',\n label: 'View all projects',\n description: 'See how this project compares across your portfolio.',\n icon: <FolderKanban className=\"h-4 w-4\" />,\n onClick: () => navigate('../projects/projects-page'),\n },\n {\n id: 'team',\n label: 'Check the team',\n description: 'Review who is working on this and their workload.',\n icon: <Users className=\"h-4 w-4\" />,\n onClick: () => navigate('../teams/teams-page'),\n },\n {\n id: 'new-project',\n label: 'Start a new project',\n description: 'Kick off the next piece of work.',\n icon: <Target className=\"h-4 w-4\" />,\n onClick: () => navigate('../projects/create-project-page'),\n },\n ]}\n />\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Target className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{project.progress}%</p>\n <p className=\"text-xs text-text-secondary\">Progress</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <DollarSign className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">${(project.budget / 1000).toFixed(0)}K</p>\n <p className=\"text-xs text-text-secondary\">Budget</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Users className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{project.teamName}</p>\n <p className=\"text-xs text-text-secondary\">Team</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Calendar className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{project.endDate}</p>\n <p className=\"text-xs text-text-secondary\">Deadline</p>\n </CardContent>\n </Card>\n </div>\n <EmphasisPanel>\n <div className=\"mb-2 text-lg font-semibold\">Task Progress</div>\n <div className=\"space-y-2\">\n <div className=\"flex justify-between text-sm\">\n <span>\n {project.completedTasks} of {project.tasks} tasks completed\n </span>\n <span className=\"font-medium\">{project.progress}%</span>\n </div>\n <div className=\"h-3 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-3 bg-action-primary-bg rounded\"\n style={{ width: `${project.progress}%` }}\n />\n </div>\n </div>\n </EmphasisPanel>\n <Card>\n <CardHeader>\n <CardTitle>Budget Tracking</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-2\">\n <div className=\"flex justify-between text-sm\">\n <span>\n Spent ${(project.spent / 1000).toFixed(0)}K of ${(project.budget / 1000).toFixed(0)}\n K\n </span>\n <span className=\"font-medium\">{budgetPct}%</span>\n </div>\n <div className=\"h-3 bg-bg-sunken rounded overflow-hidden\">\n <div\n className={`h-3 rounded ${budgetOverrun ? 'bg-status-error-text' : 'bg-status-success-text'}`}\n style={{ width: `${budgetPct}%` }}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-4 mt-4 text-sm\">\n <div>\n <p className=\"text-xs text-text-secondary\">Manager</p>\n <p className=\"font-medium\">{project.managerName}</p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Timeline</p>\n <p>\n {project.startDate} — {project.endDate}\n </p>\n </div>\n </div>\n </CardContent>\n </Card>\n\n <FormModal\n open={editModal.open}\n onOpenChange={editModal.onOpenChange}\n title=\"Edit Project\"\n description={`Update the details for ${project.name}.`}\n fields={PROJECT_EDIT_FIELDS}\n initialValues={editInitialValues}\n submitLabel=\"Save Changes\"\n onSubmit={handleEdit}\n />\n\n <ResponsiveDialog open={confirmDeleteOpen} onOpenChange={setConfirmDeleteOpen}>\n <ResponsiveDialogContent className=\"sm:max-w-[440px]\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Delete {project.name}?</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n This permanently removes the project and its tracking data. This cannot be undone.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <ResponsiveDialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={() => setConfirmDeleteOpen(false)}\n disabled={deleting}\n >\n Cancel\n </Button>\n <Button type=\"button\" variant=\"destructive\" onClick={handleDelete} disabled={deleting}>\n {deleting ? 'Deleting…' : 'Delete Project'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -11,7 +11,7 @@ function y() {
|
|
|
11
11
|
let { t: y } = v(), b = r(), { projects: x } = e(), S = x.filter((e) => e.status === "active"), { page: C, pageSize: w, setPage: T } = _({ initialPageSize: 12 }), E = (e, t) => {
|
|
12
12
|
let n = y(e);
|
|
13
13
|
return n === e ? t : n;
|
|
14
|
-
}, D = x.slice((C - 1) * w, (C - 1) * w + w), O = (e) => b(
|
|
14
|
+
}, D = x.slice((C - 1) * w, (C - 1) * w + w), O = (e) => b(`../projects/project-details-page?id=${encodeURIComponent(e.id)}`);
|
|
15
15
|
return /* @__PURE__ */ a("div", {
|
|
16
16
|
className: "space-y-6 p-6",
|
|
17
17
|
children: [
|
|
@@ -36,7 +36,7 @@ function y() {
|
|
|
36
36
|
})
|
|
37
37
|
] }), /* @__PURE__ */ a(s, {
|
|
38
38
|
size: "sm",
|
|
39
|
-
onClick: () => b("
|
|
39
|
+
onClick: () => b("../projects/create-project-page"),
|
|
40
40
|
children: [/* @__PURE__ */ i(n, { className: "h-4 w-4 mr-1" }), E("crewfoundry.projects.actions.newProject", "New Project")]
|
|
41
41
|
})]
|
|
42
42
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectsProjectsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/ProjectsProjectsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Button,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n Pagination,\n} from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { Plus } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore, type Project } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\n\nexport function ProjectsProjectsPagePage() {\n const { t: translate } = useI18n();\n const navigate = useNavigate();\n const { projects } = useCrewFoundryStore();\n const activeProjects = projects.filter((p) => p.status === 'active');\n const { page, pageSize, setPage } = usePagination({ initialPageSize: 12 });\n const t = (key: string, fallback: string) => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n\n const pageProjects = projects.slice((page - 1) * pageSize, (page - 1) * pageSize + pageSize);\n\n const openProject = (project: Project) =>\n navigate(
|
|
1
|
+
{"version":3,"file":"ProjectsProjectsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/ProjectsProjectsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Button,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n Pagination,\n} from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { Plus } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore, type Project } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\n\nexport function ProjectsProjectsPagePage() {\n const { t: translate } = useI18n();\n const navigate = useNavigate();\n const { projects } = useCrewFoundryStore();\n const activeProjects = projects.filter((p) => p.status === 'active');\n const { page, pageSize, setPage } = usePagination({ initialPageSize: 12 });\n const t = (key: string, fallback: string) => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n\n const pageProjects = projects.slice((page - 1) * pageSize, (page - 1) * pageSize + pageSize);\n\n const openProject = (project: Project) =>\n navigate(`../projects/project-details-page?id=${encodeURIComponent(project.id)}`);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{t('crewfoundry.projects.title', 'Projects')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {activeProjects.length}{' '}\n {t('crewfoundry.projects.summary.activeProjects', 'active projects')}\n </p>\n <PagePurpose className=\"mt-1\">\n All your projects in one view — track status, progress, and budgets so nothing stalls\n and everything ships on time.\n </PagePurpose>\n </div>\n <Button size=\"sm\" onClick={() => navigate('../projects/create-project-page')}>\n <Plus className=\"h-4 w-4 mr-1\" />\n {t('crewfoundry.projects.actions.newProject', 'New Project')}\n </Button>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {projects.length === 0 ? (\n <div className=\"col-span-full\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No projects yet\"\n description=\"Create your first project to start tracking work.\"\n />\n </div>\n ) : (\n pageProjects.map((project, _idx) => (\n <GlassCard\n key={project.id}\n treatment={_idx === 0 && page === 1 ? 'glass' : 'plain'}\n glow={_idx === 0 && page === 1}\n role=\"button\"\n tabIndex={0}\n aria-label={`Open ${project.name} project detail`}\n onClick={() => openProject(project)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n openProject(project);\n }\n }}\n className=\"hover:shadow-md transition-shadow cursor-pointer focus-visible:outline-none focus-visible:shadow-[var(--shadow-focus)]\"\n >\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">{project.name}</CardTitle>\n <Badge\n variant={\n project.status === 'active'\n ? 'default'\n : project.status === 'completed'\n ? 'secondary'\n : 'outline'\n }\n className=\"text-[10px]\"\n >\n {formatEnumLabel(project.status)}\n </Badge>\n </div>\n <CardDescription>{project.description}</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-2 mb-3\">\n <div className=\"flex justify-between text-xs\">\n <span>\n {project.completedTasks}/{project.tasks}{' '}\n {t('crewfoundry.projects.card.tasks', 'tasks')}\n </span>\n <span>{project.progress}%</span>\n </div>\n <div className=\"h-2 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-2 bg-action-primary-bg rounded\"\n style={{ width: `${project.progress}%` }}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-2 text-xs text-text-secondary\">\n <span>\n {t('crewfoundry.projects.card.team', 'Team')}: {project.teamName}\n </span>\n <span>\n {t('crewfoundry.projects.card.manager', 'Manager')}: {project.managerName}\n </span>\n <span>\n {t('crewfoundry.projects.card.budget', 'Budget')}: $\n {(project.budget / 1000).toFixed(0)}K\n </span>\n <span>\n {t('crewfoundry.projects.card.spent', 'Spent')}: $\n {(project.spent / 1000).toFixed(0)}K\n </span>\n </div>\n <div className=\"flex gap-1 mt-2\">\n <Badge\n variant={\n project.priority === 'critical'\n ? 'destructive'\n : project.priority === 'high'\n ? 'default'\n : 'secondary'\n }\n className=\"text-[10px]\"\n >\n {formatEnumLabel(project.priority)}\n </Badge>\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {project.startDate} — {project.endDate}\n </Badge>\n </div>\n </CardContent>\n </GlassCard>\n ))\n )}\n </div>\n\n {projects.length > pageSize && (\n <Pagination\n page={page}\n pageSize={pageSize}\n totalItems={projects.length}\n onPageChange={setPage}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -47,21 +47,21 @@ function C() {
|
|
|
47
47
|
label: "View team analytics",
|
|
48
48
|
description: "See productivity and workload trends for this team.",
|
|
49
49
|
icon: /* @__PURE__ */ u(n, { className: "h-4 w-4" }),
|
|
50
|
-
onClick: () => C("
|
|
50
|
+
onClick: () => C("../teams/team-analytics-page")
|
|
51
51
|
},
|
|
52
52
|
{
|
|
53
53
|
id: "settings",
|
|
54
54
|
label: "Adjust team settings",
|
|
55
55
|
description: "Manage membership, permissions, and preferences.",
|
|
56
56
|
icon: /* @__PURE__ */ u(a, { className: "h-4 w-4" }),
|
|
57
|
-
onClick: () => C(
|
|
57
|
+
onClick: () => C(`../teams/team-settings-page?id=${encodeURIComponent(k.id)}`)
|
|
58
58
|
},
|
|
59
59
|
{
|
|
60
60
|
id: "directory",
|
|
61
61
|
label: "Browse all teams",
|
|
62
62
|
description: "Explore how the rest of the organization is structured.",
|
|
63
63
|
icon: /* @__PURE__ */ u(s, { className: "h-4 w-4" }),
|
|
64
|
-
onClick: () => C("
|
|
64
|
+
onClick: () => C("../teams/team-directory-page")
|
|
65
65
|
}
|
|
66
66
|
]
|
|
67
67
|
}),
|
|
@@ -186,9 +186,9 @@ function C() {
|
|
|
186
186
|
role: "button",
|
|
187
187
|
tabIndex: 0,
|
|
188
188
|
"aria-label": `Open ${e.name} project detail`,
|
|
189
|
-
onClick: () => C(
|
|
189
|
+
onClick: () => C(`../projects/project-details-page?id=${encodeURIComponent(e.id)}`),
|
|
190
190
|
onKeyDown: (t) => {
|
|
191
|
-
(t.key === "Enter" || t.key === " ") && (t.preventDefault(), C(
|
|
191
|
+
(t.key === "Enter" || t.key === " ") && (t.preventDefault(), C(`../projects/project-details-page?id=${encodeURIComponent(e.id)}`));
|
|
192
192
|
},
|
|
193
193
|
className: "flex items-center justify-between rounded border p-3 cursor-pointer hover:bg-bg-sunken/50 focus-visible:outline-none focus-visible:shadow-[var(--shadow-focus)]",
|
|
194
194
|
children: [/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamsTeamDetailPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/TeamsTeamDetailPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n EmphasisPanel,\n GradientText,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n ResponsiveTable,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { BarChart3, Calendar, FolderKanban, Settings, Star, Users } from 'lucide-react';\nimport { useNavigate, useSearchParams } from 'react-router-dom';\nimport { useCrewFoundryStore, type Employee } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\n\nexport function TeamsTeamDetailPagePage() {\n const navigate = useNavigate();\n const [searchParams] = useSearchParams();\n const { teams, employees, projects } = useCrewFoundryStore();\n const teamId = searchParams.get('id');\n const team = teams.find((t) => t.id === teamId) ?? teams[0];\n if (!team)\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Team not found\"\n description=\"This team is no longer available.\"\n />\n </div>\n );\n const teamMembers = employees.filter((e) => team.members.includes(e.id));\n const teamProjects = projects.filter((p) => p.teamId === team.id);\n\n const memberColumns: ResponsiveTableColumn<Employee>[] = [\n {\n key: 'name',\n header: 'Name',\n priority: 'primary',\n cell: (emp) => (\n <span className=\"font-medium text-text-primary\">\n {emp.firstName} {emp.lastName}\n </span>\n ),\n },\n {\n key: 'position',\n header: 'Position',\n priority: 'secondary',\n cell: (emp) => <span className=\"text-text-secondary\">{emp.position}</span>,\n },\n {\n key: 'performance',\n header: 'Performance',\n priority: 'secondary',\n cell: (emp) => (\n <span className=\"flex items-center gap-1 md:justify-center\">\n <Star className=\"h-3 w-3 text-status-warning-text\" />\n {emp.performanceScore}\n </span>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n priority: 'primary',\n cell: (emp) => (\n <Badge variant={emp.status === 'active' ? 'default' : 'secondary'} className=\"text-[10px]\">\n {formatEnumLabel(emp.status)}\n </Badge>\n ),\n },\n ];\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{team.name}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">{team.description}</p>\n <PagePurpose className=\"mt-2\">\n A complete view of one team — who's on it, what they're working on, and how\n they're performing — so you can support the group and keep its projects on track.\n </PagePurpose>\n </div>\n <Badge variant={team.status === 'active' ? 'default' : 'secondary'}>{formatEnumLabel(team.status)}</Badge>\n </div>\n\n <NextSteps\n storageKey=\"crewfoundry-team-details\"\n steps={[\n {\n id: 'analytics',\n label: 'View team analytics',\n description: 'See productivity and workload trends for this team.',\n icon: <BarChart3 className=\"h-4 w-4\" />,\n onClick: () => navigate('../../teams/team-analytics-page'),\n },\n {\n id: 'settings',\n label: 'Adjust team settings',\n description: 'Manage membership, permissions, and preferences.',\n icon: <Settings className=\"h-4 w-4\" />,\n onClick: () =>\n navigate(`../../teams/team-settings-page?id=${encodeURIComponent(team.id)}`),\n },\n {\n id: 'directory',\n label: 'Browse all teams',\n description: 'Explore how the rest of the organization is structured.',\n icon: <Users className=\"h-4 w-4\" />,\n onClick: () => navigate('../../teams/team-directory-page'),\n },\n ]}\n />\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Users className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{team.memberCount}</p>\n <p className=\"text-xs text-text-secondary\">Members</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <FolderKanban className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{team.projectCount}</p>\n <p className=\"text-xs text-text-secondary\">Projects</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Star className=\"h-5 w-5 mx-auto text-status-warning-text mb-1\" />\n <p className=\"text-lg font-bold\">{team.leadName}</p>\n <p className=\"text-xs text-text-secondary\">Lead</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Calendar className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{team.createdAt}</p>\n <p className=\"text-xs text-text-secondary\">Created</p>\n </CardContent>\n </Card>\n </div>\n <EmphasisPanel>\n <div className=\"mb-2 text-lg font-semibold\">Team Members</div>\n {teamMembers.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title=\"No members yet\"\n description=\"Add people to this team to start collaborating.\"\n />\n ) : (\n <ResponsiveTable columns={memberColumns} data={teamMembers} rowKey={(emp) => emp.id} />\n )}\n </EmphasisPanel>\n {teamProjects.length > 0 && (\n <Card>\n <CardHeader>\n <CardTitle>Team Projects</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {teamProjects.map((p) => (\n <div\n key={p.id}\n role=\"button\"\n tabIndex={0}\n aria-label={`Open ${p.name} project detail`}\n onClick={() =>\n navigate(`../../projects/project-details-page?id=${encodeURIComponent(p.id)}`)\n }\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n navigate(`../../projects/project-details-page?id=${encodeURIComponent(p.id)}`);\n }\n }}\n className=\"flex items-center justify-between rounded border p-3 cursor-pointer hover:bg-bg-sunken/50 focus-visible:outline-none focus-visible:shadow-[var(--shadow-focus)]\"\n >\n <div>\n <p className=\"text-sm font-medium\">{p.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {p.startDate} — {p.endDate}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <div className=\"w-24 h-2 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-2 bg-action-primary-bg rounded\"\n style={{ width: `${p.progress}%` }}\n />\n </div>\n <span className=\"text-xs\">{p.progress}%</span>\n <Badge\n variant={p.status === 'active' ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {formatEnumLabel(p.status)}\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TeamsTeamDetailPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/TeamsTeamDetailPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n EmphasisPanel,\n GradientText,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n ResponsiveTable,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { BarChart3, Calendar, FolderKanban, Settings, Star, Users } from 'lucide-react';\nimport { useNavigate, useSearchParams } from 'react-router-dom';\nimport { useCrewFoundryStore, type Employee } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\n\nexport function TeamsTeamDetailPagePage() {\n const navigate = useNavigate();\n const [searchParams] = useSearchParams();\n const { teams, employees, projects } = useCrewFoundryStore();\n const teamId = searchParams.get('id');\n const team = teams.find((t) => t.id === teamId) ?? teams[0];\n if (!team)\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Team not found\"\n description=\"This team is no longer available.\"\n />\n </div>\n );\n const teamMembers = employees.filter((e) => team.members.includes(e.id));\n const teamProjects = projects.filter((p) => p.teamId === team.id);\n\n const memberColumns: ResponsiveTableColumn<Employee>[] = [\n {\n key: 'name',\n header: 'Name',\n priority: 'primary',\n cell: (emp) => (\n <span className=\"font-medium text-text-primary\">\n {emp.firstName} {emp.lastName}\n </span>\n ),\n },\n {\n key: 'position',\n header: 'Position',\n priority: 'secondary',\n cell: (emp) => <span className=\"text-text-secondary\">{emp.position}</span>,\n },\n {\n key: 'performance',\n header: 'Performance',\n priority: 'secondary',\n cell: (emp) => (\n <span className=\"flex items-center gap-1 md:justify-center\">\n <Star className=\"h-3 w-3 text-status-warning-text\" />\n {emp.performanceScore}\n </span>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n priority: 'primary',\n cell: (emp) => (\n <Badge variant={emp.status === 'active' ? 'default' : 'secondary'} className=\"text-[10px]\">\n {formatEnumLabel(emp.status)}\n </Badge>\n ),\n },\n ];\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{team.name}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">{team.description}</p>\n <PagePurpose className=\"mt-2\">\n A complete view of one team — who's on it, what they're working on, and how\n they're performing — so you can support the group and keep its projects on track.\n </PagePurpose>\n </div>\n <Badge variant={team.status === 'active' ? 'default' : 'secondary'}>\n {formatEnumLabel(team.status)}\n </Badge>\n </div>\n\n <NextSteps\n storageKey=\"crewfoundry-team-details\"\n steps={[\n {\n id: 'analytics',\n label: 'View team analytics',\n description: 'See productivity and workload trends for this team.',\n icon: <BarChart3 className=\"h-4 w-4\" />,\n onClick: () => navigate('../teams/team-analytics-page'),\n },\n {\n id: 'settings',\n label: 'Adjust team settings',\n description: 'Manage membership, permissions, and preferences.',\n icon: <Settings className=\"h-4 w-4\" />,\n onClick: () =>\n navigate(`../teams/team-settings-page?id=${encodeURIComponent(team.id)}`),\n },\n {\n id: 'directory',\n label: 'Browse all teams',\n description: 'Explore how the rest of the organization is structured.',\n icon: <Users className=\"h-4 w-4\" />,\n onClick: () => navigate('../teams/team-directory-page'),\n },\n ]}\n />\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Users className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{team.memberCount}</p>\n <p className=\"text-xs text-text-secondary\">Members</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <FolderKanban className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{team.projectCount}</p>\n <p className=\"text-xs text-text-secondary\">Projects</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Star className=\"h-5 w-5 mx-auto text-status-warning-text mb-1\" />\n <p className=\"text-lg font-bold\">{team.leadName}</p>\n <p className=\"text-xs text-text-secondary\">Lead</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Calendar className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{team.createdAt}</p>\n <p className=\"text-xs text-text-secondary\">Created</p>\n </CardContent>\n </Card>\n </div>\n <EmphasisPanel>\n <div className=\"mb-2 text-lg font-semibold\">Team Members</div>\n {teamMembers.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title=\"No members yet\"\n description=\"Add people to this team to start collaborating.\"\n />\n ) : (\n <ResponsiveTable columns={memberColumns} data={teamMembers} rowKey={(emp) => emp.id} />\n )}\n </EmphasisPanel>\n {teamProjects.length > 0 && (\n <Card>\n <CardHeader>\n <CardTitle>Team Projects</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {teamProjects.map((p) => (\n <div\n key={p.id}\n role=\"button\"\n tabIndex={0}\n aria-label={`Open ${p.name} project detail`}\n onClick={() =>\n navigate(`../projects/project-details-page?id=${encodeURIComponent(p.id)}`)\n }\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n navigate(`../projects/project-details-page?id=${encodeURIComponent(p.id)}`);\n }\n }}\n className=\"flex items-center justify-between rounded border p-3 cursor-pointer hover:bg-bg-sunken/50 focus-visible:outline-none focus-visible:shadow-[var(--shadow-focus)]\"\n >\n <div>\n <p className=\"text-sm font-medium\">{p.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {p.startDate} — {p.endDate}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <div className=\"w-24 h-2 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-2 bg-action-primary-bg rounded\"\n style={{ width: `${p.progress}%` }}\n />\n </div>\n <span className=\"text-xs\">{p.progress}%</span>\n <Badge\n variant={p.status === 'active' ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {formatEnumLabel(p.status)}\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|