@burdenoff/microfe-crewfoundry 2026.704.2 → 2026.704.4
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/AdminAdminPagePage.js +76 -75
- package/dist/crewfoundry/pages/AdminAdminPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js +118 -117
- package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js +115 -114
- package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js +105 -104
- package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsTeamsPagePage.js +52 -51
- package/dist/crewfoundry/pages/CommunicationsTeamsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js +94 -93
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyCompanyProfilePagePage.js +97 -96
- package/dist/crewfoundry/pages/CompanyCompanyProfilePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js +68 -67
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardAnalyticsPagePage.js +96 -95
- package/dist/crewfoundry/pages/DashboardAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardDashboardPagePage.js +153 -152
- package/dist/crewfoundry/pages/DashboardDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardEnhancedDashboardPagePage.js +107 -106
- package/dist/crewfoundry/pages/DashboardEnhancedDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringApplicationsPagePage.js +77 -76
- package/dist/crewfoundry/pages/HiringApplicationsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.js +80 -79
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.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/HiringInterviewsPagePage.js +102 -101
- package/dist/crewfoundry/pages/HiringInterviewsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js +126 -125
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobsPagePage.js +64 -63
- package/dist/crewfoundry/pages/HiringJobsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.js +96 -95
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js +127 -126
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrPayrollPagePage.js +118 -117
- package/dist/crewfoundry/pages/HrPayrollPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js +123 -122
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js +77 -76
- package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js +49 -48
- package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js +62 -61
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPagePage.js +72 -71
- package/dist/crewfoundry/pages/LearningLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js +70 -69
- package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js +57 -56
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.js +58 -57
- package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.js +122 -121
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js +70 -69
- package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js +41 -40
- package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js +65 -64
- package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.js +75 -74
- package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js +60 -59
- package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js +53 -52
- package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.js +37 -36
- package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsProjectDetailsPagePage.js +126 -125
- package/dist/crewfoundry/pages/ProjectsProjectDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js +62 -61
- package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamAnalyticsPagePage.js +50 -49
- package/dist/crewfoundry/pages/TeamsTeamAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js +72 -71
- package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js +40 -39
- package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamSettingsPagePage.js +130 -129
- package/dist/crewfoundry/pages/TeamsTeamSettingsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamsPagePage.js +60 -59
- package/dist/crewfoundry/pages/TeamsTeamsPagePage.js.map +1 -1
- package/dist/crewfoundry/store/crewfoundryStore.js +177 -146
- package/dist/crewfoundry/store/crewfoundryStore.js.map +1 -1
- package/dist/crewfoundry/utils/formatEnumLabel.js +8 -0
- package/dist/crewfoundry/utils/formatEnumLabel.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommunicationsTeamsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/CommunicationsTeamsPagePage.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 { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { FolderKanban, Plus, Users } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n FormModal,\n useFormModal,\n type FieldConfig,\n type FormValues,\n} from '../components/forms/FormModal';\nimport { useCrewFoundryStore, type Team } from '../store/crewfoundryStore';\n\n// CreateCrewTeamInput accepts only this field subset; the store spreads the\n// object straight into `input`, so we send exactly the accepted shape.\nconst CHANNEL_FIELDS: FieldConfig[] = [\n { name: 'name', label: 'Channel Name', type: 'text', required: true },\n { name: 'department', label: 'Department', type: 'text', placeholder: 'e.g. Engineering' },\n { name: 'leadName', label: 'Channel Lead', type: 'text', placeholder: 'Lead full name' },\n {\n name: 'status',\n label: 'Status',\n type: 'select',\n options: [\n { label: 'Active', value: 'active' },\n { label: 'Forming', value: 'forming' },\n ],\n },\n { name: 'description', label: 'Description', type: 'textarea', colSpan: 2 },\n];\n\nexport function CommunicationsTeamsPagePage() {\n const navigate = useNavigate();\n const { teams } = useCrewFoundryStore();\n const addTeam = useCrewFoundryStore((state) => state.addTeam);\n const createModal = useFormModal();\n const { page, pageSize, setPage } = usePagination({ initialPageSize: 12 });\n\n const pageTeams = teams.slice((page - 1) * pageSize, (page - 1) * pageSize + pageSize);\n\n const openTeam = (team: Team) =>\n navigate(`../../teams/team-detail-page?id=${encodeURIComponent(team.id)}`);\n\n const handleCreate = async (values: FormValues) => {\n const payload: Partial<Team> = {\n name: String(values.name ?? '').trim(),\n description: String(values.description ?? '').trim(),\n department: String(values.department ?? '').trim() || 'operations',\n leadName: String(values.leadName ?? '').trim(),\n status: (String(values.status ?? 'forming') as Team['status']) || 'forming',\n members: [],\n };\n await addTeam(payload as Team);\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-start justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Team Channels</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">{teams.length} active team channels</p>\n <PagePurpose className=\"mt-1\">\n Jump into team channels to collaborate with your groups — a focused place for each\n team's ongoing discussion.\n </PagePurpose>\n </div>\n <Button size=\"sm\" onClick={() => createModal.openModal()}>\n <Plus className=\"h-4 w-4 mr-1\" />\n New Channel\n </Button>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {teams.length === 0 ? (\n <div className=\"col-span-full\">\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title=\"No team channels\"\n description=\"Create a channel to give a team a focused place to collaborate.\"\n />\n </div>\n ) : (\n pageTeams.map((team, _idx) => (\n <GlassCard\n key={team.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 ${team.name} channel`}\n onClick={() => openTeam(team)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n openTeam(team);\n }\n }}\n className=\"cursor-pointer hover:shadow-md transition-shadow 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\">{team.name}</CardTitle>\n <Badge\n variant={team.status === 'active' ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {team.status}\n </Badge>\n </div>\n <CardDescription>{team.description}</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"flex items-center justify-between text-sm text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Users className=\"h-3 w-3\" />\n {team.memberCount} members\n </span>\n <span className=\"flex items-center gap-1\">\n <FolderKanban className=\"h-3 w-3\" />\n {team.projectCount} projects\n </span>\n </div>\n <div className=\"mt-2 text-xs text-text-secondary\">\n <span>Lead: {team.leadName}</span> · <span>{team.department}</span>\n </div>\n </CardContent>\n </GlassCard>\n ))\n )}\n </div>\n\n {teams.length > pageSize && (\n <Pagination\n page={page}\n pageSize={pageSize}\n totalItems={teams.length}\n onPageChange={setPage}\n />\n )}\n\n <FormModal\n open={createModal.open}\n onOpenChange={createModal.onOpenChange}\n title=\"New Channel\"\n description=\"Give a team a focused place to collaborate.\"\n fields={CHANNEL_FIELDS}\n submitLabel=\"Create Channel\"\n onSubmit={handleCreate}\n />\n </div>\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"CommunicationsTeamsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/CommunicationsTeamsPagePage.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 { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { FolderKanban, Plus, Users } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n FormModal,\n useFormModal,\n type FieldConfig,\n type FormValues,\n} from '../components/forms/FormModal';\nimport { useCrewFoundryStore, type Team } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\n\n// CreateCrewTeamInput accepts only this field subset; the store spreads the\n// object straight into `input`, so we send exactly the accepted shape.\nconst CHANNEL_FIELDS: FieldConfig[] = [\n { name: 'name', label: 'Channel Name', type: 'text', required: true },\n { name: 'department', label: 'Department', type: 'text', placeholder: 'e.g. Engineering' },\n { name: 'leadName', label: 'Channel Lead', type: 'text', placeholder: 'Lead full name' },\n {\n name: 'status',\n label: 'Status',\n type: 'select',\n options: [\n { label: 'Active', value: 'active' },\n { label: 'Forming', value: 'forming' },\n ],\n },\n { name: 'description', label: 'Description', type: 'textarea', colSpan: 2 },\n];\n\nexport function CommunicationsTeamsPagePage() {\n const navigate = useNavigate();\n const { teams } = useCrewFoundryStore();\n const addTeam = useCrewFoundryStore((state) => state.addTeam);\n const createModal = useFormModal();\n const { page, pageSize, setPage } = usePagination({ initialPageSize: 12 });\n\n const pageTeams = teams.slice((page - 1) * pageSize, (page - 1) * pageSize + pageSize);\n\n const openTeam = (team: Team) =>\n navigate(`../../teams/team-detail-page?id=${encodeURIComponent(team.id)}`);\n\n const handleCreate = async (values: FormValues) => {\n const payload: Partial<Team> = {\n name: String(values.name ?? '').trim(),\n description: String(values.description ?? '').trim(),\n department: String(values.department ?? '').trim() || 'operations',\n leadName: String(values.leadName ?? '').trim(),\n status: (String(values.status ?? 'forming') as Team['status']) || 'forming',\n members: [],\n };\n await addTeam(payload as Team);\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-start justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Team Channels</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">{teams.length} active team channels</p>\n <PagePurpose className=\"mt-1\">\n Jump into team channels to collaborate with your groups — a focused place for each\n team's ongoing discussion.\n </PagePurpose>\n </div>\n <Button size=\"sm\" onClick={() => createModal.openModal()}>\n <Plus className=\"h-4 w-4 mr-1\" />\n New Channel\n </Button>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {teams.length === 0 ? (\n <div className=\"col-span-full\">\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title=\"No team channels\"\n description=\"Create a channel to give a team a focused place to collaborate.\"\n />\n </div>\n ) : (\n pageTeams.map((team, _idx) => (\n <GlassCard\n key={team.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 ${team.name} channel`}\n onClick={() => openTeam(team)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n openTeam(team);\n }\n }}\n className=\"cursor-pointer hover:shadow-md transition-shadow 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\">{team.name}</CardTitle>\n <Badge\n variant={team.status === 'active' ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {formatEnumLabel(team.status)}\n </Badge>\n </div>\n <CardDescription>{team.description}</CardDescription>\n </CardHeader>\n <CardContent>\n <div className=\"flex items-center justify-between text-sm text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Users className=\"h-3 w-3\" />\n {team.memberCount} members\n </span>\n <span className=\"flex items-center gap-1\">\n <FolderKanban className=\"h-3 w-3\" />\n {team.projectCount} projects\n </span>\n </div>\n <div className=\"mt-2 text-xs text-text-secondary\">\n <span>Lead: {team.leadName}</span> · <span>{team.department}</span>\n </div>\n </CardContent>\n </GlassCard>\n ))\n )}\n </div>\n\n {teams.length > pageSize && (\n <Pagination\n page={page}\n pageSize={pageSize}\n totalItems={teams.length}\n onPageChange={setPage}\n />\n )}\n\n <FormModal\n open={createModal.open}\n onOpenChange={createModal.onOpenChange}\n title=\"New Channel\"\n description=\"Give a team a focused place to collaborate.\"\n fields={CHANNEL_FIELDS}\n submitLabel=\"Create Channel\"\n onSubmit={handleCreate}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { useCrewFoundryStore as e } from "../store/crewfoundryStore.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
2
|
+
import { formatEnumLabel as t } from "../utils/formatEnumLabel.js";
|
|
3
|
+
import { FormModal as n, useFormModal as r } from "../components/forms/FormModal.js";
|
|
4
|
+
import { useState as i } from "react";
|
|
5
|
+
import { Building2 as a, DollarSign as o, FolderKanban as s, Pencil as c, Plus as l, Search as u, Trash2 as d } from "lucide-react";
|
|
6
|
+
import { jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
7
|
+
import { AlertDialog as m, AlertDialogAction as h, AlertDialogCancel as g, AlertDialogContent as _, AlertDialogDescription as v, AlertDialogFooter as ee, AlertDialogHeader as te, AlertDialogTitle as ne, AlertDialogTrigger as re, Badge as ie, Button as y, Card as b, CardContent as x, CardHeader as S, CardTitle as C, GlassCard as w, GradientText as T, IllustratedEmptyState as E, Input as D, PagePurpose as O, Pagination as k, ResponsiveTable as A, Select as j, SelectContent as M, SelectItem as N, SelectTrigger as P, SelectValue as F } from "@burdenoff/fe-libs/ui";
|
|
8
|
+
import { usePagination as I } from "@burdenoff/fe-libs/shared/hooks";
|
|
8
9
|
//#region src/crewfoundry/pages/CompanyClientManagementPagePage.tsx
|
|
9
|
-
var
|
|
10
|
+
var L = [
|
|
10
11
|
{
|
|
11
12
|
name: "name",
|
|
12
13
|
label: "Client name",
|
|
@@ -67,7 +68,7 @@ var F = [
|
|
|
67
68
|
label: "Client since",
|
|
68
69
|
type: "date"
|
|
69
70
|
}
|
|
70
|
-
],
|
|
71
|
+
], ae = (e) => ({
|
|
71
72
|
name: e.name,
|
|
72
73
|
industry: e.industry,
|
|
73
74
|
contactPerson: e.contactPerson,
|
|
@@ -77,7 +78,7 @@ var F = [
|
|
|
77
78
|
revenue: e.revenue,
|
|
78
79
|
projects: e.projects,
|
|
79
80
|
since: e.since ? e.since.slice(0, 10) : ""
|
|
80
|
-
}),
|
|
81
|
+
}), R = (e, t) => ({
|
|
81
82
|
id: t?.id ?? crypto.randomUUID(),
|
|
82
83
|
name: String(e.name ?? ""),
|
|
83
84
|
industry: String(e.industry ?? ""),
|
|
@@ -88,69 +89,69 @@ var F = [
|
|
|
88
89
|
revenue: typeof e.revenue == "number" ? e.revenue : Number(e.revenue) || 0,
|
|
89
90
|
projects: typeof e.projects == "number" ? e.projects : Number(e.projects) || 0,
|
|
90
91
|
since: String(e.since ?? "")
|
|
91
|
-
}),
|
|
92
|
-
function
|
|
93
|
-
let { clients:
|
|
94
|
-
let t = `${e.name} ${e.industry} ${e.contactPerson} ${e.email}`.toLowerCase().includes(
|
|
92
|
+
}), z = (e) => e === "active" ? "default" : e === "prospect" ? "secondary" : "destructive";
|
|
93
|
+
function B() {
|
|
94
|
+
let { clients: B, addClient: V, updateClient: H, removeClient: U } = e(), W = r(), [G, K] = i(""), [q, J] = i("all"), { page: oe, pageSize: Y, offset: X, setPage: Z } = I({ initialPageSize: 25 }), Q = B.reduce((e, t) => e + t.revenue, 0), se = B.filter((e) => e.status === "active"), $ = B.filter((e) => {
|
|
95
|
+
let t = `${e.name} ${e.industry} ${e.contactPerson} ${e.email}`.toLowerCase().includes(G.toLowerCase()), n = q === "all" || e.status === q;
|
|
95
96
|
return t && n;
|
|
96
|
-
}),
|
|
97
|
-
return /* @__PURE__ */
|
|
97
|
+
}), ce = $.slice(X, X + Y);
|
|
98
|
+
return /* @__PURE__ */ p("div", {
|
|
98
99
|
className: "space-y-6 p-6",
|
|
99
100
|
children: [
|
|
100
|
-
/* @__PURE__ */
|
|
101
|
+
/* @__PURE__ */ p("div", {
|
|
101
102
|
className: "flex items-center justify-between",
|
|
102
|
-
children: [/* @__PURE__ */
|
|
103
|
-
/* @__PURE__ */
|
|
103
|
+
children: [/* @__PURE__ */ p("div", { children: [
|
|
104
|
+
/* @__PURE__ */ f("h1", {
|
|
104
105
|
className: "text-2xl font-semibold",
|
|
105
|
-
children: /* @__PURE__ */
|
|
106
|
+
children: /* @__PURE__ */ f(T, { children: "Client Management" })
|
|
106
107
|
}),
|
|
107
|
-
/* @__PURE__ */
|
|
108
|
+
/* @__PURE__ */ p("p", {
|
|
108
109
|
className: "text-sm text-text-secondary",
|
|
109
110
|
children: [
|
|
110
|
-
|
|
111
|
+
B.length,
|
|
111
112
|
" clients · $",
|
|
112
113
|
(Q / 1e6).toFixed(1),
|
|
113
114
|
"M revenue"
|
|
114
115
|
]
|
|
115
116
|
}),
|
|
116
|
-
/* @__PURE__ */
|
|
117
|
+
/* @__PURE__ */ f(O, {
|
|
117
118
|
className: "mt-1",
|
|
118
119
|
children: "Keep track of the clients you work with — their contacts, status, and engagements — so account relationships never slip through the cracks."
|
|
119
120
|
})
|
|
120
|
-
] }), /* @__PURE__ */
|
|
121
|
+
] }), /* @__PURE__ */ p(y, {
|
|
121
122
|
size: "sm",
|
|
122
|
-
onClick: () =>
|
|
123
|
-
children: [/* @__PURE__ */
|
|
123
|
+
onClick: () => W.openModal(),
|
|
124
|
+
children: [/* @__PURE__ */ f(l, { className: "h-4 w-4 mr-1" }), "Add Client"]
|
|
124
125
|
})]
|
|
125
126
|
}),
|
|
126
|
-
/* @__PURE__ */
|
|
127
|
+
/* @__PURE__ */ p("div", {
|
|
127
128
|
className: "grid gap-4 md:grid-cols-3",
|
|
128
129
|
children: [
|
|
129
|
-
/* @__PURE__ */
|
|
130
|
+
/* @__PURE__ */ p(w, {
|
|
130
131
|
treatment: "glass",
|
|
131
132
|
glow: !0,
|
|
132
133
|
className: "border-border-seam",
|
|
133
|
-
children: [/* @__PURE__ */
|
|
134
|
+
children: [/* @__PURE__ */ p(S, {
|
|
134
135
|
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
135
|
-
children: [/* @__PURE__ */
|
|
136
|
+
children: [/* @__PURE__ */ f(C, {
|
|
136
137
|
className: "text-sm font-medium",
|
|
137
138
|
children: "Active Clients"
|
|
138
|
-
}), /* @__PURE__ */
|
|
139
|
-
}), /* @__PURE__ */
|
|
139
|
+
}), /* @__PURE__ */ f(a, { className: "h-4 w-4 text-text-secondary" })]
|
|
140
|
+
}), /* @__PURE__ */ f(x, { children: /* @__PURE__ */ f("div", {
|
|
140
141
|
className: "text-2xl font-bold tabular-nums",
|
|
141
|
-
children:
|
|
142
|
+
children: se.length
|
|
142
143
|
}) })]
|
|
143
144
|
}),
|
|
144
|
-
/* @__PURE__ */
|
|
145
|
+
/* @__PURE__ */ p(w, {
|
|
145
146
|
treatment: "metric",
|
|
146
147
|
className: "border-border-seam",
|
|
147
|
-
children: [/* @__PURE__ */
|
|
148
|
+
children: [/* @__PURE__ */ p(S, {
|
|
148
149
|
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
149
|
-
children: [/* @__PURE__ */
|
|
150
|
+
children: [/* @__PURE__ */ f(C, {
|
|
150
151
|
className: "text-sm font-medium",
|
|
151
152
|
children: "Total Revenue"
|
|
152
|
-
}), /* @__PURE__ */
|
|
153
|
-
}), /* @__PURE__ */
|
|
153
|
+
}), /* @__PURE__ */ f(o, { className: "h-4 w-4 text-text-secondary" })]
|
|
154
|
+
}), /* @__PURE__ */ f(x, { children: /* @__PURE__ */ p("div", {
|
|
154
155
|
className: "text-2xl font-bold tabular-nums",
|
|
155
156
|
children: [
|
|
156
157
|
"$",
|
|
@@ -159,75 +160,75 @@ function z() {
|
|
|
159
160
|
]
|
|
160
161
|
}) })]
|
|
161
162
|
}),
|
|
162
|
-
/* @__PURE__ */
|
|
163
|
+
/* @__PURE__ */ p(w, {
|
|
163
164
|
treatment: "metric",
|
|
164
165
|
className: "border-border-seam",
|
|
165
|
-
children: [/* @__PURE__ */
|
|
166
|
+
children: [/* @__PURE__ */ p(S, {
|
|
166
167
|
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
167
|
-
children: [/* @__PURE__ */
|
|
168
|
+
children: [/* @__PURE__ */ f(C, {
|
|
168
169
|
className: "text-sm font-medium",
|
|
169
170
|
children: "Total Projects"
|
|
170
|
-
}), /* @__PURE__ */
|
|
171
|
-
}), /* @__PURE__ */
|
|
171
|
+
}), /* @__PURE__ */ f(s, { className: "h-4 w-4 text-text-secondary" })]
|
|
172
|
+
}), /* @__PURE__ */ f(x, { children: /* @__PURE__ */ f("div", {
|
|
172
173
|
className: "text-2xl font-bold tabular-nums",
|
|
173
|
-
children:
|
|
174
|
+
children: B.reduce((e, t) => e + t.projects, 0)
|
|
174
175
|
}) })]
|
|
175
176
|
})
|
|
176
177
|
]
|
|
177
178
|
}),
|
|
178
|
-
/* @__PURE__ */
|
|
179
|
+
/* @__PURE__ */ p(b, { children: [/* @__PURE__ */ f(S, { children: /* @__PURE__ */ f(C, { children: "Client Portfolio" }) }), /* @__PURE__ */ p(x, {
|
|
179
180
|
className: "space-y-4",
|
|
180
181
|
children: [
|
|
181
|
-
/* @__PURE__ */
|
|
182
|
+
/* @__PURE__ */ p("div", {
|
|
182
183
|
className: "flex flex-col gap-2 sm:flex-row sm:items-center",
|
|
183
|
-
children: [/* @__PURE__ */
|
|
184
|
+
children: [/* @__PURE__ */ p("div", {
|
|
184
185
|
className: "relative flex-1",
|
|
185
|
-
children: [/* @__PURE__ */ u
|
|
186
|
+
children: [/* @__PURE__ */ f(u, { className: "absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ f(D, {
|
|
186
187
|
placeholder: "Search clients...",
|
|
187
188
|
className: "pl-9",
|
|
188
|
-
value:
|
|
189
|
+
value: G,
|
|
189
190
|
onChange: (e) => {
|
|
190
|
-
|
|
191
|
+
K(e.target.value), Z(1);
|
|
191
192
|
}
|
|
192
193
|
})]
|
|
193
|
-
}), /* @__PURE__ */
|
|
194
|
-
value:
|
|
194
|
+
}), /* @__PURE__ */ p(j, {
|
|
195
|
+
value: q,
|
|
195
196
|
onValueChange: (e) => {
|
|
196
|
-
|
|
197
|
+
J(e), Z(1);
|
|
197
198
|
},
|
|
198
|
-
children: [/* @__PURE__ */
|
|
199
|
+
children: [/* @__PURE__ */ f(P, {
|
|
199
200
|
className: "w-full sm:w-40",
|
|
200
201
|
"aria-label": "Filter by status",
|
|
201
|
-
children: /* @__PURE__ */
|
|
202
|
-
}), /* @__PURE__ */
|
|
203
|
-
/* @__PURE__ */
|
|
202
|
+
children: /* @__PURE__ */ f(F, {})
|
|
203
|
+
}), /* @__PURE__ */ p(M, { children: [
|
|
204
|
+
/* @__PURE__ */ f(N, {
|
|
204
205
|
value: "all",
|
|
205
206
|
children: "All statuses"
|
|
206
207
|
}),
|
|
207
|
-
/* @__PURE__ */
|
|
208
|
+
/* @__PURE__ */ f(N, {
|
|
208
209
|
value: "active",
|
|
209
210
|
children: "Active"
|
|
210
211
|
}),
|
|
211
|
-
/* @__PURE__ */
|
|
212
|
+
/* @__PURE__ */ f(N, {
|
|
212
213
|
value: "prospect",
|
|
213
214
|
children: "Prospect"
|
|
214
215
|
}),
|
|
215
|
-
/* @__PURE__ */
|
|
216
|
+
/* @__PURE__ */ f(N, {
|
|
216
217
|
value: "churned",
|
|
217
218
|
children: "Churned"
|
|
218
219
|
})
|
|
219
220
|
] })]
|
|
220
221
|
})]
|
|
221
222
|
}),
|
|
222
|
-
/* @__PURE__ */
|
|
223
|
-
data:
|
|
223
|
+
/* @__PURE__ */ f(A, {
|
|
224
|
+
data: ce,
|
|
224
225
|
rowKey: (e) => e.id,
|
|
225
226
|
columns: [
|
|
226
227
|
{
|
|
227
228
|
key: "name",
|
|
228
229
|
header: "Client",
|
|
229
230
|
priority: "primary",
|
|
230
|
-
cell: (e) => /* @__PURE__ */
|
|
231
|
+
cell: (e) => /* @__PURE__ */ f("span", {
|
|
231
232
|
className: "font-medium",
|
|
232
233
|
children: e.name
|
|
233
234
|
})
|
|
@@ -236,7 +237,7 @@ function z() {
|
|
|
236
237
|
key: "industry",
|
|
237
238
|
header: "Industry",
|
|
238
239
|
priority: "secondary",
|
|
239
|
-
cell: (e) => /* @__PURE__ */
|
|
240
|
+
cell: (e) => /* @__PURE__ */ f("span", {
|
|
240
241
|
className: "text-text-secondary",
|
|
241
242
|
children: e.industry
|
|
242
243
|
})
|
|
@@ -245,10 +246,10 @@ function z() {
|
|
|
245
246
|
key: "contact",
|
|
246
247
|
header: "Contact",
|
|
247
248
|
priority: "secondary",
|
|
248
|
-
cell: (e) => /* @__PURE__ */
|
|
249
|
+
cell: (e) => /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("p", {
|
|
249
250
|
className: "text-xs",
|
|
250
251
|
children: e.contactPerson
|
|
251
|
-
}), /* @__PURE__ */
|
|
252
|
+
}), /* @__PURE__ */ f("p", {
|
|
252
253
|
className: "text-xs text-text-secondary",
|
|
253
254
|
children: e.email
|
|
254
255
|
})] })
|
|
@@ -259,7 +260,7 @@ function z() {
|
|
|
259
260
|
priority: "secondary",
|
|
260
261
|
headerClassName: "text-right",
|
|
261
262
|
cellClassName: "text-right",
|
|
262
|
-
cell: (e) => /* @__PURE__ */
|
|
263
|
+
cell: (e) => /* @__PURE__ */ p("span", { children: [
|
|
263
264
|
"$",
|
|
264
265
|
(e.revenue / 1e3).toFixed(0),
|
|
265
266
|
"K"
|
|
@@ -271,13 +272,13 @@ function z() {
|
|
|
271
272
|
priority: "hidden-on-mobile",
|
|
272
273
|
headerClassName: "text-right",
|
|
273
274
|
cellClassName: "text-right",
|
|
274
|
-
cell: (e) => /* @__PURE__ */
|
|
275
|
+
cell: (e) => /* @__PURE__ */ f("span", { children: e.projects })
|
|
275
276
|
},
|
|
276
277
|
{
|
|
277
278
|
key: "since",
|
|
278
279
|
header: "Since",
|
|
279
280
|
priority: "hidden-on-mobile",
|
|
280
|
-
cell: (e) => /* @__PURE__ */
|
|
281
|
+
cell: (e) => /* @__PURE__ */ f("span", {
|
|
281
282
|
className: "text-text-secondary",
|
|
282
283
|
children: e.since
|
|
283
284
|
})
|
|
@@ -286,68 +287,68 @@ function z() {
|
|
|
286
287
|
key: "status",
|
|
287
288
|
header: "Status",
|
|
288
289
|
priority: "secondary",
|
|
289
|
-
cell: (e) => /* @__PURE__ */
|
|
290
|
-
variant:
|
|
290
|
+
cell: (e) => /* @__PURE__ */ f(ie, {
|
|
291
|
+
variant: z(e.status),
|
|
291
292
|
className: "text-[10px]",
|
|
292
|
-
children: e.status
|
|
293
|
+
children: t(e.status)
|
|
293
294
|
})
|
|
294
295
|
}
|
|
295
296
|
],
|
|
296
|
-
emptyState: /* @__PURE__ */
|
|
297
|
+
emptyState: /* @__PURE__ */ f(E, {
|
|
297
298
|
illustration: "empty-search",
|
|
298
299
|
title: "No clients match",
|
|
299
300
|
description: "Try a different search term or status filter."
|
|
300
301
|
}),
|
|
301
|
-
rowActions: (e) => /* @__PURE__ */
|
|
302
|
+
rowActions: (e) => /* @__PURE__ */ p("div", {
|
|
302
303
|
className: "flex items-center justify-end gap-1",
|
|
303
|
-
children: [/* @__PURE__ */
|
|
304
|
+
children: [/* @__PURE__ */ f(y, {
|
|
304
305
|
variant: "ghost",
|
|
305
306
|
size: "icon",
|
|
306
307
|
"aria-label": `Edit ${e.name}`,
|
|
307
|
-
onClick: () =>
|
|
308
|
-
children: /* @__PURE__ */
|
|
309
|
-
}), /* @__PURE__ */
|
|
308
|
+
onClick: () => W.openModal(e),
|
|
309
|
+
children: /* @__PURE__ */ f(c, { className: "h-4 w-4" })
|
|
310
|
+
}), /* @__PURE__ */ p(m, { children: [/* @__PURE__ */ f(re, {
|
|
310
311
|
asChild: !0,
|
|
311
|
-
children: /* @__PURE__ */
|
|
312
|
+
children: /* @__PURE__ */ f(y, {
|
|
312
313
|
variant: "ghost",
|
|
313
314
|
size: "icon",
|
|
314
315
|
"aria-label": `Delete ${e.name}`,
|
|
315
|
-
children: /* @__PURE__ */
|
|
316
|
+
children: /* @__PURE__ */ f(d, { className: "h-4 w-4 text-status-error-text" })
|
|
316
317
|
})
|
|
317
|
-
}), /* @__PURE__ */
|
|
318
|
+
}), /* @__PURE__ */ p(_, { children: [/* @__PURE__ */ p(te, { children: [/* @__PURE__ */ f(ne, { children: "Delete client?" }), /* @__PURE__ */ p(v, { children: [
|
|
318
319
|
"This removes “",
|
|
319
320
|
e.name,
|
|
320
321
|
"” from your client portfolio. This action cannot be undone."
|
|
321
|
-
] })] }), /* @__PURE__ */
|
|
322
|
-
onClick: () => void
|
|
322
|
+
] })] }), /* @__PURE__ */ p(ee, { children: [/* @__PURE__ */ f(g, { children: "Cancel" }), /* @__PURE__ */ f(h, {
|
|
323
|
+
onClick: () => void U(e.id),
|
|
323
324
|
children: "Delete"
|
|
324
325
|
})] })] })] })]
|
|
325
326
|
})
|
|
326
327
|
}),
|
|
327
|
-
$.length > Y && /* @__PURE__ */
|
|
328
|
-
page:
|
|
328
|
+
$.length > Y && /* @__PURE__ */ f(k, {
|
|
329
|
+
page: oe,
|
|
329
330
|
pageSize: Y,
|
|
330
331
|
totalItems: $.length,
|
|
331
332
|
onPageChange: Z
|
|
332
333
|
})
|
|
333
334
|
]
|
|
334
335
|
})] }),
|
|
335
|
-
/* @__PURE__ */
|
|
336
|
-
open:
|
|
337
|
-
onOpenChange:
|
|
338
|
-
title:
|
|
336
|
+
/* @__PURE__ */ f(n, {
|
|
337
|
+
open: W.open,
|
|
338
|
+
onOpenChange: W.onOpenChange,
|
|
339
|
+
title: W.context ? "Edit client" : "Add client",
|
|
339
340
|
description: "Track the client relationship, contacts, and engagement value.",
|
|
340
|
-
fields:
|
|
341
|
-
initialValues:
|
|
342
|
-
submitLabel:
|
|
341
|
+
fields: L,
|
|
342
|
+
initialValues: W.context ? ae(W.context) : void 0,
|
|
343
|
+
submitLabel: W.context ? "Save changes" : "Add client",
|
|
343
344
|
onSubmit: async (e) => {
|
|
344
|
-
|
|
345
|
+
W.context ? await H(W.context.id, R(e, W.context)) : await V(R(e));
|
|
345
346
|
}
|
|
346
347
|
})
|
|
347
348
|
]
|
|
348
349
|
});
|
|
349
350
|
}
|
|
350
351
|
//#endregion
|
|
351
|
-
export {
|
|
352
|
+
export { B as CompanyClientManagementPagePage };
|
|
352
353
|
|
|
353
354
|
//# sourceMappingURL=CompanyClientManagementPagePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CompanyClientManagementPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/CompanyClientManagementPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogTrigger,\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n Input,\n PagePurpose,\n Pagination,\n ResponsiveTable,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { Building2, DollarSign, FolderKanban, Pencil, Plus, Search, Trash2 } from 'lucide-react';\nimport {\n FormModal,\n useFormModal,\n type FieldConfig,\n type FormValues,\n} from '../components/forms/FormModal';\nimport { useCrewFoundryStore, type Client } from '../store/crewfoundryStore';\n\nconst CLIENT_FIELDS: FieldConfig[] = [\n { name: 'name', label: 'Client name', type: 'text', required: true },\n { name: 'industry', label: 'Industry', type: 'text' },\n { name: 'contactPerson', label: 'Contact person', type: 'text' },\n { name: 'email', label: 'Contact email', type: 'email' },\n { name: 'phone', label: 'Phone', type: 'text' },\n {\n name: 'status',\n label: 'Status',\n type: 'select',\n options: [\n { label: 'Active', value: 'active' },\n { label: 'Prospect', value: 'prospect' },\n { label: 'Churned', value: 'churned' },\n ],\n },\n { name: 'revenue', label: 'Revenue (USD)', type: 'number' },\n { name: 'projects', label: 'Projects', type: 'number' },\n { name: 'since', label: 'Client since', type: 'date' },\n];\n\nconst toInitialValues = (client: Client): FormValues => ({\n name: client.name,\n industry: client.industry,\n contactPerson: client.contactPerson,\n email: client.email,\n phone: client.phone,\n status: client.status,\n revenue: client.revenue,\n projects: client.projects,\n since: client.since ? client.since.slice(0, 10) : '',\n});\n\nconst valuesToClient = (values: FormValues, base?: Client): Client => ({\n id: base?.id ?? crypto.randomUUID(),\n name: String(values.name ?? ''),\n industry: String(values.industry ?? ''),\n contactPerson: String(values.contactPerson ?? ''),\n email: String(values.email ?? ''),\n phone: String(values.phone ?? ''),\n status: (values.status as Client['status']) || 'prospect',\n revenue: typeof values.revenue === 'number' ? values.revenue : Number(values.revenue) || 0,\n projects: typeof values.projects === 'number' ? values.projects : Number(values.projects) || 0,\n since: String(values.since ?? ''),\n});\n\nconst statusVariant = (status: Client['status']) =>\n status === 'active' ? 'default' : status === 'prospect' ? 'secondary' : 'destructive';\n\nexport function CompanyClientManagementPagePage() {\n const { clients, addClient, updateClient, removeClient } = useCrewFoundryStore();\n const modal = useFormModal<Client>();\n const [search, setSearch] = useState('');\n const [filterStatus, setFilterStatus] = useState<string>('all');\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 25 });\n\n const totalRevenue = clients.reduce((s, c) => s + c.revenue, 0);\n const activeClients = clients.filter((c) => c.status === 'active');\n\n const filtered = clients.filter((c) => {\n const matchSearch = `${c.name} ${c.industry} ${c.contactPerson} ${c.email}`\n .toLowerCase()\n .includes(search.toLowerCase());\n const matchStatus = filterStatus === 'all' || c.status === filterStatus;\n return matchSearch && matchStatus;\n });\n const paged = filtered.slice(offset, offset + pageSize);\n\n const handleSubmit = async (values: FormValues) => {\n if (modal.context) {\n await updateClient(modal.context.id, valuesToClient(values, modal.context));\n } else {\n await addClient(valuesToClient(values));\n }\n };\n\n const columns: ResponsiveTableColumn<Client>[] = [\n {\n key: 'name',\n header: 'Client',\n priority: 'primary',\n cell: (c) => <span className=\"font-medium\">{c.name}</span>,\n },\n {\n key: 'industry',\n header: 'Industry',\n priority: 'secondary',\n cell: (c) => <span className=\"text-text-secondary\">{c.industry}</span>,\n },\n {\n key: 'contact',\n header: 'Contact',\n priority: 'secondary',\n cell: (c) => (\n <div>\n <p className=\"text-xs\">{c.contactPerson}</p>\n <p className=\"text-xs text-text-secondary\">{c.email}</p>\n </div>\n ),\n },\n {\n key: 'revenue',\n header: 'Revenue',\n priority: 'secondary',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (c) => <span>${(c.revenue / 1000).toFixed(0)}K</span>,\n },\n {\n key: 'projects',\n header: 'Projects',\n priority: 'hidden-on-mobile',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (c) => <span>{c.projects}</span>,\n },\n {\n key: 'since',\n header: 'Since',\n priority: 'hidden-on-mobile',\n cell: (c) => <span className=\"text-text-secondary\">{c.since}</span>,\n },\n {\n key: 'status',\n header: 'Status',\n priority: 'secondary',\n cell: (c) => (\n <Badge variant={statusVariant(c.status)} className=\"text-[10px]\">\n {c.status}\n </Badge>\n ),\n },\n ];\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>Client Management</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {clients.length} clients · ${(totalRevenue / 1000000).toFixed(1)}M revenue\n </p>\n <PagePurpose className=\"mt-1\">\n Keep track of the clients you work with — their contacts, status, and engagements — so\n account relationships never slip through the cracks.\n </PagePurpose>\n </div>\n <Button size=\"sm\" onClick={() => modal.openModal()}>\n <Plus className=\"h-4 w-4 mr-1\" />\n Add Client\n </Button>\n </div>\n <div className=\"grid gap-4 md:grid-cols-3\">\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\">Active Clients</CardTitle>\n <Building2 className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{activeClients.length}</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\">Total Revenue</CardTitle>\n <DollarSign className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(totalRevenue / 1000000).toFixed(1)}M\n </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\">Total Projects</CardTitle>\n <FolderKanban className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n {clients.reduce((s, c) => s + c.projects, 0)}\n </div>\n </CardContent>\n </GlassCard>\n </div>\n <Card>\n <CardHeader>\n <CardTitle>Client Portfolio</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n placeholder=\"Search clients...\"\n className=\"pl-9\"\n value={search}\n onChange={(event) => {\n setSearch(event.target.value);\n setPage(1);\n }}\n />\n </div>\n <Select\n value={filterStatus}\n onValueChange={(value) => {\n setFilterStatus(value);\n setPage(1);\n }}\n >\n <SelectTrigger className=\"w-full sm:w-40\" aria-label=\"Filter by status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">All statuses</SelectItem>\n <SelectItem value=\"active\">Active</SelectItem>\n <SelectItem value=\"prospect\">Prospect</SelectItem>\n <SelectItem value=\"churned\">Churned</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <ResponsiveTable\n data={paged}\n rowKey={(c) => c.id}\n columns={columns}\n emptyState={\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No clients match\"\n description=\"Try a different search term or status filter.\"\n />\n }\n rowActions={(c) => (\n <div className=\"flex items-center justify-end gap-1\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label={`Edit ${c.name}`}\n onClick={() => modal.openModal(c)}\n >\n <Pencil className=\"h-4 w-4\" />\n </Button>\n <AlertDialog>\n <AlertDialogTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" aria-label={`Delete ${c.name}`}>\n <Trash2 className=\"h-4 w-4 text-status-error-text\" />\n </Button>\n </AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Delete client?</AlertDialogTitle>\n <AlertDialogDescription>\n This removes “{c.name}” from your client portfolio. This action cannot be\n undone.\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancel</AlertDialogCancel>\n <AlertDialogAction onClick={() => void removeClient(c.id)}>\n Delete\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </div>\n )}\n />\n {filtered.length > pageSize && (\n <Pagination\n page={page}\n pageSize={pageSize}\n totalItems={filtered.length}\n onPageChange={setPage}\n />\n )}\n </CardContent>\n </Card>\n\n <FormModal\n open={modal.open}\n onOpenChange={modal.onOpenChange}\n title={modal.context ? 'Edit client' : 'Add client'}\n description=\"Track the client relationship, contacts, and engagement value.\"\n fields={CLIENT_FIELDS}\n initialValues={modal.context ? toInitialValues(modal.context) : undefined}\n submitLabel={modal.context ? 'Save changes' : 'Add client'}\n onSubmit={handleSubmit}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CompanyClientManagementPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/CompanyClientManagementPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogTrigger,\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n Input,\n PagePurpose,\n Pagination,\n ResponsiveTable,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { Building2, DollarSign, FolderKanban, Pencil, Plus, Search, Trash2 } from 'lucide-react';\nimport {\n FormModal,\n useFormModal,\n type FieldConfig,\n type FormValues,\n} from '../components/forms/FormModal';\nimport { useCrewFoundryStore, type Client } from '../store/crewfoundryStore';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\n\nconst CLIENT_FIELDS: FieldConfig[] = [\n { name: 'name', label: 'Client name', type: 'text', required: true },\n { name: 'industry', label: 'Industry', type: 'text' },\n { name: 'contactPerson', label: 'Contact person', type: 'text' },\n { name: 'email', label: 'Contact email', type: 'email' },\n { name: 'phone', label: 'Phone', type: 'text' },\n {\n name: 'status',\n label: 'Status',\n type: 'select',\n options: [\n { label: 'Active', value: 'active' },\n { label: 'Prospect', value: 'prospect' },\n { label: 'Churned', value: 'churned' },\n ],\n },\n { name: 'revenue', label: 'Revenue (USD)', type: 'number' },\n { name: 'projects', label: 'Projects', type: 'number' },\n { name: 'since', label: 'Client since', type: 'date' },\n];\n\nconst toInitialValues = (client: Client): FormValues => ({\n name: client.name,\n industry: client.industry,\n contactPerson: client.contactPerson,\n email: client.email,\n phone: client.phone,\n status: client.status,\n revenue: client.revenue,\n projects: client.projects,\n since: client.since ? client.since.slice(0, 10) : '',\n});\n\nconst valuesToClient = (values: FormValues, base?: Client): Client => ({\n id: base?.id ?? crypto.randomUUID(),\n name: String(values.name ?? ''),\n industry: String(values.industry ?? ''),\n contactPerson: String(values.contactPerson ?? ''),\n email: String(values.email ?? ''),\n phone: String(values.phone ?? ''),\n status: (values.status as Client['status']) || 'prospect',\n revenue: typeof values.revenue === 'number' ? values.revenue : Number(values.revenue) || 0,\n projects: typeof values.projects === 'number' ? values.projects : Number(values.projects) || 0,\n since: String(values.since ?? ''),\n});\n\nconst statusVariant = (status: Client['status']) =>\n status === 'active' ? 'default' : status === 'prospect' ? 'secondary' : 'destructive';\n\nexport function CompanyClientManagementPagePage() {\n const { clients, addClient, updateClient, removeClient } = useCrewFoundryStore();\n const modal = useFormModal<Client>();\n const [search, setSearch] = useState('');\n const [filterStatus, setFilterStatus] = useState<string>('all');\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 25 });\n\n const totalRevenue = clients.reduce((s, c) => s + c.revenue, 0);\n const activeClients = clients.filter((c) => c.status === 'active');\n\n const filtered = clients.filter((c) => {\n const matchSearch = `${c.name} ${c.industry} ${c.contactPerson} ${c.email}`\n .toLowerCase()\n .includes(search.toLowerCase());\n const matchStatus = filterStatus === 'all' || c.status === filterStatus;\n return matchSearch && matchStatus;\n });\n const paged = filtered.slice(offset, offset + pageSize);\n\n const handleSubmit = async (values: FormValues) => {\n if (modal.context) {\n await updateClient(modal.context.id, valuesToClient(values, modal.context));\n } else {\n await addClient(valuesToClient(values));\n }\n };\n\n const columns: ResponsiveTableColumn<Client>[] = [\n {\n key: 'name',\n header: 'Client',\n priority: 'primary',\n cell: (c) => <span className=\"font-medium\">{c.name}</span>,\n },\n {\n key: 'industry',\n header: 'Industry',\n priority: 'secondary',\n cell: (c) => <span className=\"text-text-secondary\">{c.industry}</span>,\n },\n {\n key: 'contact',\n header: 'Contact',\n priority: 'secondary',\n cell: (c) => (\n <div>\n <p className=\"text-xs\">{c.contactPerson}</p>\n <p className=\"text-xs text-text-secondary\">{c.email}</p>\n </div>\n ),\n },\n {\n key: 'revenue',\n header: 'Revenue',\n priority: 'secondary',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (c) => <span>${(c.revenue / 1000).toFixed(0)}K</span>,\n },\n {\n key: 'projects',\n header: 'Projects',\n priority: 'hidden-on-mobile',\n headerClassName: 'text-right',\n cellClassName: 'text-right',\n cell: (c) => <span>{c.projects}</span>,\n },\n {\n key: 'since',\n header: 'Since',\n priority: 'hidden-on-mobile',\n cell: (c) => <span className=\"text-text-secondary\">{c.since}</span>,\n },\n {\n key: 'status',\n header: 'Status',\n priority: 'secondary',\n cell: (c) => (\n <Badge variant={statusVariant(c.status)} className=\"text-[10px]\">\n {formatEnumLabel(c.status)}\n </Badge>\n ),\n },\n ];\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>Client Management</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {clients.length} clients · ${(totalRevenue / 1000000).toFixed(1)}M revenue\n </p>\n <PagePurpose className=\"mt-1\">\n Keep track of the clients you work with — their contacts, status, and engagements — so\n account relationships never slip through the cracks.\n </PagePurpose>\n </div>\n <Button size=\"sm\" onClick={() => modal.openModal()}>\n <Plus className=\"h-4 w-4 mr-1\" />\n Add Client\n </Button>\n </div>\n <div className=\"grid gap-4 md:grid-cols-3\">\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\">Active Clients</CardTitle>\n <Building2 className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{activeClients.length}</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\">Total Revenue</CardTitle>\n <DollarSign className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${(totalRevenue / 1000000).toFixed(1)}M\n </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\">Total Projects</CardTitle>\n <FolderKanban className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n {clients.reduce((s, c) => s + c.projects, 0)}\n </div>\n </CardContent>\n </GlassCard>\n </div>\n <Card>\n <CardHeader>\n <CardTitle>Client Portfolio</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n placeholder=\"Search clients...\"\n className=\"pl-9\"\n value={search}\n onChange={(event) => {\n setSearch(event.target.value);\n setPage(1);\n }}\n />\n </div>\n <Select\n value={filterStatus}\n onValueChange={(value) => {\n setFilterStatus(value);\n setPage(1);\n }}\n >\n <SelectTrigger className=\"w-full sm:w-40\" aria-label=\"Filter by status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">All statuses</SelectItem>\n <SelectItem value=\"active\">Active</SelectItem>\n <SelectItem value=\"prospect\">Prospect</SelectItem>\n <SelectItem value=\"churned\">Churned</SelectItem>\n </SelectContent>\n </Select>\n </div>\n <ResponsiveTable\n data={paged}\n rowKey={(c) => c.id}\n columns={columns}\n emptyState={\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No clients match\"\n description=\"Try a different search term or status filter.\"\n />\n }\n rowActions={(c) => (\n <div className=\"flex items-center justify-end gap-1\">\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label={`Edit ${c.name}`}\n onClick={() => modal.openModal(c)}\n >\n <Pencil className=\"h-4 w-4\" />\n </Button>\n <AlertDialog>\n <AlertDialogTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" aria-label={`Delete ${c.name}`}>\n <Trash2 className=\"h-4 w-4 text-status-error-text\" />\n </Button>\n </AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Delete client?</AlertDialogTitle>\n <AlertDialogDescription>\n This removes “{c.name}” from your client portfolio. This action cannot be\n undone.\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancel</AlertDialogCancel>\n <AlertDialogAction onClick={() => void removeClient(c.id)}>\n Delete\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </div>\n )}\n />\n {filtered.length > pageSize && (\n <Pagination\n page={page}\n pageSize={pageSize}\n totalItems={filtered.length}\n onPageChange={setPage}\n />\n )}\n </CardContent>\n </Card>\n\n <FormModal\n open={modal.open}\n onOpenChange={modal.onOpenChange}\n title={modal.context ? 'Edit client' : 'Add client'}\n description=\"Track the client relationship, contacts, and engagement value.\"\n fields={CLIENT_FIELDS}\n initialValues={modal.context ? toInitialValues(modal.context) : undefined}\n submitLabel={modal.context ? 'Save changes' : 'Add client'}\n onSubmit={handleSubmit}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|