@burdenoff/microfe-timecampus 2026.626.2 → 2026.703.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/generated/wspace-operations.js +996 -816
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/generated/wspace-types.js +28 -0
- package/dist/generated/wspace-types.js.map +1 -0
- package/dist/index.js +4 -4
- package/dist/timecampus/TimeCampusRoutes.js +16 -12
- package/dist/timecampus/TimeCampusRoutes.js.map +1 -1
- package/dist/timecampus/components/Modal.js +65 -0
- package/dist/timecampus/components/Modal.js.map +1 -0
- package/dist/timecampus/components/StatusBadge.js +28 -0
- package/dist/timecampus/components/StatusBadge.js.map +1 -0
- package/dist/timecampus/components/Toast.js +87 -0
- package/dist/timecampus/components/Toast.js.map +1 -0
- package/dist/timecampus/index.js +1 -1
- package/dist/timecampus/pages/HomePage.js +143 -135
- package/dist/timecampus/pages/HomePage.js.map +1 -1
- package/dist/timecampus/pages/applets/AppletStorePage.js +14 -19
- package/dist/timecampus/pages/applets/AppletStorePage.js.map +1 -1
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js +49 -37
- package/dist/timecampus/pages/applets/InstalledAppletsPage.js.map +1 -1
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +315 -87
- package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
- package/dist/timecampus/pages/channels/ChannelsPage.js +340 -46
- package/dist/timecampus/pages/channels/ChannelsPage.js.map +1 -1
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js +374 -92
- package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
- package/dist/timecampus/pages/dashboard/ReportsPage.js +70 -37
- package/dist/timecampus/pages/dashboard/ReportsPage.js.map +1 -1
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +366 -60
- package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +461 -87
- package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/geofencing/GeofencingPage.js +314 -55
- package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js +30 -27
- package/dist/timecampus/pages/industry-solutions/IndustrySolutionsPage.js.map +1 -1
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +269 -74
- package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/ActionItemFormModal.js +127 -0
- package/dist/timecampus/pages/meetings/ActionItemFormModal.js.map +1 -0
- package/dist/timecampus/pages/meetings/MeetingCreatePage.js +62 -52
- package/dist/timecampus/pages/meetings/MeetingCreatePage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js +361 -74
- package/dist/timecampus/pages/meetings/MeetingDetailPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/MeetingFormModal.js +156 -0
- package/dist/timecampus/pages/meetings/MeetingFormModal.js.map +1 -0
- package/dist/timecampus/pages/meetings/MeetingsPage.js +175 -74
- package/dist/timecampus/pages/meetings/MeetingsPage.js.map +1 -1
- package/dist/timecampus/pages/meetings/ParticipantFormModal.js +105 -0
- package/dist/timecampus/pages/meetings/ParticipantFormModal.js.map +1 -0
- package/dist/timecampus/pages/pomodoro/PomodoroPage.js +307 -67
- package/dist/timecampus/pages/pomodoro/PomodoroPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectCreatePage.js +51 -48
- package/dist/timecampus/pages/projects/ProjectCreatePage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectDetailPage.js +248 -51
- package/dist/timecampus/pages/projects/ProjectDetailPage.js.map +1 -1
- package/dist/timecampus/pages/projects/ProjectsPage.js +354 -87
- package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
- package/dist/timecampus/pages/projects/TasksPage.js +367 -65
- package/dist/timecampus/pages/projects/TasksPage.js.map +1 -1
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +674 -104
- package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamAnalyticsPage.js +46 -34
- package/dist/timecampus/pages/team/TeamAnalyticsPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamMembersPage.js +93 -69
- package/dist/timecampus/pages/team/TeamMembersPage.js.map +1 -1
- package/dist/timecampus/pages/team/TeamPage.js +4 -3
- package/dist/timecampus/pages/team/TeamPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplateDetailPage.js +72 -53
- package/dist/timecampus/pages/templates/TemplateDetailPage.js.map +1 -1
- package/dist/timecampus/pages/templates/TemplatesPage.js +40 -35
- package/dist/timecampus/pages/templates/TemplatesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeEntriesPage.js +484 -97
- package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimeTrackingPage.js +210 -99
- package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
- package/dist/timecampus/pages/time/TimerPage.js +229 -97
- package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +315 -110
- package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
- package/dist/timecampus/pages/timeline/TimelinePage.js +250 -0
- package/dist/timecampus/pages/timeline/TimelinePage.js.map +1 -0
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js +158 -76
- package/dist/timecampus/pages/widgets/WidgetDetailPage.js.map +1 -1
- package/dist/timecampus/pages/widgets/WidgetsPage.js +181 -52
- package/dist/timecampus/pages/widgets/WidgetsPage.js.map +1 -1
- package/dist/timecampus/pages/workflows/WorkflowsPage.js +212 -61
- package/dist/timecampus/pages/workflows/WorkflowsPage.js.map +1 -1
- package/dist/timecampus/store/timecampusStore.js +13 -1
- package/dist/timecampus/store/timecampusStore.js.map +1 -1
- package/dist/timecampus/utils/helpers.js +20 -10
- package/dist/timecampus/utils/helpers.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResourcePlanningPage.js","names":[],"sources":["../../../../src/timecampus/pages/resource-planning/ResourcePlanningPage.tsx"],"sourcesContent":["import { CalendarRange, Users, TrendingUp } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { getStatusColor, getInitials } from \"../../utils/helpers\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useListResourcePlansQuery,\n useGetCapacityOverviewQuery,\n type ResourcePlanFieldsFragment,\n type ResourceAllocationFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function ResourcePlanningPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const now = new Date();\n const startDate = new Date(\n now.getFullYear(),\n now.getMonth(),\n now.getDate(),\n ).toISOString();\n const endDate = new Date(\n now.getFullYear(),\n now.getMonth(),\n now.getDate() + 7,\n ).toISOString();\n\n const {\n data: plansData,\n loading: plansLoading,\n error: plansError,\n } = useListResourcePlansQuery();\n const { data: capacityData } = useGetCapacityOverviewQuery({\n variables: { startDate, endDate },\n });\n\n const plans = plansData?.resourcePlans?.items ?? [];\n const capacity = capacityData?.capacityOverview;\n\n // Flatten all allocations across plans for display\n const allocations = plans.flatMap((plan: ResourcePlanFieldsFragment) =>\n plan.allocations.map((alloc: ResourceAllocationFieldsFragment) => ({\n ...alloc,\n planName: plan.name,\n })),\n );\n\n if (plansLoading) {\n return (\n <PageLayout\n title=\"Resource Planning\"\n description=\"Plan and optimize team resource allocation\"\n >\n <LoadingSkeleton variant=\"list\" count={5} />\n </PageLayout>\n );\n }\n\n if (plansError) {\n return (\n <PageLayout\n title=\"Resource Planning\"\n description=\"Plan and optimize team resource allocation\"\n >\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n Failed to load resource planning data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n const utilizationRate = capacity?.utilizationRate ?? 0;\n const overallocatedCount = capacity?.overallocatedUsers?.length ?? 0;\n const availableHours = capacity?.availableCapacity ?? 0;\n\n return (\n <PageLayout\n title=\"Resource Planning\"\n description=\"Plan and optimize team resource allocation\"\n purpose=\"Match the work ahead to the people you have — see who's over- or under-booked and how much capacity is free this week — so you can commit to deadlines you can actually hit without burning anyone out.\"\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title=\"Team Utilization\"\n value={`${Math.round(utilizationRate)}%`}\n icon={<TrendingUp className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Over-allocated\"\n value={String(overallocatedCount)}\n icon={<Users className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-error-bg text-status-error-text\"\n />\n <StatCard\n title=\"Active Plans\"\n value={String(plans.length)}\n icon={<Users className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Available Capacity\"\n value={`${Math.round(availableHours)}h`}\n icon={<CalendarRange className=\"h-5 w-5\" />}\n description=\"This week\"\n />\n </div>\n\n {allocations.length === 0 ? (\n <EmptyState\n illustration=\"empty-people\"\n title=\"No resource allocations found\"\n description=\"Create a resource plan to start allocating team members and balancing workload across the week.\"\n />\n ) : (\n <div className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\">\n <div className=\"divide-y divide-border\">\n {allocations.map(\n (\n alloc: ResourceAllocationFieldsFragment & { planName: string },\n ) => {\n const utilization =\n alloc.utilizationRate ??\n (alloc.allocatedHours > 0\n ? Math.round(\n (alloc.actualHours / alloc.allocatedHours) * 100,\n )\n : 0);\n return (\n <div\n key={alloc.id}\n className=\"flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-9 w-9 items-center justify-center rounded-full bg-action-primary-bg/10 text-sm font-medium text-primary\">\n {getInitials(alloc.userId)}\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {alloc.userId}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {alloc.planName} ·{\" \"}\n {alloc.startDate.slice(0, 10)} to{\" \"}\n {alloc.endDate.slice(0, 10)}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-4\">\n <div className=\"text-right\">\n <p className=\"text-sm font-medium text-text-primary\">\n {alloc.allocatedHours}h allocated\n </p>\n </div>\n <div className=\"w-24\">\n <div className=\"flex items-center justify-between text-xs mb-0.5\">\n <span\n className={`font-medium ${utilization > 100 ? \"text-status-error-text\" : utilization < 70 ? \"text-status-warning-text\" : \"text-status-success-text\"}`}\n >\n {utilization}%\n </span>\n </div>\n <div className=\"h-2 rounded-full bg-bg-sunken\">\n <div\n className={`h-2 rounded-full ${utilization > 100 ? \"bg-status-error-text\" : utilization < 70 ? \"bg-status-warning-text\" : \"bg-status-success-text\"}`}\n style={{ width: `${Math.min(utilization, 100)}%` }}\n />\n </div>\n </div>\n </div>\n </div>\n );\n },\n )}\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ResourcePlanningPage.js","names":[],"sources":["../../../../src/timecampus/pages/resource-planning/ResourcePlanningPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport {\n CalendarRange,\n Users,\n TrendingUp,\n Plus,\n Pencil,\n Trash2,\n UserPlus,\n} from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { Modal } from \"../../components/Modal\";\nimport { getStatusColor, getInitials } from \"../../utils/helpers\";\nimport {\n useListResourcePlansQuery,\n useGetCapacityOverviewQuery,\n useCreateResourcePlanMutation,\n useUpdateResourcePlanMutation,\n useDeleteResourcePlanMutation,\n useCreateAllocationMutation,\n useUpdateAllocationMutation,\n useDeleteAllocationMutation,\n useBulkCreateAllocationsMutation,\n type ResourcePlanFieldsFragment,\n type ResourceAllocationFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nconst PAGE_SIZE = 25;\n\nconst inputClass =\n \"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary\";\n\nconst toDateInput = (iso: string) => iso.slice(0, 10);\nconst toIso = (dateStr: string) => new Date(dateStr).toISOString();\n\ninterface PlanFormState {\n name: string;\n description: string;\n startDate: string;\n endDate: string;\n status: string;\n}\n\ninterface AllocationRow {\n userId: string;\n allocatedHours: string;\n startDate: string;\n endDate: string;\n notes: string;\n}\n\ninterface AllocationEditState {\n userId: string;\n allocatedHours: string;\n actualHours: string;\n startDate: string;\n endDate: string;\n notes: string;\n}\n\nconst emptyPlanForm = (): PlanFormState => {\n const now = new Date();\n const start = new Date(now.getFullYear(), now.getMonth(), now.getDate());\n const end = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 7);\n return {\n name: \"\",\n description: \"\",\n startDate: toDateInput(start.toISOString()),\n endDate: toDateInput(end.toISOString()),\n status: \"active\",\n };\n};\n\nconst emptyAllocationRow = (\n plan?: ResourcePlanFieldsFragment,\n): AllocationRow => ({\n userId: \"\",\n allocatedHours: \"\",\n startDate: plan ? toDateInput(plan.startDate) : \"\",\n endDate: plan ? toDateInput(plan.endDate) : \"\",\n notes: \"\",\n});\n\nexport function ResourcePlanningPage() {\n const now = new Date();\n const startDate = new Date(\n now.getFullYear(),\n now.getMonth(),\n now.getDate(),\n ).toISOString();\n const endDate = new Date(\n now.getFullYear(),\n now.getMonth(),\n now.getDate() + 7,\n ).toISOString();\n\n const {\n data: plansData,\n loading: plansLoading,\n error: plansError,\n refetch: refetchPlans,\n fetchMore,\n } = useListResourcePlansQuery({ variables: { limit: PAGE_SIZE, offset: 0 } });\n const { data: capacityData, refetch: refetchCapacity } =\n useGetCapacityOverviewQuery({ variables: { startDate, endDate } });\n\n const [createPlan, { loading: creatingPlan, error: createPlanError }] =\n useCreateResourcePlanMutation();\n const [updatePlan, { loading: updatingPlan, error: updatePlanError }] =\n useUpdateResourcePlanMutation();\n const [deletePlan, { loading: deletingPlan }] =\n useDeleteResourcePlanMutation();\n const [createAllocation, { loading: creatingAlloc }] =\n useCreateAllocationMutation();\n const [bulkCreateAllocations, { loading: bulkCreating }] =\n useBulkCreateAllocationsMutation();\n const [\n updateAllocation,\n { loading: updatingAlloc, error: updateAllocError },\n ] = useUpdateAllocationMutation();\n const [deleteAllocation, { loading: deletingAlloc }] =\n useDeleteAllocationMutation();\n\n const [planModalOpen, setPlanModalOpen] = useState(false);\n const [editingPlan, setEditingPlan] =\n useState<ResourcePlanFieldsFragment | null>(null);\n const [planForm, setPlanForm] = useState<PlanFormState>(emptyPlanForm());\n const [confirmDeletePlan, setConfirmDeletePlan] =\n useState<ResourcePlanFieldsFragment | null>(null);\n\n const [allocModalPlan, setAllocModalPlan] =\n useState<ResourcePlanFieldsFragment | null>(null);\n const [allocRows, setAllocRows] = useState<AllocationRow[]>([]);\n const [allocError, setAllocError] = useState(false);\n\n const [editingAlloc, setEditingAlloc] =\n useState<ResourceAllocationFieldsFragment | null>(null);\n const [allocEditForm, setAllocEditForm] =\n useState<AllocationEditState | null>(null);\n const [confirmDeleteAlloc, setConfirmDeleteAlloc] =\n useState<ResourceAllocationFieldsFragment | null>(null);\n\n const [loadingMore, setLoadingMore] = useState(false);\n\n const plans = plansData?.resourcePlans?.items ?? [];\n const hasMore = plansData?.resourcePlans?.hasMore ?? false;\n const capacity = capacityData?.capacityOverview;\n\n const refetchAll = async () => {\n await Promise.all([refetchPlans(), refetchCapacity()]);\n };\n\n // -- Plan handlers --------------------------------------------------------\n const openCreatePlan = () => {\n setEditingPlan(null);\n setPlanForm(emptyPlanForm());\n setPlanModalOpen(true);\n };\n\n const openEditPlan = (plan: ResourcePlanFieldsFragment) => {\n setEditingPlan(plan);\n setPlanForm({\n name: plan.name,\n description: plan.description ?? \"\",\n startDate: toDateInput(plan.startDate),\n endDate: toDateInput(plan.endDate),\n status: plan.status,\n });\n setPlanModalOpen(true);\n };\n\n const handleSubmitPlan = async () => {\n if (!planForm.name.trim()) return;\n if (editingPlan) {\n await updatePlan({\n variables: {\n id: editingPlan.id,\n input: {\n name: planForm.name.trim(),\n description: planForm.description.trim() || undefined,\n startDate: toIso(planForm.startDate),\n endDate: toIso(planForm.endDate),\n status: planForm.status,\n },\n },\n });\n } else {\n await createPlan({\n variables: {\n input: {\n name: planForm.name.trim(),\n description: planForm.description.trim() || undefined,\n startDate: toIso(planForm.startDate),\n endDate: toIso(planForm.endDate),\n },\n },\n });\n }\n setPlanModalOpen(false);\n await refetchAll();\n };\n\n const handleDeletePlan = async () => {\n if (!confirmDeletePlan) return;\n await deletePlan({ variables: { id: confirmDeletePlan.id } });\n setConfirmDeletePlan(null);\n await refetchAll();\n };\n\n // -- Allocation handlers --------------------------------------------------\n const openAddAllocation = (plan: ResourcePlanFieldsFragment) => {\n setAllocModalPlan(plan);\n setAllocRows([emptyAllocationRow(plan)]);\n setAllocError(false);\n };\n\n const updateAllocRow = (index: number, patch: Partial<AllocationRow>) => {\n setAllocRows((rows) =>\n rows.map((r, i) => (i === index ? { ...r, ...patch } : r)),\n );\n };\n\n const addAllocRow = () => {\n setAllocRows((rows) => [\n ...rows,\n emptyAllocationRow(allocModalPlan ?? undefined),\n ]);\n };\n\n const removeAllocRow = (index: number) => {\n setAllocRows((rows) => rows.filter((_, i) => i !== index));\n };\n\n const allocRowsValid = allocRows.every(\n (r) =>\n r.userId.trim() !== \"\" &&\n r.allocatedHours.trim() !== \"\" &&\n r.startDate !== \"\" &&\n r.endDate !== \"\",\n );\n\n const handleSubmitAllocations = async () => {\n if (!allocModalPlan || !allocRowsValid || allocRows.length === 0) return;\n setAllocError(false);\n try {\n if (allocRows.length === 1) {\n const r = allocRows[0];\n await createAllocation({\n variables: {\n input: {\n resourcePlanId: allocModalPlan.id,\n userId: r.userId.trim(),\n allocatedHours: Number(r.allocatedHours),\n startDate: toIso(r.startDate),\n endDate: toIso(r.endDate),\n notes: r.notes.trim() || undefined,\n },\n },\n });\n } else {\n await bulkCreateAllocations({\n variables: {\n planId: allocModalPlan.id,\n allocations: allocRows.map((r) => ({\n userId: r.userId.trim(),\n allocatedHours: Number(r.allocatedHours),\n startDate: toIso(r.startDate),\n endDate: toIso(r.endDate),\n notes: r.notes.trim() || undefined,\n })),\n },\n });\n }\n setAllocModalPlan(null);\n await refetchAll();\n } catch {\n setAllocError(true);\n }\n };\n\n const openEditAllocation = (alloc: ResourceAllocationFieldsFragment) => {\n setEditingAlloc(alloc);\n setAllocEditForm({\n userId: alloc.userId,\n allocatedHours: String(alloc.allocatedHours),\n actualHours: String(alloc.actualHours),\n startDate: toDateInput(alloc.startDate),\n endDate: toDateInput(alloc.endDate),\n notes: alloc.notes ?? \"\",\n });\n };\n\n const handleSubmitAllocEdit = async () => {\n if (!editingAlloc || !allocEditForm) return;\n await updateAllocation({\n variables: {\n id: editingAlloc.id,\n input: {\n userId: allocEditForm.userId.trim(),\n allocatedHours: Number(allocEditForm.allocatedHours),\n actualHours: Number(allocEditForm.actualHours),\n startDate: toIso(allocEditForm.startDate),\n endDate: toIso(allocEditForm.endDate),\n notes: allocEditForm.notes.trim() || undefined,\n },\n },\n });\n setEditingAlloc(null);\n setAllocEditForm(null);\n await refetchAll();\n };\n\n const handleDeleteAllocation = async () => {\n if (!confirmDeleteAlloc) return;\n await deleteAllocation({ variables: { id: confirmDeleteAlloc.id } });\n setConfirmDeleteAlloc(null);\n await refetchAll();\n };\n\n const handleLoadMore = async () => {\n setLoadingMore(true);\n try {\n await fetchMore({\n variables: { limit: PAGE_SIZE, offset: plans.length },\n updateQuery: (prev, { fetchMoreResult }) => {\n if (!fetchMoreResult) return prev;\n return {\n resourcePlans: {\n ...fetchMoreResult.resourcePlans,\n items: [\n ...prev.resourcePlans.items,\n ...fetchMoreResult.resourcePlans.items,\n ],\n },\n };\n },\n });\n } finally {\n setLoadingMore(false);\n }\n };\n\n const newPlanButton = (\n <button\n onClick={openCreatePlan}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus className=\"h-4 w-4\" />\n New Resource Plan\n </button>\n );\n\n if (plansLoading) {\n return (\n <PageLayout\n title=\"Resource Planning\"\n description=\"Plan and optimize team resource allocation\"\n >\n <LoadingSkeleton variant=\"list\" count={5} />\n </PageLayout>\n );\n }\n\n if (plansError) {\n return (\n <PageLayout\n title=\"Resource Planning\"\n description=\"Plan and optimize team resource allocation\"\n >\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n Failed to load resource planning data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n const utilizationRate = capacity?.utilizationRate ?? 0;\n const overallocatedCount = capacity?.overallocatedUsers?.length ?? 0;\n const availableHours = capacity?.availableCapacity ?? 0;\n\n return (\n <PageLayout\n title=\"Resource Planning\"\n description=\"Plan and optimize team resource allocation\"\n purpose=\"Match the work ahead to the people you have — see who's over- or under-booked and how much capacity is free this week — so you can commit to deadlines you can actually hit without burning anyone out.\"\n actions={newPlanButton}\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n featured\n title=\"Team Utilization\"\n value={`${Math.round(utilizationRate)}%`}\n icon={<TrendingUp className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Over-allocated\"\n value={String(overallocatedCount)}\n icon={<Users className=\"h-5 w-5\" />}\n iconBgColor=\"bg-status-error-bg text-status-error-text\"\n />\n <StatCard\n title=\"Active Plans\"\n value={String(plans.length)}\n icon={<Users className=\"h-5 w-5\" />}\n />\n <StatCard\n title=\"Available Capacity\"\n value={`${Math.round(availableHours)}h`}\n icon={<CalendarRange className=\"h-5 w-5\" />}\n description=\"This week\"\n />\n </div>\n\n {plans.length === 0 ? (\n <EmptyState\n illustration=\"empty-people\"\n title=\"No resource plans yet\"\n description=\"Create a resource plan to start allocating team members and balancing workload across the week.\"\n action={newPlanButton}\n />\n ) : (\n <div className=\"space-y-6\">\n {plans.map((plan: ResourcePlanFieldsFragment) => (\n <div\n key={plan.id}\n className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop\"\n >\n <div className=\"flex flex-col gap-3 border-b border-border-seam p-5 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <div className=\"flex items-center gap-2\">\n <p className=\"text-sm font-semibold text-text-primary\">\n {plan.name}\n </p>\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getStatusColor(plan.status)}`}\n >\n {plan.status}\n </span>\n </div>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {plan.startDate.slice(0, 10)} to {plan.endDate.slice(0, 10)}{\" \"}\n · {plan.totalAllocatedHours}h allocated ·{\" \"}\n {plan.allocations.length} people\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => openAddAllocation(plan)}\n className=\"inline-flex items-center gap-1 rounded-md bg-action-primary-bg px-2.5 py-1.5 text-xs font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <UserPlus className=\"h-3.5 w-3.5\" />\n Add Allocation\n </button>\n <button\n onClick={() => openEditPlan(plan)}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-2.5 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n <Pencil className=\"h-3.5 w-3.5\" />\n Edit\n </button>\n <button\n onClick={() => setConfirmDeletePlan(plan)}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-2.5 py-1.5 text-xs font-medium text-status-error-text hover:bg-status-error-bg\"\n >\n <Trash2 className=\"h-3.5 w-3.5\" />\n Delete\n </button>\n </div>\n </div>\n\n {plan.allocations.length === 0 ? (\n <div className=\"p-5 text-sm text-text-secondary\">\n No allocations yet. Use “Add Allocation” to assign\n team members.\n </div>\n ) : (\n <div className=\"divide-y divide-border\">\n {plan.allocations.map(\n (alloc: ResourceAllocationFieldsFragment) => {\n const utilization =\n alloc.utilizationRate ??\n (alloc.allocatedHours > 0\n ? Math.round(\n (alloc.actualHours / alloc.allocatedHours) * 100,\n )\n : 0);\n return (\n <div\n key={alloc.id}\n className=\"flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-9 w-9 items-center justify-center rounded-full bg-action-primary-bg/10 text-sm font-medium text-primary\">\n {getInitials(alloc.userId)}\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {alloc.userId}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {alloc.startDate.slice(0, 10)} to{\" \"}\n {alloc.endDate.slice(0, 10)}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-4\">\n <div className=\"text-right\">\n <p className=\"text-sm font-medium text-text-primary\">\n {alloc.allocatedHours}h allocated\n </p>\n </div>\n <div className=\"w-24\">\n <div className=\"flex items-center justify-between text-xs mb-0.5\">\n <span\n className={`font-medium ${utilization > 100 ? \"text-status-error-text\" : utilization < 70 ? \"text-status-warning-text\" : \"text-status-success-text\"}`}\n >\n {utilization}%\n </span>\n </div>\n <div className=\"h-2 rounded-full bg-bg-sunken\">\n <div\n className={`h-2 rounded-full ${utilization > 100 ? \"bg-status-error-text\" : utilization < 70 ? \"bg-status-warning-text\" : \"bg-status-success-text\"}`}\n style={{\n width: `${Math.min(utilization, 100)}%`,\n }}\n />\n </div>\n </div>\n <div className=\"flex items-center gap-1\">\n <button\n onClick={() => openEditAllocation(alloc)}\n aria-label=\"Edit allocation\"\n className=\"rounded-md border border-border-subtle p-1.5 text-text-primary hover:bg-bg-sunken/50\"\n >\n <Pencil className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => setConfirmDeleteAlloc(alloc)}\n aria-label=\"Delete allocation\"\n className=\"rounded-md border border-border-subtle p-1.5 text-status-error-text hover:bg-status-error-bg\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n </div>\n );\n },\n )}\n </div>\n )}\n </div>\n ))}\n {hasMore && (\n <div className=\"flex justify-center\">\n <button\n onClick={handleLoadMore}\n disabled={loadingMore}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50 disabled:opacity-50\"\n >\n {loadingMore ? \"Loading...\" : \"Load more\"}\n </button>\n </div>\n )}\n </div>\n )}\n\n {/* Plan create/edit modal */}\n <Modal\n open={planModalOpen}\n onClose={() => setPlanModalOpen(false)}\n title={editingPlan ? \"Edit Resource Plan\" : \"New Resource Plan\"}\n description=\"Set the window this plan covers, then allocate people to it.\"\n size=\"lg\"\n footer={\n <>\n <button\n onClick={() => setPlanModalOpen(false)}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Cancel\n </button>\n <button\n onClick={handleSubmitPlan}\n disabled={creatingPlan || updatingPlan || !planForm.name.trim()}\n className=\"rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {creatingPlan || updatingPlan\n ? \"Saving...\"\n : editingPlan\n ? \"Save Changes\"\n : \"Create Plan\"}\n </button>\n </>\n }\n >\n {(createPlanError || updatePlanError) && (\n <div className=\"mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text\">\n Failed to save resource plan. Please try again.\n </div>\n )}\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Plan Name\n </label>\n <input\n type=\"text\"\n placeholder=\"e.g. Q3 Delivery\"\n value={planForm.name}\n onChange={(e) =>\n setPlanForm({ ...planForm, name: e.target.value })\n }\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Description (optional)\n </label>\n <textarea\n rows={2}\n placeholder=\"What this plan covers\"\n value={planForm.description}\n onChange={(e) =>\n setPlanForm({ ...planForm, description: e.target.value })\n }\n className={inputClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Start Date\n </label>\n <input\n type=\"date\"\n value={planForm.startDate}\n onChange={(e) =>\n setPlanForm({ ...planForm, startDate: e.target.value })\n }\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n End Date\n </label>\n <input\n type=\"date\"\n value={planForm.endDate}\n onChange={(e) =>\n setPlanForm({ ...planForm, endDate: e.target.value })\n }\n className={inputClass}\n />\n </div>\n </div>\n {editingPlan && (\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Status\n </label>\n <select\n value={planForm.status}\n onChange={(e) =>\n setPlanForm({ ...planForm, status: e.target.value })\n }\n className={inputClass}\n >\n <option value=\"active\">Active</option>\n <option value=\"draft\">Draft</option>\n <option value=\"completed\">Completed</option>\n <option value=\"archived\">Archived</option>\n </select>\n </div>\n )}\n </div>\n </Modal>\n\n {/* Add allocation(s) modal */}\n <Modal\n open={allocModalPlan !== null}\n onClose={() => setAllocModalPlan(null)}\n title=\"Add Allocation\"\n description={\n allocModalPlan\n ? `Assign team members to \"${allocModalPlan.name}\".`\n : undefined\n }\n size=\"xl\"\n footer={\n <>\n <button\n onClick={() => setAllocModalPlan(null)}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Cancel\n </button>\n <button\n onClick={handleSubmitAllocations}\n disabled={creatingAlloc || bulkCreating || !allocRowsValid}\n className=\"rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {creatingAlloc || bulkCreating\n ? \"Saving...\"\n : allocRows.length > 1\n ? `Add ${allocRows.length} Allocations`\n : \"Add Allocation\"}\n </button>\n </>\n }\n >\n {allocError && (\n <div className=\"mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text\">\n Failed to add allocations. Please check the values and try again.\n </div>\n )}\n <div className=\"space-y-4\">\n {allocRows.map((row, index) => (\n <div\n key={index}\n className=\"rounded-lg border border-border-subtle p-3\"\n >\n <div className=\"mb-2 flex items-center justify-between\">\n <span className=\"text-xs font-medium text-text-secondary\">\n Allocation {index + 1}\n </span>\n {allocRows.length > 1 && (\n <button\n onClick={() => removeAllocRow(index)}\n className=\"text-xs font-medium text-status-error-text hover:underline\"\n >\n Remove\n </button>\n )}\n </div>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div>\n <label className=\"block text-xs font-medium text-text-primary\">\n User ID\n </label>\n <input\n type=\"text\"\n placeholder=\"User identifier\"\n value={row.userId}\n onChange={(e) =>\n updateAllocRow(index, { userId: e.target.value })\n }\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"block text-xs font-medium text-text-primary\">\n Allocated Hours\n </label>\n <input\n type=\"number\"\n placeholder=\"0\"\n value={row.allocatedHours}\n onChange={(e) =>\n updateAllocRow(index, {\n allocatedHours: e.target.value,\n })\n }\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"block text-xs font-medium text-text-primary\">\n Start Date\n </label>\n <input\n type=\"date\"\n value={row.startDate}\n onChange={(e) =>\n updateAllocRow(index, { startDate: e.target.value })\n }\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"block text-xs font-medium text-text-primary\">\n End Date\n </label>\n <input\n type=\"date\"\n value={row.endDate}\n onChange={(e) =>\n updateAllocRow(index, { endDate: e.target.value })\n }\n className={inputClass}\n />\n </div>\n <div className=\"sm:col-span-2\">\n <label className=\"block text-xs font-medium text-text-primary\">\n Notes (optional)\n </label>\n <input\n type=\"text\"\n placeholder=\"Context for this allocation\"\n value={row.notes}\n onChange={(e) =>\n updateAllocRow(index, { notes: e.target.value })\n }\n className={inputClass}\n />\n </div>\n </div>\n </div>\n ))}\n <button\n onClick={addAllocRow}\n className=\"inline-flex items-center gap-1 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n <Plus className=\"h-4 w-4\" />\n Add another person\n </button>\n </div>\n </Modal>\n\n {/* Edit allocation modal */}\n <Modal\n open={editingAlloc !== null && allocEditForm !== null}\n onClose={() => {\n setEditingAlloc(null);\n setAllocEditForm(null);\n }}\n title=\"Edit Allocation\"\n size=\"lg\"\n footer={\n <>\n <button\n onClick={() => {\n setEditingAlloc(null);\n setAllocEditForm(null);\n }}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Cancel\n </button>\n <button\n onClick={handleSubmitAllocEdit}\n disabled={updatingAlloc}\n className=\"rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {updatingAlloc ? \"Saving...\" : \"Save Changes\"}\n </button>\n </>\n }\n >\n {updateAllocError && (\n <div className=\"mb-4 rounded-lg border border-status-error-bg/40 bg-status-error-bg p-3 text-sm text-status-error-text\">\n Failed to update allocation. Please try again.\n </div>\n )}\n {allocEditForm && (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n User ID\n </label>\n <input\n type=\"text\"\n value={allocEditForm.userId}\n onChange={(e) =>\n setAllocEditForm({\n ...allocEditForm,\n userId: e.target.value,\n })\n }\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Allocated Hours\n </label>\n <input\n type=\"number\"\n value={allocEditForm.allocatedHours}\n onChange={(e) =>\n setAllocEditForm({\n ...allocEditForm,\n allocatedHours: e.target.value,\n })\n }\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Actual Hours\n </label>\n <input\n type=\"number\"\n value={allocEditForm.actualHours}\n onChange={(e) =>\n setAllocEditForm({\n ...allocEditForm,\n actualHours: e.target.value,\n })\n }\n className={inputClass}\n />\n </div>\n <div />\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n Start Date\n </label>\n <input\n type=\"date\"\n value={allocEditForm.startDate}\n onChange={(e) =>\n setAllocEditForm({\n ...allocEditForm,\n startDate: e.target.value,\n })\n }\n className={inputClass}\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary\">\n End Date\n </label>\n <input\n type=\"date\"\n value={allocEditForm.endDate}\n onChange={(e) =>\n setAllocEditForm({\n ...allocEditForm,\n endDate: e.target.value,\n })\n }\n className={inputClass}\n />\n </div>\n <div className=\"sm:col-span-2\">\n <label className=\"block text-sm font-medium text-text-primary\">\n Notes (optional)\n </label>\n <input\n type=\"text\"\n value={allocEditForm.notes}\n onChange={(e) =>\n setAllocEditForm({\n ...allocEditForm,\n notes: e.target.value,\n })\n }\n className={inputClass}\n />\n </div>\n </div>\n )}\n </Modal>\n\n {/* Delete plan confirm */}\n <Modal\n open={confirmDeletePlan !== null}\n onClose={() => setConfirmDeletePlan(null)}\n title=\"Delete Resource Plan\"\n description=\"This action cannot be undone.\"\n footer={\n <>\n <button\n onClick={() => setConfirmDeletePlan(null)}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Cancel\n </button>\n <button\n onClick={handleDeletePlan}\n disabled={deletingPlan}\n className=\"rounded-md bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg/80 disabled:opacity-50\"\n >\n {deletingPlan ? \"Deleting...\" : \"Delete\"}\n </button>\n </>\n }\n >\n <p className=\"text-sm text-text-secondary\">\n Are you sure you want to delete{\" \"}\n <span className=\"font-medium text-text-primary\">\n {confirmDeletePlan?.name}\n </span>\n ? All its allocations will be removed.\n </p>\n </Modal>\n\n {/* Delete allocation confirm */}\n <Modal\n open={confirmDeleteAlloc !== null}\n onClose={() => setConfirmDeleteAlloc(null)}\n title=\"Delete Allocation\"\n description=\"This action cannot be undone.\"\n footer={\n <>\n <button\n onClick={() => setConfirmDeleteAlloc(null)}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Cancel\n </button>\n <button\n onClick={handleDeleteAllocation}\n disabled={deletingAlloc}\n className=\"rounded-md bg-status-error-bg px-4 py-2 text-sm font-medium text-status-error-text hover:bg-status-error-bg/80 disabled:opacity-50\"\n >\n {deletingAlloc ? \"Deleting...\" : \"Delete\"}\n </button>\n </>\n }\n >\n <p className=\"text-sm text-text-secondary\">\n Remove the allocation for{\" \"}\n <span className=\"font-medium text-text-primary\">\n {confirmDeleteAlloc?.userId}\n </span>\n ?\n </p>\n </Modal>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,74 +1,86 @@
|
|
|
1
|
-
import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
|
|
2
|
-
import { LoadingSkeleton as
|
|
3
|
-
import { useGetTimeEntryStatsQuery as
|
|
4
|
-
import { PageLayout as
|
|
5
|
-
import { BarChart3 as
|
|
6
|
-
import { jsx as
|
|
7
|
-
import { GlassCard as
|
|
1
|
+
import { useTimeCampus as e, useTimeCampusNavigate as t } from "../../providers/TimeCampusProvider.js";
|
|
2
|
+
import { LoadingSkeleton as n } from "../../components/LoadingSkeleton.js";
|
|
3
|
+
import { useGetTimeEntryStatsQuery as r, useGetWorkPatternSummaryQuery as i } from "../../../generated/wspace-operations.js";
|
|
4
|
+
import { PageLayout as a } from "../../components/PageLayout.js";
|
|
5
|
+
import { BarChart3 as o, Users as s } from "lucide-react";
|
|
6
|
+
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
7
|
+
import { GlassCard as u } from "@burdenoff/fe-libs/ui";
|
|
8
8
|
//#region src/timecampus/pages/team/TeamAnalyticsPage.tsx
|
|
9
|
-
function
|
|
10
|
-
let { workspaceId:
|
|
11
|
-
|
|
9
|
+
function d() {
|
|
10
|
+
let { workspaceId: d = "" } = e(), f = t(), p = /* @__PURE__ */ l("div", {
|
|
11
|
+
className: "flex items-center gap-2",
|
|
12
|
+
children: [/* @__PURE__ */ c("button", {
|
|
13
|
+
onClick: () => f("/team"),
|
|
14
|
+
className: "inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
|
|
15
|
+
children: "Team Overview"
|
|
16
|
+
}), /* @__PURE__ */ l("button", {
|
|
17
|
+
onClick: () => f("/team/members"),
|
|
18
|
+
className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
|
|
19
|
+
children: [/* @__PURE__ */ c(s, { className: "h-4 w-4" }), "View Members"]
|
|
20
|
+
})]
|
|
21
|
+
}), { data: m, loading: h, error: g } = r({ skip: !d }), { data: _, loading: v } = i({ skip: !d }), y = m?.timeEntryStats, b = _?.workPatternSummary;
|
|
22
|
+
return h || v ? /* @__PURE__ */ c(a, {
|
|
12
23
|
title: "Team Analytics",
|
|
13
24
|
description: "Team performance insights and metrics",
|
|
14
|
-
children: /* @__PURE__ */
|
|
25
|
+
children: /* @__PURE__ */ c(n, {
|
|
15
26
|
variant: "card",
|
|
16
27
|
count: 4
|
|
17
28
|
})
|
|
18
|
-
}) :
|
|
29
|
+
}) : g ? /* @__PURE__ */ c(a, {
|
|
19
30
|
title: "Team Analytics",
|
|
20
31
|
description: "Team performance insights and metrics",
|
|
21
|
-
children: /* @__PURE__ */
|
|
32
|
+
children: /* @__PURE__ */ c("div", {
|
|
22
33
|
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
23
34
|
children: "Failed to load team analytics. Please try again later."
|
|
24
35
|
})
|
|
25
|
-
}) : /* @__PURE__ */
|
|
36
|
+
}) : /* @__PURE__ */ c(a, {
|
|
26
37
|
title: "Team Analytics",
|
|
27
38
|
description: "Team performance insights and metrics",
|
|
28
39
|
purpose: "Compare how your team works — hours by member, productive days, focus versus meeting time — so you can rebalance workload, recognise top contributors, and fix the patterns that slow everyone down.",
|
|
29
|
-
|
|
40
|
+
actions: p,
|
|
41
|
+
children: /* @__PURE__ */ l("div", {
|
|
30
42
|
className: "grid grid-cols-1 gap-6 lg:grid-cols-2",
|
|
31
|
-
children: [/* @__PURE__ */
|
|
43
|
+
children: [/* @__PURE__ */ l(u, {
|
|
32
44
|
treatment: "glass",
|
|
33
45
|
glow: !0,
|
|
34
46
|
className: "p-6",
|
|
35
|
-
children: [/* @__PURE__ */
|
|
47
|
+
children: [/* @__PURE__ */ l("div", {
|
|
36
48
|
className: "mb-4 flex items-center gap-2",
|
|
37
|
-
children: [/* @__PURE__ */ s
|
|
49
|
+
children: [/* @__PURE__ */ c(s, { className: "h-5 w-5 text-primary" }), /* @__PURE__ */ c("h3", {
|
|
38
50
|
className: "text-sm font-semibold text-text-primary",
|
|
39
51
|
children: "Hours by Member"
|
|
40
52
|
})]
|
|
41
|
-
}), /* @__PURE__ */
|
|
53
|
+
}), /* @__PURE__ */ c("div", {
|
|
42
54
|
className: "flex h-48 items-center justify-center rounded-lg border border-dashed border-border-subtle",
|
|
43
|
-
children: /* @__PURE__ */
|
|
55
|
+
children: /* @__PURE__ */ l("div", {
|
|
44
56
|
className: "text-center",
|
|
45
|
-
children: [/* @__PURE__ */
|
|
57
|
+
children: [/* @__PURE__ */ c("p", {
|
|
46
58
|
className: "text-sm text-text-secondary",
|
|
47
|
-
children:
|
|
48
|
-
}),
|
|
59
|
+
children: y ? `${Math.round(y.totalHours)}h total · ${Math.round(y.billableHours)}h billable` : "No data available"
|
|
60
|
+
}), b?.mostProductiveDay && /* @__PURE__ */ l("p", {
|
|
49
61
|
className: "mt-1 text-xs text-text-secondary",
|
|
50
|
-
children: ["Most productive: ",
|
|
62
|
+
children: ["Most productive: ", b.mostProductiveDay]
|
|
51
63
|
})]
|
|
52
64
|
})
|
|
53
65
|
})]
|
|
54
|
-
}), /* @__PURE__ */
|
|
66
|
+
}), /* @__PURE__ */ l("div", {
|
|
55
67
|
className: "rounded-xl border border-border-seam bg-bg-surface shadow-elevation-1 p-6",
|
|
56
|
-
children: [/* @__PURE__ */
|
|
68
|
+
children: [/* @__PURE__ */ l("div", {
|
|
57
69
|
className: "mb-4 flex items-center gap-2",
|
|
58
|
-
children: [/* @__PURE__ */
|
|
70
|
+
children: [/* @__PURE__ */ c(o, { className: "h-5 w-5 text-primary" }), /* @__PURE__ */ c("h3", {
|
|
59
71
|
className: "text-sm font-semibold text-text-primary",
|
|
60
72
|
children: "Productivity Trends"
|
|
61
73
|
})]
|
|
62
|
-
}), /* @__PURE__ */
|
|
74
|
+
}), /* @__PURE__ */ c("div", {
|
|
63
75
|
className: "flex h-48 items-center justify-center rounded-lg border border-dashed border-border-subtle",
|
|
64
|
-
children: /* @__PURE__ */
|
|
76
|
+
children: /* @__PURE__ */ l("div", {
|
|
65
77
|
className: "text-center",
|
|
66
|
-
children: [/* @__PURE__ */
|
|
78
|
+
children: [/* @__PURE__ */ c("p", {
|
|
67
79
|
className: "text-sm text-text-secondary",
|
|
68
|
-
children:
|
|
69
|
-
}),
|
|
80
|
+
children: b ? `${Math.round(b.averageProductivity)}% avg productivity · ${b.averageFocusHours.toFixed(1)}h avg focus` : "No data available"
|
|
81
|
+
}), b?.insights && b.insights.length > 0 && /* @__PURE__ */ c("p", {
|
|
70
82
|
className: "mt-1 text-xs text-text-secondary",
|
|
71
|
-
children:
|
|
83
|
+
children: b.insights[0].title
|
|
72
84
|
})]
|
|
73
85
|
})
|
|
74
86
|
})]
|
|
@@ -77,6 +89,6 @@ function u() {
|
|
|
77
89
|
});
|
|
78
90
|
}
|
|
79
91
|
//#endregion
|
|
80
|
-
export {
|
|
92
|
+
export { d as TeamAnalyticsPage };
|
|
81
93
|
|
|
82
94
|
//# sourceMappingURL=TeamAnalyticsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamAnalyticsPage.js","names":[],"sources":["../../../../src/timecampus/pages/team/TeamAnalyticsPage.tsx"],"sourcesContent":["import { BarChart3, Users } from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport {
|
|
1
|
+
{"version":3,"file":"TeamAnalyticsPage.js","names":[],"sources":["../../../../src/timecampus/pages/team/TeamAnalyticsPage.tsx"],"sourcesContent":["import { BarChart3, Users } from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport {\n useTimeCampus,\n useTimeCampusNavigate,\n} from \"../../providers/TimeCampusProvider\";\nimport {\n useGetTimeEntryStatsQuery,\n useGetWorkPatternSummaryQuery,\n} from \"@/generated/wspace-operations\";\n\nexport function TeamAnalyticsPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const navigate = useTimeCampusNavigate();\n\n const analyticsActions = (\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => navigate(\"/team\")}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Team Overview\n </button>\n <button\n onClick={() => navigate(\"/team/members\")}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Users className=\"h-4 w-4\" />\n View Members\n </button>\n </div>\n );\n\n const {\n data: statsData,\n loading: statsLoading,\n error: statsError,\n } = useGetTimeEntryStatsQuery({\n skip: !workspaceId,\n });\n\n const { data: patternData, loading: patternLoading } =\n useGetWorkPatternSummaryQuery({\n skip: !workspaceId,\n });\n\n const stats = statsData?.timeEntryStats;\n const pattern = patternData?.workPatternSummary;\n\n if (statsLoading || patternLoading) {\n return (\n <PageLayout\n title=\"Team Analytics\"\n description=\"Team performance insights and metrics\"\n >\n <LoadingSkeleton variant=\"card\" count={4} />\n </PageLayout>\n );\n }\n\n if (statsError) {\n return (\n <PageLayout\n title=\"Team Analytics\"\n description=\"Team performance insights and metrics\"\n >\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n Failed to load team analytics. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Team Analytics\"\n description=\"Team performance insights and metrics\"\n purpose=\"Compare how your team works — hours by member, productive days, focus versus meeting time — so you can rebalance workload, recognise top contributors, and fix the patterns that slow everyone down.\"\n actions={analyticsActions}\n >\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n <GlassCard treatment=\"glass\" glow className=\"p-6\">\n <div className=\"mb-4 flex items-center gap-2\">\n <Users className=\"h-5 w-5 text-primary\" />\n <h3 className=\"text-sm font-semibold text-text-primary\">\n Hours by Member\n </h3>\n </div>\n <div className=\"flex h-48 items-center justify-center rounded-lg border border-dashed border-border-subtle\">\n <div className=\"text-center\">\n <p className=\"text-sm text-text-secondary\">\n {stats\n ? `${Math.round(stats.totalHours)}h total · ${Math.round(stats.billableHours)}h billable`\n : \"No data available\"}\n </p>\n {pattern?.mostProductiveDay && (\n <p className=\"mt-1 text-xs text-text-secondary\">\n Most productive: {pattern.mostProductiveDay}\n </p>\n )}\n </div>\n </div>\n </GlassCard>\n <div className=\"rounded-xl border border-border-seam bg-bg-surface shadow-elevation-1 p-6\">\n <div className=\"mb-4 flex items-center gap-2\">\n <BarChart3 className=\"h-5 w-5 text-primary\" />\n <h3 className=\"text-sm font-semibold text-text-primary\">\n Productivity Trends\n </h3>\n </div>\n <div className=\"flex h-48 items-center justify-center rounded-lg border border-dashed border-border-subtle\">\n <div className=\"text-center\">\n <p className=\"text-sm text-text-secondary\">\n {pattern\n ? `${Math.round(pattern.averageProductivity)}% avg productivity · ${pattern.averageFocusHours.toFixed(1)}h avg focus`\n : \"No data available\"}\n </p>\n {pattern?.insights && pattern.insights.length > 0 && (\n <p className=\"mt-1 text-xs text-text-secondary\">\n {pattern.insights[0].title}\n </p>\n )}\n </div>\n </div>\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;AAaA,SAAgB,IAAoB;CAClC,IAAM,EAAE,iBAAc,OAAO,GAAe,EACtC,IAAW,GAAuB,EAElC,IACJ,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,UAAD;GACE,eAAe,EAAS,QAAQ;GAChC,WAAU;aACX;GAEQ,CAAA,EACT,kBAAC,UAAD;GACE,eAAe,EAAS,gBAAgB;GACxC,WAAU;aAFZ,CAIE,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA,EAAA,eAEtB;KACL;KAGF,EACJ,MAAM,GACN,SAAS,GACT,OAAO,MACL,EAA0B,EAC5B,MAAM,CAAC,GACR,CAAC,EAEI,EAAE,MAAM,GAAa,SAAS,MAClC,EAA8B,EAC5B,MAAM,CAAC,GACR,CAAC,EAEE,IAAQ,GAAW,gBACnB,IAAU,GAAa;AA0B7B,QAxBI,KAAgB,IAEhB,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;GAAK,CAAA;EACjC,CAAA,GAIb,IAEA,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;YAEZ,kBAAC,OAAD;GAAK,WAAU;aAAoG;GAE7G,CAAA;EACK,CAAA,GAKf,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;EACR,SAAS;YAET,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD;IAAW,WAAU;IAAQ,MAAA;IAAK,WAAU;cAA5C,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAO,WAAU,wBAAyB,CAAA,EAC1C,kBAAC,MAAD;MAAI,WAAU;gBAA0C;MAEnD,CAAA,CACD;QACN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,IACG,GAAG,KAAK,MAAM,EAAM,WAAW,CAAC,YAAY,KAAK,MAAM,EAAM,cAAc,CAAC,cAC5E;OACF,CAAA,EACH,GAAS,qBACR,kBAAC,KAAD;OAAG,WAAU;iBAAb,CAAgD,qBAC5B,EAAQ,kBACxB;SAEF;;KACF,CAAA,CACI;OACZ,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAW,WAAU,wBAAyB,CAAA,EAC9C,kBAAC,MAAD;MAAI,WAAU;gBAA0C;MAEnD,CAAA,CACD;QACN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,IACG,GAAG,KAAK,MAAM,EAAQ,oBAAoB,CAAC,uBAAuB,EAAQ,kBAAkB,QAAQ,EAAE,CAAC,eACvG;OACF,CAAA,EACH,GAAS,YAAY,EAAQ,SAAS,SAAS,KAC9C,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAQ,SAAS,GAAG;OACnB,CAAA,CAEF;;KACF,CAAA,CACF;MACF;;EACK,CAAA"}
|
|
@@ -1,103 +1,127 @@
|
|
|
1
|
-
import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
|
|
2
|
-
import { formatHours as
|
|
3
|
-
import { LoadingSkeleton as
|
|
4
|
-
import { useListTimeEntriesQuery as
|
|
5
|
-
import { PageLayout as
|
|
6
|
-
import { EmptyState as
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
1
|
+
import { useTimeCampus as e, useTimeCampusNavigate as t } from "../../providers/TimeCampusProvider.js";
|
|
2
|
+
import { formatHours as n } from "../../utils/helpers.js";
|
|
3
|
+
import { LoadingSkeleton as r } from "../../components/LoadingSkeleton.js";
|
|
4
|
+
import { useListTimeEntriesQuery as i } from "../../../generated/wspace-operations.js";
|
|
5
|
+
import { PageLayout as a } from "../../components/PageLayout.js";
|
|
6
|
+
import { EmptyState as o } from "../../components/EmptyState.js";
|
|
7
|
+
import { Modal as s } from "../../components/Modal.js";
|
|
8
|
+
import { useState as c } from "react";
|
|
9
|
+
import { Activity as l, Plus as u, Users as d } from "lucide-react";
|
|
10
|
+
import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
11
|
+
import { GlassCard as h } from "@burdenoff/fe-libs/ui";
|
|
10
12
|
//#region src/timecampus/pages/team/TeamMembersPage.tsx
|
|
11
|
-
function
|
|
12
|
-
let { workspaceId:
|
|
13
|
+
function g() {
|
|
14
|
+
let { workspaceId: g = "" } = e(), _ = t(), [v, y] = c(!1), b = /* @__PURE__ */ m("button", {
|
|
15
|
+
onClick: () => y(!0),
|
|
16
|
+
className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
|
|
17
|
+
children: [/* @__PURE__ */ p(u, { className: "h-4 w-4" }), "Invite Member"]
|
|
18
|
+
}), { data: x, loading: S, error: C } = i({
|
|
13
19
|
variables: { limit: 100 },
|
|
14
|
-
skip: !
|
|
15
|
-
}),
|
|
20
|
+
skip: !g
|
|
21
|
+
}), w = (x?.timeEntries?.items ?? []).reduce((e, t) => {
|
|
16
22
|
let n = t.project?.name ?? "Unassigned", r = t.projectId ?? "unassigned";
|
|
17
23
|
return e[r] || (e[r] = {
|
|
18
24
|
name: n,
|
|
19
25
|
totalHours: 0,
|
|
20
26
|
entries: 0
|
|
21
27
|
}), e[r].totalHours += (t.duration ?? 0) / 3600, e[r].entries += 1, e;
|
|
22
|
-
}, {}),
|
|
28
|
+
}, {}), T = Object.entries(w).map(([e, t]) => ({
|
|
23
29
|
id: e,
|
|
24
30
|
...t
|
|
25
31
|
})).sort((e, t) => t.totalHours - e.totalHours);
|
|
26
|
-
return
|
|
32
|
+
return S ? /* @__PURE__ */ p(a, {
|
|
27
33
|
title: "Team Members",
|
|
28
34
|
description: "Manage team members and roles",
|
|
29
|
-
actions:
|
|
30
|
-
|
|
31
|
-
children: [/* @__PURE__ */ c(o, { className: "h-4 w-4" }), "Invite Member"]
|
|
32
|
-
}),
|
|
33
|
-
children: /* @__PURE__ */ c(n, {
|
|
35
|
+
actions: b,
|
|
36
|
+
children: /* @__PURE__ */ p(r, {
|
|
34
37
|
variant: "list",
|
|
35
38
|
count: 5
|
|
36
39
|
})
|
|
37
|
-
}) :
|
|
40
|
+
}) : C ? /* @__PURE__ */ p(a, {
|
|
38
41
|
title: "Team Members",
|
|
39
42
|
description: "Manage team members and roles",
|
|
40
|
-
children: /* @__PURE__ */
|
|
43
|
+
children: /* @__PURE__ */ p("div", {
|
|
41
44
|
className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
|
|
42
45
|
children: "Failed to load team data. Please try again later."
|
|
43
46
|
})
|
|
44
|
-
}) : /* @__PURE__ */
|
|
47
|
+
}) : /* @__PURE__ */ m(a, {
|
|
45
48
|
title: "Team Members",
|
|
46
49
|
description: "Manage team members and roles",
|
|
47
50
|
purpose: "Understand how effort is distributed across your team and projects — use it to spot who's overloaded, where time is concentrated, and when to invite or reassign people.",
|
|
48
|
-
actions:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}), /* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("p", {
|
|
73
|
-
className: "text-sm font-medium text-text-primary",
|
|
74
|
-
children: e.name
|
|
75
|
-
}), /* @__PURE__ */ l("p", {
|
|
76
|
-
className: "text-xs text-text-secondary",
|
|
77
|
-
children: [e.entries, " time entries"]
|
|
78
|
-
})] })]
|
|
79
|
-
}), /* @__PURE__ */ l("div", {
|
|
80
|
-
className: "flex items-center gap-4",
|
|
81
|
-
children: [/* @__PURE__ */ l("div", {
|
|
82
|
-
className: "text-right",
|
|
83
|
-
children: [/* @__PURE__ */ c("p", {
|
|
51
|
+
actions: b,
|
|
52
|
+
children: [
|
|
53
|
+
/* @__PURE__ */ p("div", {
|
|
54
|
+
className: "mb-4 rounded-lg border border-status-warning-bg/40 bg-status-warning-bg p-3 text-xs text-status-warning-text",
|
|
55
|
+
children: "Team member data is managed by the groups module. Showing time entry activity by project."
|
|
56
|
+
}),
|
|
57
|
+
T.length === 0 ? /* @__PURE__ */ p(o, {
|
|
58
|
+
illustration: "empty-people",
|
|
59
|
+
title: "No time entry data available",
|
|
60
|
+
description: "Once your team starts tracking time, their activity by project shows up here."
|
|
61
|
+
}) : /* @__PURE__ */ p(h, {
|
|
62
|
+
treatment: "glass",
|
|
63
|
+
glow: !0,
|
|
64
|
+
className: "overflow-hidden",
|
|
65
|
+
children: /* @__PURE__ */ p("div", {
|
|
66
|
+
className: "divide-y divide-border",
|
|
67
|
+
children: T.map((e) => /* @__PURE__ */ m("div", {
|
|
68
|
+
className: "flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors",
|
|
69
|
+
children: [/* @__PURE__ */ m("div", {
|
|
70
|
+
className: "flex items-center gap-3",
|
|
71
|
+
children: [/* @__PURE__ */ p("div", {
|
|
72
|
+
className: "flex h-9 w-9 items-center justify-center rounded-full bg-action-primary-bg/10 text-sm font-medium text-primary",
|
|
73
|
+
children: /* @__PURE__ */ p(d, { className: "h-4 w-4" })
|
|
74
|
+
}), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("p", {
|
|
84
75
|
className: "text-sm font-medium text-text-primary",
|
|
85
|
-
children:
|
|
86
|
-
}), /* @__PURE__ */
|
|
76
|
+
children: e.name
|
|
77
|
+
}), /* @__PURE__ */ m("p", {
|
|
87
78
|
className: "text-xs text-text-secondary",
|
|
88
|
-
children: "
|
|
79
|
+
children: [e.entries, " time entries"]
|
|
80
|
+
})] })]
|
|
81
|
+
}), /* @__PURE__ */ m("div", {
|
|
82
|
+
className: "flex items-center gap-4",
|
|
83
|
+
children: [/* @__PURE__ */ m("div", {
|
|
84
|
+
className: "text-right",
|
|
85
|
+
children: [/* @__PURE__ */ p("p", {
|
|
86
|
+
className: "text-sm font-medium text-text-primary",
|
|
87
|
+
children: n(e.totalHours)
|
|
88
|
+
}), /* @__PURE__ */ p("p", {
|
|
89
|
+
className: "text-xs text-text-secondary",
|
|
90
|
+
children: "total tracked"
|
|
91
|
+
})]
|
|
92
|
+
}), /* @__PURE__ */ p("span", {
|
|
93
|
+
className: "inline-flex rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text",
|
|
94
|
+
children: "active"
|
|
89
95
|
})]
|
|
90
|
-
}), /* @__PURE__ */ c("span", {
|
|
91
|
-
className: "inline-flex rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text",
|
|
92
|
-
children: "active"
|
|
93
96
|
})]
|
|
94
|
-
})
|
|
95
|
-
}
|
|
97
|
+
}, e.id))
|
|
98
|
+
})
|
|
99
|
+
}),
|
|
100
|
+
/* @__PURE__ */ p(s, {
|
|
101
|
+
open: v,
|
|
102
|
+
onClose: () => y(!1),
|
|
103
|
+
title: "Managing Team Members",
|
|
104
|
+
description: "How people are added to this workspace.",
|
|
105
|
+
footer: /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("button", {
|
|
106
|
+
onClick: () => y(!1),
|
|
107
|
+
className: "rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50",
|
|
108
|
+
children: "Close"
|
|
109
|
+
}), /* @__PURE__ */ m("button", {
|
|
110
|
+
onClick: () => {
|
|
111
|
+
y(!1), _("/team-activity");
|
|
112
|
+
},
|
|
113
|
+
className: "inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
|
|
114
|
+
children: [/* @__PURE__ */ p(l, { className: "h-4 w-4" }), "View Team Activity"]
|
|
115
|
+
})] }),
|
|
116
|
+
children: /* @__PURE__ */ p("p", {
|
|
117
|
+
className: "text-sm text-text-secondary",
|
|
118
|
+
children: "Team membership and roles are managed centrally by your workspace People & Groups administration, so invitations are sent from there. Once members join and start tracking time, their activity by project appears on this page and in the live team activity view."
|
|
119
|
+
})
|
|
96
120
|
})
|
|
97
|
-
|
|
121
|
+
]
|
|
98
122
|
});
|
|
99
123
|
}
|
|
100
124
|
//#endregion
|
|
101
|
-
export {
|
|
125
|
+
export { g as TeamMembersPage };
|
|
102
126
|
|
|
103
127
|
//# sourceMappingURL=TeamMembersPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamMembersPage.js","names":[],"sources":["../../../../src/timecampus/pages/team/TeamMembersPage.tsx"],"sourcesContent":["import { Users, Plus } from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { formatHours } from \"../../utils/helpers\";\nimport {
|
|
1
|
+
{"version":3,"file":"TeamMembersPage.js","names":[],"sources":["../../../../src/timecampus/pages/team/TeamMembersPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { Users, Plus, Activity } from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { Modal } from \"../../components/Modal\";\nimport { formatHours } from \"../../utils/helpers\";\nimport {\n useTimeCampus,\n useTimeCampusNavigate,\n} from \"../../providers/TimeCampusProvider\";\nimport {\n useListTimeEntriesQuery,\n type TimeEntryFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nexport function TeamMembersPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const navigate = useTimeCampusNavigate();\n const [inviteInfoOpen, setInviteInfoOpen] = useState(false);\n\n const inviteButton = (\n <button\n onClick={() => setInviteInfoOpen(true)}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus className=\"h-4 w-4\" />\n Invite Member\n </button>\n );\n\n const { data, loading, error } = useListTimeEntriesQuery({\n variables: { limit: 100 },\n skip: !workspaceId,\n });\n\n const entries = data?.timeEntries?.items ?? [];\n\n // Group time entries by project to show team activity (member data comes from groups module)\n const projectSummary = entries.reduce<\n Record<string, { name: string; totalHours: number; entries: number }>\n >(\n (\n acc: Record<\n string,\n { name: string; totalHours: number; entries: number }\n >,\n entry: TimeEntryFieldsFragment,\n ) => {\n const projectName = entry.project?.name ?? \"Unassigned\";\n const projectId = entry.projectId ?? \"unassigned\";\n if (!acc[projectId]) {\n acc[projectId] = { name: projectName, totalHours: 0, entries: 0 };\n }\n acc[projectId].totalHours += (entry.duration ?? 0) / 3600;\n acc[projectId].entries += 1;\n return acc;\n },\n {},\n );\n\n const summaryList = Object.entries(projectSummary)\n .map(([id, info]) => ({\n id,\n ...(info as { name: string; totalHours: number; entries: number }),\n }))\n .sort((a, b) => b.totalHours - a.totalHours);\n\n if (loading) {\n return (\n <PageLayout\n title=\"Team Members\"\n description=\"Manage team members and roles\"\n actions={inviteButton}\n >\n <LoadingSkeleton variant=\"list\" count={5} />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"Team Members\"\n description=\"Manage team members and roles\"\n >\n <div className=\"rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text\">\n Failed to load team data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Team Members\"\n description=\"Manage team members and roles\"\n purpose=\"Understand how effort is distributed across your team and projects — use it to spot who's overloaded, where time is concentrated, and when to invite or reassign people.\"\n actions={inviteButton}\n >\n <div className=\"mb-4 rounded-lg border border-status-warning-bg/40 bg-status-warning-bg p-3 text-xs text-status-warning-text\">\n Team member data is managed by the groups module. Showing time entry\n activity by project.\n </div>\n {summaryList.length === 0 ? (\n <EmptyState\n illustration=\"empty-people\"\n title=\"No time entry data available\"\n description=\"Once your team starts tracking time, their activity by project shows up here.\"\n />\n ) : (\n <GlassCard treatment=\"glass\" glow className=\"overflow-hidden\">\n <div className=\"divide-y divide-border\">\n {summaryList.map((project) => (\n <div\n key={project.id}\n className=\"flex items-center justify-between p-4 hover:bg-bg-sunken/50 transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-9 w-9 items-center justify-center rounded-full bg-action-primary-bg/10 text-sm font-medium text-primary\">\n <Users className=\"h-4 w-4\" />\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {project.name}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {project.entries} time entries\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-4\">\n <div className=\"text-right\">\n <p className=\"text-sm font-medium text-text-primary\">\n {formatHours(project.totalHours)}\n </p>\n <p className=\"text-xs text-text-secondary\">total tracked</p>\n </div>\n <span className=\"inline-flex rounded-full bg-status-success-bg px-2 py-0.5 text-xs font-medium text-status-success-text\">\n active\n </span>\n </div>\n </div>\n ))}\n </div>\n </GlassCard>\n )}\n\n <Modal\n open={inviteInfoOpen}\n onClose={() => setInviteInfoOpen(false)}\n title=\"Managing Team Members\"\n description=\"How people are added to this workspace.\"\n footer={\n <>\n <button\n onClick={() => setInviteInfoOpen(false)}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n Close\n </button>\n <button\n onClick={() => {\n setInviteInfoOpen(false);\n navigate(\"/team-activity\");\n }}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Activity className=\"h-4 w-4\" />\n View Team Activity\n </button>\n </>\n }\n >\n <p className=\"text-sm text-text-secondary\">\n Team membership and roles are managed centrally by your workspace\n People & Groups administration, so invitations are sent from\n there. Once members join and start tracking time, their activity by\n project appears on this page and in the live team activity view.\n </p>\n </Modal>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -95,11 +95,12 @@ function f() {
|
|
|
95
95
|
children: [/* @__PURE__ */ l("h3", {
|
|
96
96
|
className: "mb-4 text-sm font-semibold text-text-primary",
|
|
97
97
|
children: "Team Activity Feed"
|
|
98
|
-
}), /* @__PURE__ */ l("
|
|
99
|
-
|
|
98
|
+
}), /* @__PURE__ */ l("button", {
|
|
99
|
+
onClick: () => p("/team-activity"),
|
|
100
|
+
className: "flex h-48 w-full items-center justify-center rounded-lg border border-dashed border-border-subtle transition-colors hover:bg-bg-sunken/50",
|
|
100
101
|
children: /* @__PURE__ */ l("p", {
|
|
101
102
|
className: "text-sm text-text-secondary",
|
|
102
|
-
children: "
|
|
103
|
+
children: "Open the live team activity timeline"
|
|
103
104
|
})
|
|
104
105
|
})]
|
|
105
106
|
})
|