@burdenoff/microfe-crewfoundry 2026.1029.2 → 2026.1030.2
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/components/EmployeeSkillsPanel.d.ts +12 -0
- package/dist/crewfoundry/components/EmployeeSkillsPanel.d.ts.map +1 -0
- package/dist/crewfoundry/components/EmployeeSkillsPanel.js +168 -0
- package/dist/crewfoundry/components/EmployeeSkillsPanel.js.map +1 -0
- package/dist/crewfoundry/lib/useFocusedRecordParam.d.ts +51 -0
- package/dist/crewfoundry/lib/useFocusedRecordParam.d.ts.map +1 -0
- package/dist/crewfoundry/lib/useFocusedRecordParam.js +38 -0
- package/dist/crewfoundry/lib/useFocusedRecordParam.js.map +1 -0
- package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.d.ts.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js +119 -112
- package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.d.ts.map +1 -1
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js +78 -71
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.d.ts.map +1 -1
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js +88 -81
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.d.ts.map +1 -1
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.js +133 -126
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringInterviewsPagePage.d.ts.map +1 -1
- package/dist/crewfoundry/pages/HiringInterviewsPagePage.js +150 -143
- package/dist/crewfoundry/pages/HiringInterviewsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.d.ts.map +1 -1
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.js +110 -103
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrEmployeeProfilePagePage.d.ts.map +1 -1
- package/dist/crewfoundry/pages/HrEmployeeProfilePagePage.js +124 -134
- package/dist/crewfoundry/pages/HrEmployeeProfilePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.d.ts.map +1 -1
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js +173 -162
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrPayrollPagePage.d.ts.map +1 -1
- package/dist/crewfoundry/pages/HrPayrollPagePage.js +169 -158
- package/dist/crewfoundry/pages/HrPayrollPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.d.ts.map +1 -1
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js +179 -163
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js.map +1 -1
- package/dist/crewfoundry/store/crewfoundryStore.d.ts +22 -0
- package/dist/crewfoundry/store/crewfoundryStore.d.ts.map +1 -1
- package/dist/crewfoundry/store/crewfoundryStore.js +158 -129
- package/dist/crewfoundry/store/crewfoundryStore.js.map +1 -1
- package/dist/utils/pageContext.d.ts +43 -4
- package/dist/utils/pageContext.d.ts.map +1 -1
- package/dist/utils/pageContext.js +10 -3
- package/dist/utils/pageContext.js.map +1 -1
- package/package.json +3 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HrDepartmentsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/HrDepartmentsPagePage.tsx"],"sourcesContent":["import { useCallback, useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { Building2, DollarSign, MapPin, Network, Pencil, Plus, Trash2, Users } from 'lucide-react';\nimport { useCrewFoundryStore, type Department, type Employee } from '../store/crewfoundryStore';\nimport { AvatarStack, OrgTree, StatCard, type OrgNode } from '../components/viz';\nimport {\n FormModal,\n useFormModal,\n type FieldConfig,\n type FormValues,\n} from '../components/forms/FormModal';\nimport { ConfirmDialog } from '../components/forms/ConfirmDialog';\nimport { getNumber, getText, newClientId } from '../components/forms/formValues';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\n\nconst fullName = (employee: Employee): string =>\n `${employee.firstName} ${employee.lastName}`.trim();\n\nconst DEPARTMENT_FIELDS: FieldConfig[] = [\n { name: 'name', label: 'Department name', type: 'text', required: true, colSpan: 2 },\n { name: 'headName', label: 'Department head', type: 'text', required: true },\n { name: 'location', label: 'Location', type: 'text', required: true },\n { name: 'budget', label: 'Annual budget', type: 'number', required: true },\n {\n name: 'status',\n label: 'Status',\n type: 'select',\n required: true,\n options: [\n { label: 'Active', value: 'active' },\n { label: 'Restructuring', value: 'restructuring' },\n ],\n },\n];\n\nconst toFormValues = (department: Department): FormValues => ({\n name: department.name,\n headName: department.headName,\n location: department.location,\n budget: department.budget,\n status: department.status,\n});\n\nexport function HrDepartmentsPagePage() {\n const { departments, employees, teams, addDepartment, updateDepartment, removeDepartment } =\n useCrewFoundryStore();\n const form = useFormModal<Department>();\n const [deleting, setDeleting] = useState<Department | null>(null);\n\n const totalBudget = departments.reduce((s, d) => s + d.budget, 0);\n const totalHeadcount = departments.reduce((s, d) => s + d.employeeCount, 0);\n\n const editing = form.context;\n\n // Resolve a department's head to a real employee (by id first, then by name) so\n // we can show a real avatar rather than a text-only name.\n const headOf = useCallback(\n (department: Department): Employee | undefined =>\n employees.find((e) => e.id === department.headId) ||\n employees.find((e) => fullName(e) === department.headName),\n [employees]\n );\n\n // Org structure: a real hierarchy derived from the roster + departments + teams.\n // Root = a top-of-org employee (CEO / founder / chief with no manager) when one\n // exists, else a generic company node. Each department hangs off the root with\n // its head; each department's children are its real teams (or a sample of its\n // real employees when the department has no teams).\n const orgRoot = useMemo<OrgNode | null>(() => {\n if (departments.length === 0) {\n return null;\n }\n const topLeader = employees.find(\n (e) => !e.managerId && /chief|ceo|founder|president|head of company/i.test(e.position)\n );\n\n const deptNodes: OrgNode[] = departments.map((department) => {\n const head = headOf(department);\n const deptTeams = teams.filter((team) => team.department === department.name);\n let children: OrgNode[];\n if (deptTeams.length > 0) {\n children = deptTeams.slice(0, 4).map((team) => ({\n id: team.id,\n name: team.name,\n role: `${team.memberCount} member${team.memberCount === 1 ? '' : 's'}`,\n }));\n } else {\n children = employees\n .filter((e) => e.department === department.name && e.id !== head?.id)\n .slice(0, 3)\n .map((e) => ({ id: e.id, name: fullName(e), role: e.position, avatarUrl: e.avatar }));\n }\n return {\n id: department.id,\n name: department.name,\n role: department.headName ? `${department.headName}, Head` : 'Department',\n avatarUrl: head?.avatar,\n children,\n };\n });\n\n return {\n id: topLeader?.id ?? 'org-root',\n name: topLeader ? fullName(topLeader) : 'Organization',\n role: topLeader?.position ?? 'Company',\n avatarUrl: topLeader?.avatar,\n children: deptNodes,\n };\n }, [departments, employees, teams, headOf]);\n\n const handleSubmit = async (values: FormValues) => {\n const payload: Department = {\n id: editing?.id ?? newClientId(),\n name: getText(values, 'name'),\n headId: editing?.headId ?? '',\n headName: getText(values, 'headName'),\n employeeCount: editing?.employeeCount ?? 0,\n budget: getNumber(values, 'budget'),\n location: getText(values, 'location'),\n status: getText(values, 'status') as Department['status'],\n };\n if (editing) {\n await updateDepartment(editing.id, payload);\n } else {\n await addDepartment(payload);\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>Departments</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n Organizational structure and department management\n </p>\n <PagePurpose className=\"mt-1\">\n Organize your company into departments — set up structure, assign heads, and see how\n headcount is distributed across the org.\n </PagePurpose>\n </div>\n <Button size=\"sm\" onClick={() => form.openModal()}>\n <Plus className=\"h-4 w-4 mr-1\" />\n Add Department\n </Button>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-3\">\n <StatCard\n label=\"Total Departments\"\n value={departments.length}\n icon={<Building2 className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Total Headcount\"\n value={totalHeadcount}\n icon={<Users className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Total Budget\"\n value={`$${(totalBudget / 1000000).toFixed(1)}M`}\n icon={<DollarSign className=\"h-4 w-4\" />}\n />\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-3\">\n {departments.map((dept) => {\n const deptEmps = employees.filter((e) => e.department === dept.name);\n const headcount = dept.employeeCount || deptEmps.length;\n const avgSalary =\n deptEmps.length > 0\n ? Math.round(deptEmps.reduce((s, e) => s + e.salary, 0) / deptEmps.length)\n : 0;\n const avgPerfValue =\n deptEmps.length > 0\n ? deptEmps.reduce((s, e) => s + e.performanceScore, 0) / deptEmps.length\n : 0;\n // Progress bar = real average performance for the department, on a 0–100\n // scale (score out of 5). A clearly-labeled, computed metric — no fabrication.\n const perfPct = Math.round((avgPerfValue / 5) * 100);\n const head = headOf(dept);\n const perfTone =\n perfPct >= 80\n ? 'bg-status-success-text'\n : perfPct >= 60\n ? 'bg-status-warning-text'\n : 'bg-status-error-text';\n\n return (\n <GlassCard key={dept.id} treatment=\"glass\" className=\"border-border-seam\">\n <CardHeader>\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <CardTitle className=\"truncate text-lg\">{dept.name}</CardTitle>\n <div className=\"mt-2 flex items-center gap-2\">\n <AvatarStack\n people={[{ name: dept.headName || 'Unassigned', avatarUrl: head?.avatar }]}\n size={28}\n />\n <span className=\"truncate text-sm text-text-secondary\">\n <span className=\"font-medium text-text-primary\">{dept.headName}</span>\n {dept.headName ? ', Head' : 'No head assigned'}\n </span>\n </div>\n </div>\n <div className=\"flex items-center gap-1\">\n <Badge variant={dept.status === 'active' ? 'default' : 'secondary'}>\n {formatEnumLabel(dept.status)}\n </Badge>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 w-8 p-0\"\n aria-label={`Edit ${dept.name}`}\n onClick={() => form.openModal(dept)}\n >\n <Pencil className=\"h-4 w-4\" />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 w-8 p-0\"\n aria-label={`Delete ${dept.name}`}\n onClick={() => setDeleting(dept)}\n >\n <Trash2 className=\"h-4 w-4 text-status-error-text\" />\n </Button>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex items-end justify-between gap-3\">\n <div>\n <p className=\"text-3xl font-bold leading-none tabular-nums\">{headcount}</p>\n <p className=\"mt-1 text-xs text-text-secondary\">Employees</p>\n </div>\n <div className=\"text-right\">\n <p className=\"text-lg font-semibold tabular-nums\">\n ${(dept.budget / 1000000).toFixed(1)}M\n </p>\n <p className=\"text-xs text-text-secondary\">Annual budget</p>\n </div>\n </div>\n\n <div className=\"space-y-1\">\n <div className=\"flex items-center justify-between text-xs text-text-secondary\">\n <span>Avg performance</span>\n <span className=\"font-medium text-text-primary tabular-nums\">{perfPct}%</span>\n </div>\n <div className=\"h-2 overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className={`h-2 rounded-full ${perfTone}`}\n style={{ width: `${perfPct}%` }}\n />\n </div>\n </div>\n\n <div className=\"flex items-center justify-between text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <MapPin className=\"h-3 w-3\" />\n {dept.location}\n </span>\n <span>\n Cost/Head:{' '}\n {headcount > 0 ? `$${Math.round(dept.budget / headcount / 1000)}K` : '—'}\n </span>\n </div>\n {avgSalary > 0 && (\n <p className=\"text-xs text-text-secondary\">\n Avg salary{' '}\n <span className=\"font-medium text-text-primary\">\n ${avgSalary.toLocaleString()}\n </span>\n </p>\n )}\n </CardContent>\n </GlassCard>\n );\n })}\n </div>\n\n {orgRoot && (\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2 text-lg\">\n <Network className=\"h-4 w-4 text-text-secondary\" />\n Org structure\n </CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"overflow-x-auto pb-2\">\n <div className=\"min-w-fit\">\n <OrgTree root={orgRoot} />\n </div>\n </div>\n </CardContent>\n </Card>\n )}\n\n <FormModal\n open={form.open}\n onOpenChange={form.onOpenChange}\n title={editing ? 'Edit department' : 'Add department'}\n description={\n editing\n ? 'Update this department’s details.'\n : 'Create a new department in your organization.'\n }\n fields={DEPARTMENT_FIELDS}\n initialValues={editing ? toFormValues(editing) : undefined}\n submitLabel={editing ? 'Save changes' : 'Add department'}\n onSubmit={handleSubmit}\n />\n\n <ConfirmDialog\n open={deleting !== null}\n onOpenChange={(open) => {\n if (!open) {\n setDeleting(null);\n }\n }}\n title=\"Delete department?\"\n description={\n deleting ? `${deleting.name} will be removed. This cannot be undone.` : undefined\n }\n confirmLabel=\"Delete\"\n destructive\n onConfirm={async () => {\n if (deleting) {\n await removeDepartment(deleting.id);\n setDeleting(null);\n }\n }}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HrDepartmentsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/HrDepartmentsPagePage.tsx"],"sourcesContent":["import { useCallback, useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { Building2, DollarSign, MapPin, Network, Pencil, Plus, Trash2, Users } from 'lucide-react';\nimport { useCrewFoundryStore, type Department, type Employee } from '../store/crewfoundryStore';\nimport { AvatarStack, OrgTree, StatCard, type OrgNode } from '../components/viz';\nimport {\n FormModal,\n useFormModal,\n type FieldConfig,\n type FormValues,\n} from '../components/forms/FormModal';\nimport { ConfirmDialog } from '../components/forms/ConfirmDialog';\nimport { getNumber, getText, newClientId } from '../components/forms/formValues';\nimport { formatEnumLabel } from '../utils/formatEnumLabel';\nimport { useFocusedRecordParam } from '../lib/useFocusedRecordParam';\n\nconst fullName = (employee: Employee): string =>\n `${employee.firstName} ${employee.lastName}`.trim();\n\nconst DEPARTMENT_FIELDS: FieldConfig[] = [\n { name: 'name', label: 'Department name', type: 'text', required: true, colSpan: 2 },\n { name: 'headName', label: 'Department head', type: 'text', required: true },\n { name: 'location', label: 'Location', type: 'text', required: true },\n { name: 'budget', label: 'Annual budget', type: 'number', required: true },\n {\n name: 'status',\n label: 'Status',\n type: 'select',\n required: true,\n options: [\n { label: 'Active', value: 'active' },\n { label: 'Restructuring', value: 'restructuring' },\n ],\n },\n];\n\nconst toFormValues = (department: Department): FormValues => ({\n name: department.name,\n headName: department.headName,\n location: department.location,\n budget: department.budget,\n status: department.status,\n});\n\nexport function HrDepartmentsPagePage() {\n const { departments, employees, teams, addDepartment, updateDepartment, removeDepartment } =\n useCrewFoundryStore();\n const form = useFormModal<Department>();\n // The department open in the edit dialog is \"this department\" for the assistant.\n useFocusedRecordParam({\n records: departments,\n activeId: form.open ? (form.context?.id ?? null) : null,\n onRestore: form.openModal,\n });\n const [deleting, setDeleting] = useState<Department | null>(null);\n\n const totalBudget = departments.reduce((s, d) => s + d.budget, 0);\n const totalHeadcount = departments.reduce((s, d) => s + d.employeeCount, 0);\n\n const editing = form.context;\n\n // Resolve a department's head to a real employee (by id first, then by name) so\n // we can show a real avatar rather than a text-only name.\n const headOf = useCallback(\n (department: Department): Employee | undefined =>\n employees.find((e) => e.id === department.headId) ||\n employees.find((e) => fullName(e) === department.headName),\n [employees]\n );\n\n // Org structure: a real hierarchy derived from the roster + departments + teams.\n // Root = a top-of-org employee (CEO / founder / chief with no manager) when one\n // exists, else a generic company node. Each department hangs off the root with\n // its head; each department's children are its real teams (or a sample of its\n // real employees when the department has no teams).\n const orgRoot = useMemo<OrgNode | null>(() => {\n if (departments.length === 0) {\n return null;\n }\n const topLeader = employees.find(\n (e) => !e.managerId && /chief|ceo|founder|president|head of company/i.test(e.position)\n );\n\n const deptNodes: OrgNode[] = departments.map((department) => {\n const head = headOf(department);\n const deptTeams = teams.filter((team) => team.department === department.name);\n let children: OrgNode[];\n if (deptTeams.length > 0) {\n children = deptTeams.slice(0, 4).map((team) => ({\n id: team.id,\n name: team.name,\n role: `${team.memberCount} member${team.memberCount === 1 ? '' : 's'}`,\n }));\n } else {\n children = employees\n .filter((e) => e.department === department.name && e.id !== head?.id)\n .slice(0, 3)\n .map((e) => ({ id: e.id, name: fullName(e), role: e.position, avatarUrl: e.avatar }));\n }\n return {\n id: department.id,\n name: department.name,\n role: department.headName ? `${department.headName}, Head` : 'Department',\n avatarUrl: head?.avatar,\n children,\n };\n });\n\n return {\n id: topLeader?.id ?? 'org-root',\n name: topLeader ? fullName(topLeader) : 'Organization',\n role: topLeader?.position ?? 'Company',\n avatarUrl: topLeader?.avatar,\n children: deptNodes,\n };\n }, [departments, employees, teams, headOf]);\n\n const handleSubmit = async (values: FormValues) => {\n const payload: Department = {\n id: editing?.id ?? newClientId(),\n name: getText(values, 'name'),\n headId: editing?.headId ?? '',\n headName: getText(values, 'headName'),\n employeeCount: editing?.employeeCount ?? 0,\n budget: getNumber(values, 'budget'),\n location: getText(values, 'location'),\n status: getText(values, 'status') as Department['status'],\n };\n if (editing) {\n await updateDepartment(editing.id, payload);\n } else {\n await addDepartment(payload);\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>Departments</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n Organizational structure and department management\n </p>\n <PagePurpose className=\"mt-1\">\n Organize your company into departments — set up structure, assign heads, and see how\n headcount is distributed across the org.\n </PagePurpose>\n </div>\n <Button size=\"sm\" onClick={() => form.openModal()}>\n <Plus className=\"h-4 w-4 mr-1\" />\n Add Department\n </Button>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-3\">\n <StatCard\n label=\"Total Departments\"\n value={departments.length}\n icon={<Building2 className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Total Headcount\"\n value={totalHeadcount}\n icon={<Users className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Total Budget\"\n value={`$${(totalBudget / 1000000).toFixed(1)}M`}\n icon={<DollarSign className=\"h-4 w-4\" />}\n />\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-3\">\n {departments.map((dept) => {\n const deptEmps = employees.filter((e) => e.department === dept.name);\n const headcount = dept.employeeCount || deptEmps.length;\n const avgSalary =\n deptEmps.length > 0\n ? Math.round(deptEmps.reduce((s, e) => s + e.salary, 0) / deptEmps.length)\n : 0;\n const avgPerfValue =\n deptEmps.length > 0\n ? deptEmps.reduce((s, e) => s + e.performanceScore, 0) / deptEmps.length\n : 0;\n // Progress bar = real average performance for the department, on a 0–100\n // scale (score out of 5). A clearly-labeled, computed metric — no fabrication.\n const perfPct = Math.round((avgPerfValue / 5) * 100);\n const head = headOf(dept);\n const perfTone =\n perfPct >= 80\n ? 'bg-status-success-text'\n : perfPct >= 60\n ? 'bg-status-warning-text'\n : 'bg-status-error-text';\n\n return (\n <GlassCard key={dept.id} treatment=\"glass\" className=\"border-border-seam\">\n <CardHeader>\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <CardTitle className=\"truncate text-lg\">{dept.name}</CardTitle>\n <div className=\"mt-2 flex items-center gap-2\">\n <AvatarStack\n people={[{ name: dept.headName || 'Unassigned', avatarUrl: head?.avatar }]}\n size={28}\n />\n <span className=\"truncate text-sm text-text-secondary\">\n <span className=\"font-medium text-text-primary\">{dept.headName}</span>\n {dept.headName ? ', Head' : 'No head assigned'}\n </span>\n </div>\n </div>\n <div className=\"flex items-center gap-1\">\n <Badge variant={dept.status === 'active' ? 'default' : 'secondary'}>\n {formatEnumLabel(dept.status)}\n </Badge>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 w-8 p-0\"\n aria-label={`Edit ${dept.name}`}\n onClick={() => form.openModal(dept)}\n >\n <Pencil className=\"h-4 w-4\" />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8 w-8 p-0\"\n aria-label={`Delete ${dept.name}`}\n onClick={() => setDeleting(dept)}\n >\n <Trash2 className=\"h-4 w-4 text-status-error-text\" />\n </Button>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"flex items-end justify-between gap-3\">\n <div>\n <p className=\"text-3xl font-bold leading-none tabular-nums\">{headcount}</p>\n <p className=\"mt-1 text-xs text-text-secondary\">Employees</p>\n </div>\n <div className=\"text-right\">\n <p className=\"text-lg font-semibold tabular-nums\">\n ${(dept.budget / 1000000).toFixed(1)}M\n </p>\n <p className=\"text-xs text-text-secondary\">Annual budget</p>\n </div>\n </div>\n\n <div className=\"space-y-1\">\n <div className=\"flex items-center justify-between text-xs text-text-secondary\">\n <span>Avg performance</span>\n <span className=\"font-medium text-text-primary tabular-nums\">{perfPct}%</span>\n </div>\n <div className=\"h-2 overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className={`h-2 rounded-full ${perfTone}`}\n style={{ width: `${perfPct}%` }}\n />\n </div>\n </div>\n\n <div className=\"flex items-center justify-between text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <MapPin className=\"h-3 w-3\" />\n {dept.location}\n </span>\n <span>\n Cost/Head:{' '}\n {headcount > 0 ? `$${Math.round(dept.budget / headcount / 1000)}K` : '—'}\n </span>\n </div>\n {avgSalary > 0 && (\n <p className=\"text-xs text-text-secondary\">\n Avg salary{' '}\n <span className=\"font-medium text-text-primary\">\n ${avgSalary.toLocaleString()}\n </span>\n </p>\n )}\n </CardContent>\n </GlassCard>\n );\n })}\n </div>\n\n {orgRoot && (\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2 text-lg\">\n <Network className=\"h-4 w-4 text-text-secondary\" />\n Org structure\n </CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"overflow-x-auto pb-2\">\n <div className=\"min-w-fit\">\n <OrgTree root={orgRoot} />\n </div>\n </div>\n </CardContent>\n </Card>\n )}\n\n <FormModal\n open={form.open}\n onOpenChange={form.onOpenChange}\n title={editing ? 'Edit department' : 'Add department'}\n description={\n editing\n ? 'Update this department’s details.'\n : 'Create a new department in your organization.'\n }\n fields={DEPARTMENT_FIELDS}\n initialValues={editing ? toFormValues(editing) : undefined}\n submitLabel={editing ? 'Save changes' : 'Add department'}\n onSubmit={handleSubmit}\n />\n\n <ConfirmDialog\n open={deleting !== null}\n onOpenChange={(open) => {\n if (!open) {\n setDeleting(null);\n }\n }}\n title=\"Delete department?\"\n description={\n deleting ? `${deleting.name} will be removed. This cannot be undone.` : undefined\n }\n confirmLabel=\"Delete\"\n destructive\n onConfirm={async () => {\n if (deleting) {\n await removeDepartment(deleting.id);\n setDeleting(null);\n }\n }}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HrEmployeeProfilePagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/HrEmployeeProfilePagePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"HrEmployeeProfilePagePage.d.ts","sourceRoot":"","sources":["../../../src/crewfoundry/pages/HrEmployeeProfilePagePage.tsx"],"names":[],"mappings":"AAgNA,wBAAgB,yBAAyB,gCAqUxC"}
|
|
@@ -7,14 +7,15 @@ import { SegmentedTabs as a } from "../components/viz/SegmentedTabs.js";
|
|
|
7
7
|
import "../components/viz/index.js";
|
|
8
8
|
import { ConfirmDialog as o } from "../components/forms/ConfirmDialog.js";
|
|
9
9
|
import { AttachDocumentDialog as s } from "../components/forms/AttachDocumentDialog.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
10
|
+
import { EmployeeSkillsPanel as c } from "../components/EmployeeSkillsPanel.js";
|
|
11
|
+
import { useEffect as l, useRef as ee, useState as u } from "react";
|
|
12
|
+
import { ArrowLeft as d, BadgeCheck as f, CalendarDays as p, FileText as te, Mail as ne, MapPin as re, Phone as ie, Plus as m, Trash2 as ae } from "lucide-react";
|
|
13
|
+
import { useNavigate as h, useSearchParams as g } from "react-router";
|
|
14
|
+
import { jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
15
|
+
import { Badge as y, Button as b, Card as x, CardContent as S, CardHeader as C, CardTitle as w, GlassCard as oe, GradientText as se, IllustratedEmptyState as T, PagePurpose as ce, Spinner as le } from "@burdenoff/fe-libs/ui";
|
|
16
|
+
import { useGlobalUi as ue } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
16
17
|
//#region src/crewfoundry/pages/HrEmployeeProfilePagePage.tsx
|
|
17
|
-
var
|
|
18
|
+
var de = [
|
|
18
19
|
{
|
|
19
20
|
id: "overview",
|
|
20
21
|
label: "Overview"
|
|
@@ -35,7 +36,7 @@ var D = [
|
|
|
35
36
|
id: "activity",
|
|
36
37
|
label: "Activity"
|
|
37
38
|
}
|
|
38
|
-
],
|
|
39
|
+
], E = [
|
|
39
40
|
{
|
|
40
41
|
min: 0,
|
|
41
42
|
max: 6e4
|
|
@@ -61,11 +62,11 @@ var D = [
|
|
|
61
62
|
max: Infinity
|
|
62
63
|
}
|
|
63
64
|
];
|
|
64
|
-
function
|
|
65
|
-
let t =
|
|
65
|
+
function fe(e) {
|
|
66
|
+
let t = E.findIndex((t) => e >= t.min && e < t.max), n = t === -1 ? E.length - 1 : t, r = E[n], i = `$${Math.round(r.min / 1e3)}k`, a = r.max === Infinity ? "+" : `–$${Math.round(r.max / 1e3)}k`;
|
|
66
67
|
return `Band ${n + 1} · ${i}${a}`;
|
|
67
68
|
}
|
|
68
|
-
function
|
|
69
|
+
function D(e) {
|
|
69
70
|
let t = new Date(e);
|
|
70
71
|
if (Number.isNaN(t.getTime())) return "—";
|
|
71
72
|
let n = /* @__PURE__ */ new Date(), r = (n.getFullYear() - t.getFullYear()) * 12 + (n.getMonth() - t.getMonth());
|
|
@@ -73,37 +74,37 @@ function ue(e) {
|
|
|
73
74
|
let i = Math.floor(r / 12), a = r % 12, o = [];
|
|
74
75
|
return i > 0 && o.push(`${i} year${i === 1 ? "" : "s"}`), o.push(`${a} month${a === 1 ? "" : "s"}`), o.join(", ");
|
|
75
76
|
}
|
|
76
|
-
function
|
|
77
|
+
function O(e) {
|
|
77
78
|
return e >= 4.5 ? "Excellent" : e >= 3.5 ? "Strong" : e >= 2.5 ? "Meets expectations" : "Needs improvement";
|
|
78
79
|
}
|
|
79
|
-
function
|
|
80
|
+
function k(e) {
|
|
80
81
|
return e === "active" ? "default" : e === "on_leave" ? "secondary" : "destructive";
|
|
81
82
|
}
|
|
82
|
-
function
|
|
83
|
-
return /* @__PURE__ */
|
|
83
|
+
function A({ icon: e, children: t }) {
|
|
84
|
+
return /* @__PURE__ */ v("span", {
|
|
84
85
|
className: "inline-flex items-center gap-1.5 rounded-full border border-border-subtle bg-bg-surface px-3 py-1 text-xs text-text-secondary",
|
|
85
|
-
children: [/* @__PURE__ */
|
|
86
|
+
children: [/* @__PURE__ */ _("span", {
|
|
86
87
|
className: "text-text-muted",
|
|
87
88
|
children: e
|
|
88
|
-
}), /* @__PURE__ */
|
|
89
|
+
}), /* @__PURE__ */ _("span", {
|
|
89
90
|
className: "truncate",
|
|
90
91
|
children: t
|
|
91
92
|
})]
|
|
92
93
|
});
|
|
93
94
|
}
|
|
94
|
-
function
|
|
95
|
-
return /* @__PURE__ */
|
|
95
|
+
function j({ label: e, children: t }) {
|
|
96
|
+
return /* @__PURE__ */ v("div", {
|
|
96
97
|
className: "flex items-start justify-between gap-4 py-2 text-sm",
|
|
97
|
-
children: [/* @__PURE__ */
|
|
98
|
+
children: [/* @__PURE__ */ _("span", {
|
|
98
99
|
className: "text-text-secondary",
|
|
99
100
|
children: e
|
|
100
|
-
}), /* @__PURE__ */
|
|
101
|
+
}), /* @__PURE__ */ _("span", {
|
|
101
102
|
className: "text-right font-medium text-text-primary",
|
|
102
103
|
children: t
|
|
103
104
|
})]
|
|
104
105
|
});
|
|
105
106
|
}
|
|
106
|
-
function
|
|
107
|
+
function M(e, n, r, i, a) {
|
|
107
108
|
let o = [], s = (e) => {
|
|
108
109
|
let t = new Date(e).getTime();
|
|
109
110
|
return Number.isNaN(t) ? 0 : t;
|
|
@@ -153,73 +154,73 @@ function N(e, n, r, i, a) {
|
|
|
153
154
|
});
|
|
154
155
|
return o.sort((e, t) => t.ts - e.ts).slice(0, 14).map((e) => e.item);
|
|
155
156
|
}
|
|
156
|
-
function
|
|
157
|
-
let
|
|
158
|
-
if (
|
|
159
|
-
|
|
160
|
-
|
|
157
|
+
function N() {
|
|
158
|
+
let E = h(), [N] = g(), P = e((e) => e.employees), F = e((e) => e.timeEntries), I = e((e) => e.leaveRequests), L = e((e) => e.payrollRecords), R = e((e) => e.projects), z = e((e) => e.initialized), B = e((e) => e.employeeDocuments), V = e((e) => e.fetchEmployeeDocuments), pe = e((e) => e.uploadEmployeeDocument), me = e((e) => e.detachEmployeeDocument), { showToast: H } = ue(), [U, he] = u("overview"), [W, G] = u(!1), [K, q] = u(null), J = ee(null), ge = N.get("id"), Y = P.find((e) => e.id === ge);
|
|
159
|
+
if (l(() => {
|
|
160
|
+
U === "documents" && Y && J.current !== Y.id && (J.current = Y.id, V(Y.id).catch((e) => {
|
|
161
|
+
H(e instanceof Error ? e.message : "Unable to load documents", "error");
|
|
161
162
|
}));
|
|
162
163
|
}, [
|
|
163
|
-
|
|
164
|
+
U,
|
|
164
165
|
Y,
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
]), !
|
|
166
|
+
V,
|
|
167
|
+
H
|
|
168
|
+
]), !z && !Y) return /* @__PURE__ */ _("div", {
|
|
168
169
|
className: "flex min-h-[60vh] items-center justify-center p-6",
|
|
169
|
-
children: /* @__PURE__ */
|
|
170
|
+
children: /* @__PURE__ */ _(le, { size: "lg" })
|
|
170
171
|
});
|
|
171
|
-
if (!Y) return /* @__PURE__ */
|
|
172
|
+
if (!Y) return /* @__PURE__ */ v("div", {
|
|
172
173
|
className: "space-y-4 p-6",
|
|
173
|
-
children: [/* @__PURE__ */
|
|
174
|
+
children: [/* @__PURE__ */ v(b, {
|
|
174
175
|
variant: "ghost",
|
|
175
176
|
size: "sm",
|
|
176
|
-
onClick: () =>
|
|
177
|
-
children: [/* @__PURE__ */
|
|
178
|
-
}), /* @__PURE__ */
|
|
177
|
+
onClick: () => E("../hr/employees-page"),
|
|
178
|
+
children: [/* @__PURE__ */ _(d, { className: "mr-1 h-4 w-4" }), "Back to directory"]
|
|
179
|
+
}), /* @__PURE__ */ _(T, {
|
|
179
180
|
illustration: "empty-search",
|
|
180
181
|
title: "Employee not found",
|
|
181
182
|
description: "This profile is no longer available or the link is out of date."
|
|
182
183
|
})]
|
|
183
184
|
});
|
|
184
|
-
let X = Y.managerId ?
|
|
185
|
-
return /* @__PURE__ */
|
|
185
|
+
let X = Y.managerId ? P.find((e) => e.id === Y.managerId) : void 0, Z = X ? `${X.firstName} ${X.lastName}` : Y.managerId ? "Assigned" : "No manager", Q = `${Y.firstName} ${Y.lastName}`, $ = M(Y, F, I, L, R);
|
|
186
|
+
return /* @__PURE__ */ v("div", {
|
|
186
187
|
className: "space-y-6 p-6",
|
|
187
188
|
children: [
|
|
188
|
-
/* @__PURE__ */
|
|
189
|
+
/* @__PURE__ */ v(b, {
|
|
189
190
|
variant: "ghost",
|
|
190
191
|
size: "sm",
|
|
191
|
-
onClick: () =>
|
|
192
|
-
children: [/* @__PURE__ */
|
|
192
|
+
onClick: () => E("../hr/employees-page"),
|
|
193
|
+
children: [/* @__PURE__ */ _(d, { className: "mr-1 h-4 w-4" }), "Back to directory"]
|
|
193
194
|
}),
|
|
194
|
-
/* @__PURE__ */
|
|
195
|
+
/* @__PURE__ */ _(oe, {
|
|
195
196
|
treatment: "glass",
|
|
196
197
|
glow: !0,
|
|
197
198
|
className: "border-border-seam",
|
|
198
|
-
children: /* @__PURE__ */
|
|
199
|
+
children: /* @__PURE__ */ _(S, {
|
|
199
200
|
className: "p-6",
|
|
200
|
-
children: /* @__PURE__ */
|
|
201
|
+
children: /* @__PURE__ */ v("div", {
|
|
201
202
|
className: "flex flex-col gap-5 sm:flex-row sm:items-start",
|
|
202
|
-
children: [/* @__PURE__ */
|
|
203
|
+
children: [/* @__PURE__ */ _(i, {
|
|
203
204
|
people: [{
|
|
204
205
|
name: Q,
|
|
205
206
|
avatarUrl: Y.avatar
|
|
206
207
|
}],
|
|
207
208
|
max: 1,
|
|
208
209
|
size: 72
|
|
209
|
-
}), /* @__PURE__ */
|
|
210
|
+
}), /* @__PURE__ */ v("div", {
|
|
210
211
|
className: "flex-1 space-y-3",
|
|
211
212
|
children: [
|
|
212
|
-
/* @__PURE__ */
|
|
213
|
+
/* @__PURE__ */ v("div", {
|
|
213
214
|
className: "flex flex-wrap items-center gap-3",
|
|
214
|
-
children: [/* @__PURE__ */
|
|
215
|
+
children: [/* @__PURE__ */ _("h1", {
|
|
215
216
|
className: "text-2xl font-semibold",
|
|
216
|
-
children: /* @__PURE__ */
|
|
217
|
-
}), /* @__PURE__ */
|
|
218
|
-
variant:
|
|
217
|
+
children: /* @__PURE__ */ _(se, { children: Q })
|
|
218
|
+
}), /* @__PURE__ */ _(y, {
|
|
219
|
+
variant: k(Y.status),
|
|
219
220
|
children: t(Y.status)
|
|
220
221
|
})]
|
|
221
222
|
}),
|
|
222
|
-
/* @__PURE__ */
|
|
223
|
+
/* @__PURE__ */ v("p", {
|
|
223
224
|
className: "text-sm text-text-secondary",
|
|
224
225
|
children: [
|
|
225
226
|
Y.position,
|
|
@@ -227,23 +228,23 @@ function P() {
|
|
|
227
228
|
Y.department
|
|
228
229
|
]
|
|
229
230
|
}),
|
|
230
|
-
/* @__PURE__ */
|
|
231
|
+
/* @__PURE__ */ v("div", {
|
|
231
232
|
className: "flex flex-wrap gap-2",
|
|
232
233
|
children: [
|
|
233
|
-
/* @__PURE__ */
|
|
234
|
-
icon: /* @__PURE__ */
|
|
234
|
+
/* @__PURE__ */ _(A, {
|
|
235
|
+
icon: /* @__PURE__ */ _(ne, { className: "h-3.5 w-3.5" }),
|
|
235
236
|
children: Y.email
|
|
236
237
|
}),
|
|
237
|
-
/* @__PURE__ */
|
|
238
|
-
icon: /* @__PURE__ */
|
|
238
|
+
/* @__PURE__ */ _(A, {
|
|
239
|
+
icon: /* @__PURE__ */ _(ie, { className: "h-3.5 w-3.5" }),
|
|
239
240
|
children: Y.phone
|
|
240
241
|
}),
|
|
241
|
-
/* @__PURE__ */
|
|
242
|
-
icon: /* @__PURE__ */
|
|
242
|
+
/* @__PURE__ */ _(A, {
|
|
243
|
+
icon: /* @__PURE__ */ _(re, { className: "h-3.5 w-3.5" }),
|
|
243
244
|
children: Y.location
|
|
244
245
|
}),
|
|
245
|
-
/* @__PURE__ */
|
|
246
|
-
icon: /* @__PURE__ */
|
|
246
|
+
/* @__PURE__ */ v(A, {
|
|
247
|
+
icon: /* @__PURE__ */ _(p, { className: "h-3.5 w-3.5" }),
|
|
247
248
|
children: ["Joined ", Y.startDate]
|
|
248
249
|
})
|
|
249
250
|
]
|
|
@@ -253,26 +254,26 @@ function P() {
|
|
|
253
254
|
})
|
|
254
255
|
})
|
|
255
256
|
}),
|
|
256
|
-
/* @__PURE__ */
|
|
257
|
-
/* @__PURE__ */
|
|
258
|
-
tabs:
|
|
259
|
-
value:
|
|
260
|
-
onChange: (e) =>
|
|
257
|
+
/* @__PURE__ */ _(ce, { children: "A single view of one team member — their role, tenure, performance, and recent activity — so managers and partners have the full context in one place." }),
|
|
258
|
+
/* @__PURE__ */ _(a, {
|
|
259
|
+
tabs: de,
|
|
260
|
+
value: U,
|
|
261
|
+
onChange: (e) => he(e),
|
|
261
262
|
"aria-label": "Employee profile sections"
|
|
262
263
|
}),
|
|
263
|
-
|
|
264
|
+
U === "overview" ? /* @__PURE__ */ v("div", {
|
|
264
265
|
className: "grid gap-4 lg:grid-cols-2",
|
|
265
|
-
children: [/* @__PURE__ */
|
|
266
|
+
children: [/* @__PURE__ */ v(x, { children: [/* @__PURE__ */ _(C, { children: /* @__PURE__ */ _(w, {
|
|
266
267
|
className: "text-base",
|
|
267
268
|
children: "Details"
|
|
268
|
-
}) }), /* @__PURE__ */
|
|
269
|
+
}) }), /* @__PURE__ */ v(S, {
|
|
269
270
|
className: "divide-y divide-border-subtle pt-0",
|
|
270
271
|
children: [
|
|
271
|
-
/* @__PURE__ */
|
|
272
|
+
/* @__PURE__ */ _(j, {
|
|
272
273
|
label: "Manager",
|
|
273
|
-
children: /* @__PURE__ */
|
|
274
|
+
children: /* @__PURE__ */ v("span", {
|
|
274
275
|
className: "inline-flex items-center gap-2",
|
|
275
|
-
children: [X ? /* @__PURE__ */
|
|
276
|
+
children: [X ? /* @__PURE__ */ _(i, {
|
|
276
277
|
people: [{
|
|
277
278
|
name: Z,
|
|
278
279
|
avatarUrl: X.avatar
|
|
@@ -282,42 +283,42 @@ function P() {
|
|
|
282
283
|
}) : null, Z]
|
|
283
284
|
})
|
|
284
285
|
}),
|
|
285
|
-
/* @__PURE__ */
|
|
286
|
+
/* @__PURE__ */ _(j, {
|
|
286
287
|
label: "Employment type",
|
|
287
288
|
children: t(Y.type)
|
|
288
289
|
}),
|
|
289
|
-
/* @__PURE__ */
|
|
290
|
+
/* @__PURE__ */ _(j, {
|
|
290
291
|
label: "Salary band",
|
|
291
|
-
children:
|
|
292
|
+
children: fe(Y.salary)
|
|
292
293
|
}),
|
|
293
|
-
/* @__PURE__ */
|
|
294
|
+
/* @__PURE__ */ _(j, {
|
|
294
295
|
label: "Tenure",
|
|
295
|
-
children:
|
|
296
|
+
children: D(Y.startDate)
|
|
296
297
|
}),
|
|
297
|
-
/* @__PURE__ */
|
|
298
|
+
/* @__PURE__ */ _(j, {
|
|
298
299
|
label: "Employee ID",
|
|
299
|
-
children: /* @__PURE__ */
|
|
300
|
+
children: /* @__PURE__ */ _("span", {
|
|
300
301
|
className: "break-all font-mono text-xs",
|
|
301
302
|
children: Y.id
|
|
302
303
|
})
|
|
303
304
|
})
|
|
304
305
|
]
|
|
305
|
-
})] }), /* @__PURE__ */
|
|
306
|
+
})] }), /* @__PURE__ */ v(x, { children: [/* @__PURE__ */ _(C, { children: /* @__PURE__ */ _(w, {
|
|
306
307
|
className: "text-base",
|
|
307
308
|
children: "Performance"
|
|
308
|
-
}) }), /* @__PURE__ */
|
|
309
|
+
}) }), /* @__PURE__ */ v(S, {
|
|
309
310
|
className: "flex items-center gap-6 pt-0",
|
|
310
|
-
children: [/* @__PURE__ */
|
|
311
|
+
children: [/* @__PURE__ */ _(n, {
|
|
311
312
|
value: Y.performanceScore,
|
|
312
313
|
max: 5,
|
|
313
314
|
label: Y.performanceScore.toFixed(1),
|
|
314
315
|
caption: "/ 5"
|
|
315
|
-
}), /* @__PURE__ */
|
|
316
|
+
}), /* @__PURE__ */ v("div", {
|
|
316
317
|
className: "space-y-1",
|
|
317
|
-
children: [/* @__PURE__ */
|
|
318
|
+
children: [/* @__PURE__ */ _("p", {
|
|
318
319
|
className: "text-lg font-semibold text-text-primary",
|
|
319
|
-
children:
|
|
320
|
-
}), /* @__PURE__ */
|
|
320
|
+
children: O(Y.performanceScore)
|
|
321
|
+
}), /* @__PURE__ */ v("p", {
|
|
321
322
|
className: "text-sm text-text-secondary",
|
|
322
323
|
children: [
|
|
323
324
|
"Latest review score of ",
|
|
@@ -328,48 +329,37 @@ function P() {
|
|
|
328
329
|
})]
|
|
329
330
|
})] })]
|
|
330
331
|
}) : null,
|
|
331
|
-
|
|
332
|
+
U === "skills" ? /* @__PURE__ */ v(x, { children: [/* @__PURE__ */ _(C, { children: /* @__PURE__ */ _(w, {
|
|
332
333
|
className: "text-base",
|
|
333
334
|
children: "Skills"
|
|
334
|
-
}) }), /* @__PURE__ */
|
|
335
|
+
}) }), /* @__PURE__ */ _(S, {
|
|
335
336
|
className: "pt-0",
|
|
336
|
-
children:
|
|
337
|
-
className: "flex flex-wrap gap-2",
|
|
338
|
-
children: Y.skills.map((e) => /* @__PURE__ */ m(g, {
|
|
339
|
-
variant: "secondary",
|
|
340
|
-
className: "text-xs",
|
|
341
|
-
children: e
|
|
342
|
-
}, e))
|
|
343
|
-
}) : /* @__PURE__ */ m(C, {
|
|
344
|
-
illustration: "empty-data",
|
|
345
|
-
title: "No skills recorded",
|
|
346
|
-
description: "This team member has no skills captured yet. Add skills from the employee directory to surface them here."
|
|
347
|
-
})
|
|
337
|
+
children: /* @__PURE__ */ _(c, { employeeId: Y.id })
|
|
348
338
|
})] }) : null,
|
|
349
|
-
|
|
339
|
+
U === "documents" ? /* @__PURE__ */ v(x, { children: [/* @__PURE__ */ v(C, {
|
|
350
340
|
className: "flex flex-row items-center justify-between",
|
|
351
|
-
children: [/* @__PURE__ */
|
|
341
|
+
children: [/* @__PURE__ */ _(w, {
|
|
352
342
|
className: "text-base",
|
|
353
343
|
children: "Documents"
|
|
354
|
-
}), /* @__PURE__ */
|
|
344
|
+
}), /* @__PURE__ */ v(b, {
|
|
355
345
|
size: "sm",
|
|
356
346
|
onClick: () => G(!0),
|
|
357
|
-
children: [/* @__PURE__ */ m
|
|
347
|
+
children: [/* @__PURE__ */ _(m, { className: "mr-1.5 h-3.5 w-3.5" }), "Attach document"]
|
|
358
348
|
})]
|
|
359
|
-
}), /* @__PURE__ */
|
|
349
|
+
}), /* @__PURE__ */ _(S, {
|
|
360
350
|
className: "pt-0",
|
|
361
|
-
children:
|
|
351
|
+
children: B.length > 0 ? /* @__PURE__ */ _("div", {
|
|
362
352
|
className: "space-y-2",
|
|
363
|
-
children:
|
|
353
|
+
children: B.map((e) => /* @__PURE__ */ v("div", {
|
|
364
354
|
className: "flex items-center justify-between gap-3 rounded-input border border-border-subtle bg-bg-surface px-3 py-2.5",
|
|
365
|
-
children: [/* @__PURE__ */
|
|
355
|
+
children: [/* @__PURE__ */ v("div", {
|
|
366
356
|
className: "flex min-w-0 items-center gap-3",
|
|
367
|
-
children: [/* @__PURE__ */
|
|
357
|
+
children: [/* @__PURE__ */ _(te, { className: "h-4 w-4 shrink-0 text-text-muted" }), /* @__PURE__ */ v("div", {
|
|
368
358
|
className: "min-w-0",
|
|
369
|
-
children: [/* @__PURE__ */
|
|
359
|
+
children: [/* @__PURE__ */ _("p", {
|
|
370
360
|
className: "truncate text-sm font-medium text-text-primary",
|
|
371
361
|
children: e.title
|
|
372
|
-
}), /* @__PURE__ */
|
|
362
|
+
}), /* @__PURE__ */ v("p", {
|
|
373
363
|
className: "text-xs text-text-muted",
|
|
374
364
|
children: [
|
|
375
365
|
e.category ? `${e.category} · ` : "",
|
|
@@ -378,45 +368,45 @@ function P() {
|
|
|
378
368
|
]
|
|
379
369
|
})]
|
|
380
370
|
})]
|
|
381
|
-
}), /* @__PURE__ */
|
|
371
|
+
}), /* @__PURE__ */ _("button", {
|
|
382
372
|
type: "button",
|
|
383
373
|
"aria-label": `Remove document "${e.title}"`,
|
|
384
374
|
className: "shrink-0 text-text-muted hover:text-status-error-fg",
|
|
385
375
|
onClick: () => q(e.id),
|
|
386
|
-
children: /* @__PURE__ */
|
|
376
|
+
children: /* @__PURE__ */ _(ae, { className: "h-4 w-4" })
|
|
387
377
|
})]
|
|
388
378
|
}, e.id))
|
|
389
|
-
}) : /* @__PURE__ */
|
|
379
|
+
}) : /* @__PURE__ */ _(T, {
|
|
390
380
|
illustration: "empty-data",
|
|
391
381
|
title: "No documents attached",
|
|
392
382
|
description: "Offer letters, contracts, and compliance documents attached to this profile will appear here.",
|
|
393
|
-
action: /* @__PURE__ */
|
|
383
|
+
action: /* @__PURE__ */ v(b, {
|
|
394
384
|
size: "sm",
|
|
395
385
|
onClick: () => G(!0),
|
|
396
|
-
children: [/* @__PURE__ */ m
|
|
386
|
+
children: [/* @__PURE__ */ _(m, { className: "mr-1.5 h-3.5 w-3.5" }), "Attach document"]
|
|
397
387
|
})
|
|
398
388
|
})
|
|
399
389
|
})] }) : null,
|
|
400
|
-
|
|
390
|
+
U === "performance" ? /* @__PURE__ */ v(x, { children: [/* @__PURE__ */ _(C, { children: /* @__PURE__ */ _(w, {
|
|
401
391
|
className: "text-base",
|
|
402
392
|
children: "Performance"
|
|
403
|
-
}) }), /* @__PURE__ */
|
|
393
|
+
}) }), /* @__PURE__ */ v(S, {
|
|
404
394
|
className: "flex flex-col items-center gap-6 pt-0 sm:flex-row sm:items-center",
|
|
405
|
-
children: [/* @__PURE__ */
|
|
395
|
+
children: [/* @__PURE__ */ _(n, {
|
|
406
396
|
value: Y.performanceScore,
|
|
407
397
|
max: 5,
|
|
408
398
|
size: 160,
|
|
409
399
|
label: Y.performanceScore.toFixed(1),
|
|
410
400
|
caption: "/ 5"
|
|
411
|
-
}), /* @__PURE__ */
|
|
401
|
+
}), /* @__PURE__ */ v("div", {
|
|
412
402
|
className: "flex-1 space-y-2",
|
|
413
403
|
children: [
|
|
414
|
-
/* @__PURE__ */
|
|
404
|
+
/* @__PURE__ */ v(y, {
|
|
415
405
|
variant: "secondary",
|
|
416
406
|
className: "gap-1",
|
|
417
|
-
children: [/* @__PURE__ */
|
|
407
|
+
children: [/* @__PURE__ */ _(f, { className: "h-3.5 w-3.5" }), O(Y.performanceScore)]
|
|
418
408
|
}),
|
|
419
|
-
/* @__PURE__ */
|
|
409
|
+
/* @__PURE__ */ v("p", {
|
|
420
410
|
className: "text-sm text-text-secondary",
|
|
421
411
|
children: [
|
|
422
412
|
Q,
|
|
@@ -425,35 +415,35 @@ function P() {
|
|
|
425
415
|
" out of 5."
|
|
426
416
|
]
|
|
427
417
|
}),
|
|
428
|
-
/* @__PURE__ */
|
|
418
|
+
/* @__PURE__ */ _("p", {
|
|
429
419
|
className: "text-xs text-text-muted",
|
|
430
420
|
children: "Goal, peer, and manager sub-scores will appear here once captured on the review record."
|
|
431
421
|
})
|
|
432
422
|
]
|
|
433
423
|
})]
|
|
434
424
|
})] }) : null,
|
|
435
|
-
|
|
425
|
+
U === "activity" ? /* @__PURE__ */ v(x, { children: [/* @__PURE__ */ _(C, { children: /* @__PURE__ */ _(w, {
|
|
436
426
|
className: "text-base",
|
|
437
427
|
children: "Recent activity"
|
|
438
|
-
}) }), /* @__PURE__ */
|
|
428
|
+
}) }), /* @__PURE__ */ _(S, {
|
|
439
429
|
className: "pt-0",
|
|
440
|
-
children: $.length > 0 ? /* @__PURE__ */
|
|
430
|
+
children: $.length > 0 ? /* @__PURE__ */ _(r, { items: $ }) : /* @__PURE__ */ _(T, {
|
|
441
431
|
illustration: "empty-data",
|
|
442
432
|
title: "No activity yet",
|
|
443
433
|
description: "Time logs, leave, payroll, and project assignments for this employee will show up here."
|
|
444
434
|
})
|
|
445
435
|
})] }) : null,
|
|
446
|
-
/* @__PURE__ */
|
|
447
|
-
open:
|
|
436
|
+
/* @__PURE__ */ _(s, {
|
|
437
|
+
open: W,
|
|
448
438
|
onOpenChange: G,
|
|
449
|
-
onUpload: ({ file: e, title: t, category: n }) =>
|
|
439
|
+
onUpload: ({ file: e, title: t, category: n }) => pe({
|
|
450
440
|
employeeId: Y.id,
|
|
451
441
|
file: e,
|
|
452
442
|
title: t,
|
|
453
443
|
category: n
|
|
454
444
|
})
|
|
455
445
|
}),
|
|
456
|
-
/* @__PURE__ */
|
|
446
|
+
/* @__PURE__ */ _(o, {
|
|
457
447
|
open: K !== null,
|
|
458
448
|
onOpenChange: (e) => {
|
|
459
449
|
e || q(null);
|
|
@@ -463,13 +453,13 @@ function P() {
|
|
|
463
453
|
confirmLabel: "Remove",
|
|
464
454
|
destructive: !0,
|
|
465
455
|
onConfirm: async () => {
|
|
466
|
-
K && (await
|
|
456
|
+
K && (await me(K), q(null));
|
|
467
457
|
}
|
|
468
458
|
})
|
|
469
459
|
]
|
|
470
460
|
});
|
|
471
461
|
}
|
|
472
462
|
//#endregion
|
|
473
|
-
export {
|
|
463
|
+
export { N as HrEmployeeProfilePagePage };
|
|
474
464
|
|
|
475
465
|
//# sourceMappingURL=HrEmployeeProfilePagePage.js.map
|