@burdenoff/microfe-timecampus 2026.719.2 → 2026.719.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/generated/wspace-operations.d.ts +193 -76
  2. package/dist/generated/wspace-operations.d.ts.map +1 -1
  3. package/dist/generated/wspace-operations.js +859 -788
  4. package/dist/generated/wspace-operations.js.map +1 -1
  5. package/dist/generated/wspace-types.d.ts +56 -38
  6. package/dist/generated/wspace-types.d.ts.map +1 -1
  7. package/dist/generated/wspace-types.js.map +1 -1
  8. package/dist/index.js +5 -5
  9. package/dist/timecampus/pages/HomePage.js +1 -1
  10. package/dist/timecampus/pages/HomePage.js.map +1 -1
  11. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js +6 -3
  12. package/dist/timecampus/pages/application-usage/ApplicationUsagePage.js.map +1 -1
  13. package/dist/timecampus/pages/client-portal/ClientPortalPage.js +4 -4
  14. package/dist/timecampus/pages/client-portal/ClientPortalPage.js.map +1 -1
  15. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.d.ts.map +1 -1
  16. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js +8 -3
  17. package/dist/timecampus/pages/email-tracking/EmailTrackingPage.js.map +1 -1
  18. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.d.ts.map +1 -1
  19. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js +19 -13
  20. package/dist/timecampus/pages/expense-tracking/ExpenseTrackingPage.js.map +1 -1
  21. package/dist/timecampus/pages/focus-score/FocusScorePage.js +1 -1
  22. package/dist/timecampus/pages/focus-score/FocusScorePage.js.map +1 -1
  23. package/dist/timecampus/pages/geofencing/GeofencingPage.d.ts.map +1 -1
  24. package/dist/timecampus/pages/geofencing/GeofencingPage.js +4 -4
  25. package/dist/timecampus/pages/geofencing/GeofencingPage.js.map +1 -1
  26. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.d.ts.map +1 -1
  27. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js +4 -4
  28. package/dist/timecampus/pages/location-tracking/LocationTrackingPage.js.map +1 -1
  29. package/dist/timecampus/pages/meetings/MeetingAnalyticsPage.d.ts.map +1 -1
  30. package/dist/timecampus/pages/meetings/MeetingAnalyticsPage.js +4 -2
  31. package/dist/timecampus/pages/meetings/MeetingAnalyticsPage.js.map +1 -1
  32. package/dist/timecampus/pages/meetings/MeetingsPage.d.ts.map +1 -1
  33. package/dist/timecampus/pages/meetings/MeetingsPage.js +6 -4
  34. package/dist/timecampus/pages/meetings/MeetingsPage.js.map +1 -1
  35. package/dist/timecampus/pages/pomodoro/PomodoroAnalyticsPage.js +1 -1
  36. package/dist/timecampus/pages/pomodoro/PomodoroAnalyticsPage.js.map +1 -1
  37. package/dist/timecampus/pages/projects/ProjectsPage.js +2 -2
  38. package/dist/timecampus/pages/projects/ProjectsPage.js.map +1 -1
  39. package/dist/timecampus/pages/projects/TasksPage.js +4 -2
  40. package/dist/timecampus/pages/projects/TasksPage.js.map +1 -1
  41. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.d.ts.map +1 -1
  42. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js +4 -4
  43. package/dist/timecampus/pages/resource-planning/ResourcePlanningPage.js.map +1 -1
  44. package/dist/timecampus/pages/team/TeamAnalyticsPage.d.ts.map +1 -1
  45. package/dist/timecampus/pages/team/TeamAnalyticsPage.js +2 -2
  46. package/dist/timecampus/pages/team/TeamAnalyticsPage.js.map +1 -1
  47. package/dist/timecampus/pages/team/TeamMembersPage.js +1 -1
  48. package/dist/timecampus/pages/team/TeamMembersPage.js.map +1 -1
  49. package/dist/timecampus/pages/team-activity/TeamActivityPage.d.ts.map +1 -1
  50. package/dist/timecampus/pages/team-activity/TeamActivityPage.js +49 -45
  51. package/dist/timecampus/pages/team-activity/TeamActivityPage.js.map +1 -1
  52. package/dist/timecampus/pages/time/TimeEntriesPage.d.ts.map +1 -1
  53. package/dist/timecampus/pages/time/TimeEntriesPage.js +137 -132
  54. package/dist/timecampus/pages/time/TimeEntriesPage.js.map +1 -1
  55. package/dist/timecampus/pages/time/TimeTrackingPage.d.ts.map +1 -1
  56. package/dist/timecampus/pages/time/TimeTrackingPage.js +2 -2
  57. package/dist/timecampus/pages/time/TimeTrackingPage.js.map +1 -1
  58. package/dist/timecampus/pages/time/TimerPage.d.ts.map +1 -1
  59. package/dist/timecampus/pages/time/TimerPage.js +2 -2
  60. package/dist/timecampus/pages/time/TimerPage.js.map +1 -1
  61. package/dist/timecampus/pages/time-approval/TimeApprovalPage.d.ts.map +1 -1
  62. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js +4 -4
  63. package/dist/timecampus/pages/time-approval/TimeApprovalPage.js.map +1 -1
  64. package/dist/timecampus/pages/timeline/TimelinePage.d.ts.map +1 -1
  65. package/dist/timecampus/pages/timeline/TimelinePage.js +224 -162
  66. package/dist/timecampus/pages/timeline/TimelinePage.js.map +1 -1
  67. package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js +1 -1
  68. package/dist/timecampus/pages/work-patterns/WorkPatternsPage.js.map +1 -1
  69. package/package.json +2 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectsPage.js","names":[],"sources":["../../../../src/timecampus/pages/projects/ProjectsPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { Plus, Pencil, Trash2, Archive } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { Modal } from \"../../components/Modal\";\nimport { useTimeCampusNavigate } from \"../../providers/TimeCampusProvider\";\nimport { getStatusColor, formatHours } from \"../../utils/helpers\";\nimport {\n useListProjectsQuery,\n useCreateProjectMutation,\n useUpdateProjectMutation,\n useArchiveProjectMutation,\n useDeleteProjectMutation,\n type ListProjectsQuery,\n} from \"@/generated/wspace-operations\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\n\ntype ProjectItem = NonNullable<ListProjectsQuery[\"projects\"]>[\"items\"][number];\n\nconst PAGE_SIZE = 25;\n\nconst PROJECT_STATUSES = [\n \"active\",\n \"on-hold\",\n \"completed\",\n \"archived\",\n] as const;\n\ninterface ProjectFormState {\n name: string;\n clientId: string;\n description: string;\n status: string;\n estimatedHours: string;\n hourlyRate: string;\n}\n\nconst emptyForm: ProjectFormState = {\n name: \"\",\n clientId: \"\",\n description: \"\",\n status: \"active\",\n estimatedHours: \"\",\n hourlyRate: \"\",\n};\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\";\nconst labelClass = \"block text-sm font-medium text-text-primary\";\n\nexport function ProjectsPage() {\n const { t: translate } = useI18n();\n const t = (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n const navigate = useTimeCampusNavigate();\n\n const [limit, setLimit] = useState(PAGE_SIZE);\n const { data, loading, error, refetch } = useListProjectsQuery({\n variables: { limit, offset: 0 },\n notifyOnNetworkStatusChange: true,\n });\n\n const projects = data?.projects?.items ?? [];\n const hasMore = data?.projects?.hasMore ?? false;\n\n // Modal state\n const [createOpen, setCreateOpen] = useState(false);\n const [editTarget, setEditTarget] = useState<ProjectItem | null>(null);\n const [deleteTarget, setDeleteTarget] = useState<ProjectItem | null>(null);\n const [form, setForm] = useState<ProjectFormState>(emptyForm);\n\n const [createProject, { loading: creating, error: createError }] =\n useCreateProjectMutation();\n const [updateProject, { loading: updating, error: updateError }] =\n useUpdateProjectMutation();\n const [archiveProject, { loading: archiving }] = useArchiveProjectMutation();\n const [deleteProject, { loading: deleting, error: deleteError }] =\n useDeleteProjectMutation();\n\n const openCreate = () => {\n setForm(emptyForm);\n setCreateOpen(true);\n };\n\n const openEdit = (project: ProjectItem) => {\n setForm({\n name: project.name,\n clientId: project.clientId ?? \"\",\n description: project.description ?? \"\",\n status: project.status,\n estimatedHours:\n project.estimatedHours != null ? String(project.estimatedHours) : \"\",\n hourlyRate: project.hourlyRate != null ? String(project.hourlyRate) : \"\",\n });\n setEditTarget(project);\n };\n\n const handleCreate = async () => {\n if (!form.name.trim()) return;\n await createProject({\n variables: {\n input: {\n name: form.name.trim(),\n description: form.description.trim() || undefined,\n clientId: form.clientId.trim() || undefined,\n status: form.status,\n estimatedHours: form.estimatedHours\n ? Number(form.estimatedHours)\n : undefined,\n hourlyRate: form.hourlyRate ? Number(form.hourlyRate) : undefined,\n },\n },\n });\n setCreateOpen(false);\n await refetch();\n };\n\n const handleUpdate = async () => {\n if (!editTarget || !form.name.trim()) return;\n await updateProject({\n variables: {\n id: editTarget.id,\n input: {\n name: form.name.trim(),\n description: form.description.trim() || undefined,\n clientId: form.clientId.trim() || undefined,\n status: form.status,\n estimatedHours: form.estimatedHours\n ? Number(form.estimatedHours)\n : undefined,\n hourlyRate: form.hourlyRate ? Number(form.hourlyRate) : undefined,\n },\n },\n });\n setEditTarget(null);\n await refetch();\n };\n\n const handleArchive = async (project: ProjectItem) => {\n await archiveProject({ variables: { id: project.id } });\n await refetch();\n };\n\n const handleDelete = async () => {\n if (!deleteTarget) return;\n await deleteProject({ variables: { id: deleteTarget.id } });\n setDeleteTarget(null);\n await refetch();\n };\n\n const formFields = (\n <div className=\"space-y-4\">\n <div>\n <label className={labelClass}>\n {t(\"timecampus.projects.form.name\", \"Project Name\")}\n </label>\n <input\n type=\"text\"\n placeholder=\"Enter project name\"\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n className={inputClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className={labelClass}>\n {t(\"timecampus.projects.form.client\", \"Client\")}\n </label>\n <input\n type=\"text\"\n placeholder=\"Client name\"\n value={form.clientId}\n onChange={(e) => setForm({ ...form, clientId: e.target.value })}\n className={inputClass}\n />\n </div>\n <div>\n <label className={labelClass}>\n {t(\"timecampus.projects.form.status\", \"Status\")}\n </label>\n <select\n value={form.status}\n onChange={(e) => setForm({ ...form, status: e.target.value })}\n className={inputClass}\n >\n {PROJECT_STATUSES.map((s) => (\n <option key={s} value={s}>\n {s}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div>\n <label className={labelClass}>\n {t(\"timecampus.projects.form.description\", \"Description\")}\n </label>\n <textarea\n placeholder=\"Project description\"\n rows={3}\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n className={inputClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className={labelClass}>\n {t(\"timecampus.projects.form.estimatedHours\", \"Estimated Hours\")}\n </label>\n <input\n type=\"number\"\n placeholder=\"0\"\n value={form.estimatedHours}\n onChange={(e) =>\n setForm({ ...form, estimatedHours: e.target.value })\n }\n className={inputClass}\n />\n </div>\n <div>\n <label className={labelClass}>\n {t(\"timecampus.projects.form.hourlyRate\", \"Hourly Rate\")}\n </label>\n <input\n type=\"number\"\n placeholder=\"0.00\"\n value={form.hourlyRate}\n onChange={(e) => setForm({ ...form, hourlyRate: e.target.value })}\n className={inputClass}\n />\n </div>\n </div>\n </div>\n );\n\n if (error) {\n return (\n <PageLayout\n title={t(\"timecampus.projects.title\", \"Projects\")}\n description={t(\n \"timecampus.projects.description\",\n \"Manage and track project time\",\n )}\n >\n <div className=\"rounded-xl border border-status-error-bg/40 bg-status-error-bg p-6 text-sm text-status-error-text\">\n {t(\n \"timecampus.projects.error.loadFailed\",\n \"Failed to load projects. Please try again later.\",\n )}\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={t(\"timecampus.projects.title\", \"Projects\")}\n description={t(\n \"timecampus.projects.description\",\n \"Manage and track project time\",\n )}\n purpose={t(\n \"timecampus.projects.purpose\",\n \"Group your work into projects so every hour you track rolls up to the right client and budget — use it to estimate effort, watch progress against the plan, and bill accurately.\",\n )}\n actions={\n <button\n onClick={openCreate}\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 {t(\"timecampus.projects.actions.newProject\", \"New Project\")}\n </button>\n }\n >\n {loading && projects.length === 0 ? (\n <LoadingSkeleton variant=\"card\" count={4} />\n ) : projects.length === 0 ? (\n <EmptyState\n illustration=\"empty-generic\"\n title={t(\"timecampus.projects.empty.title\", \"No projects yet\")}\n description={t(\n \"timecampus.projects.empty\",\n \"No projects yet. Create your first project to get started.\",\n )}\n action={\n <button\n onClick={openCreate}\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 {t(\"timecampus.projects.actions.newProject\", \"New Project\")}\n </button>\n }\n />\n ) : (\n <>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {projects.map((project: ProjectItem) => (\n <div\n key={project.id}\n role=\"button\"\n tabIndex={0}\n onClick={() => navigate(`/projects/${project.id}`)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n navigate(`/projects/${project.id}`);\n }\n }}\n className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop p-5 transition-colors hover:bg-bg-sunken/50 cursor-pointer\"\n >\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-center gap-3\">\n <div\n className=\"h-3 w-3 rounded-full\"\n style={{\n backgroundColor:\n project.color ?? \"var(--color-accent-blue)\",\n }}\n />\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">\n {project.name}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {project.clientId ??\n t(\"timecampus.projects.card.noClient\", \"No client\")}\n </p>\n </div>\n </div>\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getStatusColor(project.status)}`}\n >\n {project.status}\n </span>\n </div>\n <div className=\"mt-4\">\n <div className=\"flex items-center justify-between text-xs text-text-secondary\">\n <span>\n {t(\"timecampus.projects.card.progress\", \"Progress\")}\n </span>\n <span>\n {project.estimatedHours\n ? Math.round(\n (project.trackedHours / project.estimatedHours) *\n 100,\n )\n : 0}\n %\n </span>\n </div>\n <div className=\"mt-1 h-2 rounded-full bg-bg-sunken\">\n <div\n className=\"h-2 rounded-full bg-action-primary-bg\"\n style={{\n width: `${project.estimatedHours ? Math.min(Math.round((project.trackedHours / project.estimatedHours) * 100), 100) : 0}%`,\n }}\n />\n </div>\n </div>\n <div className=\"mt-3 flex items-center justify-between text-xs text-text-secondary\">\n <span>\n {formatHours(project.trackedHours)} /{\" \"}\n {formatHours(project.estimatedHours ?? 0)}\n </span>\n <span>\n {project.tasks?.length ?? 0}{\" \"}\n {t(\"timecampus.projects.card.tasks\", \"tasks\")}\n </span>\n </div>\n <div className=\"mt-4 flex items-center justify-end gap-1 border-t border-border-subtle pt-3\">\n <button\n onClick={(e) => {\n e.stopPropagation();\n openEdit(project);\n }}\n className=\"inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium text-text-secondary hover:bg-bg-sunken hover:text-text-primary\"\n aria-label={t(\"timecampus.projects.actions.edit\", \"Edit\")}\n >\n <Pencil className=\"h-3.5 w-3.5\" />\n {t(\"timecampus.projects.actions.edit\", \"Edit\")}\n </button>\n {project.status !== \"archived\" && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n void handleArchive(project);\n }}\n disabled={archiving}\n className=\"inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium text-text-secondary hover:bg-bg-sunken hover:text-text-primary disabled:opacity-50\"\n aria-label={t(\n \"timecampus.projects.actions.archive\",\n \"Archive\",\n )}\n >\n <Archive className=\"h-3.5 w-3.5\" />\n {t(\"timecampus.projects.actions.archive\", \"Archive\")}\n </button>\n )}\n <button\n onClick={(e) => {\n e.stopPropagation();\n setDeleteTarget(project);\n }}\n className=\"inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium text-status-error-text hover:bg-status-error-bg\"\n aria-label={t(\n \"timecampus.projects.actions.delete\",\n \"Delete\",\n )}\n >\n <Trash2 className=\"h-3.5 w-3.5\" />\n {t(\"timecampus.projects.actions.delete\", \"Delete\")}\n </button>\n </div>\n </div>\n ))}\n </div>\n {hasMore && (\n <div className=\"mt-6 flex justify-center\">\n <button\n onClick={() => setLimit((prev) => prev + PAGE_SIZE)}\n disabled={loading}\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 {loading\n ? t(\"timecampus.common.loading\", \"Loading…\")\n : t(\"timecampus.common.loadMore\", \"Load more\")}\n </button>\n </div>\n )}\n </>\n )}\n\n {/* Create modal */}\n <Modal\n open={createOpen}\n onClose={() => setCreateOpen(false)}\n title={t(\"timecampus.projects.actions.newProject\", \"New Project\")}\n description={t(\n \"timecampus.projects.form.createHint\",\n \"Define a new project for time tracking.\",\n )}\n size=\"xl\"\n footer={\n <>\n <button\n onClick={() => setCreateOpen(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 {t(\"timecampus.common.cancel\", \"Cancel\")}\n </button>\n <button\n onClick={handleCreate}\n disabled={creating || !form.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 {creating\n ? t(\"timecampus.common.creating\", \"Creating…\")\n : t(\"timecampus.projects.actions.create\", \"Create Project\")}\n </button>\n </>\n }\n >\n {createError && (\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 {t(\n \"timecampus.projects.error.createFailed\",\n \"Failed to create project. Please try again.\",\n )}\n </div>\n )}\n {formFields}\n </Modal>\n\n {/* Edit modal */}\n <Modal\n open={editTarget !== null}\n onClose={() => setEditTarget(null)}\n title={t(\"timecampus.projects.actions.editProject\", \"Edit Project\")}\n size=\"xl\"\n footer={\n <>\n <button\n onClick={() => setEditTarget(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 {t(\"timecampus.common.cancel\", \"Cancel\")}\n </button>\n <button\n onClick={handleUpdate}\n disabled={updating || !form.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 {updating\n ? t(\"timecampus.common.saving\", \"Saving…\")\n : t(\"timecampus.common.saveChanges\", \"Save Changes\")}\n </button>\n </>\n }\n >\n {updateError && (\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 {t(\n \"timecampus.projects.error.updateFailed\",\n \"Failed to update project. Please try again.\",\n )}\n </div>\n )}\n {formFields}\n </Modal>\n\n {/* Delete confirm modal */}\n <Modal\n open={deleteTarget !== null}\n onClose={() => setDeleteTarget(null)}\n title={t(\"timecampus.projects.actions.deleteProject\", \"Delete Project\")}\n size=\"sm\"\n footer={\n <>\n <button\n onClick={() => setDeleteTarget(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 {t(\"timecampus.common.cancel\", \"Cancel\")}\n </button>\n <button\n onClick={handleDelete}\n disabled={deleting}\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/90 disabled:opacity-50\"\n >\n {deleting\n ? t(\"timecampus.common.deleting\", \"Deleting…\")\n : t(\"timecampus.common.delete\", \"Delete\")}\n </button>\n </>\n }\n >\n {deleteError && (\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 {t(\n \"timecampus.projects.error.deleteFailed\",\n \"Failed to delete project. Please try again.\",\n )}\n </div>\n )}\n <p className=\"text-sm text-text-secondary\">\n {t(\n \"timecampus.projects.deleteConfirm\",\n \"Are you sure you want to delete\",\n )}{\" \"}\n <span className=\"font-semibold text-text-primary\">\n {deleteTarget?.name}\n </span>\n ? {t(\"timecampus.common.cannotUndo\", \"This action cannot be undone.\")}\n </p>\n </Modal>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProjectsPage.js","names":[],"sources":["../../../../src/timecampus/pages/projects/ProjectsPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { Plus, Pencil, Trash2, Archive } from \"lucide-react\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { EmptyState } from \"../../components/EmptyState\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { Modal } from \"../../components/Modal\";\nimport { useTimeCampusNavigate } from \"../../providers/TimeCampusProvider\";\nimport { getStatusColor, formatHours } from \"../../utils/helpers\";\nimport {\n useListProjectsQuery,\n useCreateProjectMutation,\n useUpdateProjectMutation,\n useArchiveProjectMutation,\n useDeleteProjectMutation,\n type ListProjectsQuery,\n} from \"@/generated/wspace-operations\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\n\ntype ProjectItem = NonNullable<ListProjectsQuery[\"projects\"]>[\"items\"][number];\n\nconst PAGE_SIZE = 25;\n\nconst PROJECT_STATUSES = [\n \"active\",\n \"on-hold\",\n \"completed\",\n \"archived\",\n] as const;\n\ninterface ProjectFormState {\n name: string;\n clientId: string;\n description: string;\n status: string;\n estimatedHours: string;\n hourlyRate: string;\n}\n\nconst emptyForm: ProjectFormState = {\n name: \"\",\n clientId: \"\",\n description: \"\",\n status: \"active\",\n estimatedHours: \"\",\n hourlyRate: \"\",\n};\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\";\nconst labelClass = \"block text-sm font-medium text-text-primary\";\n\nexport function ProjectsPage() {\n const { t: translate } = useI18n();\n const t = (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n const navigate = useTimeCampusNavigate();\n\n const [limit, setLimit] = useState(PAGE_SIZE);\n const { data, loading, error, refetch } = useListProjectsQuery({\n variables: { pagination: { limit, offset: 0 } },\n notifyOnNetworkStatusChange: true,\n });\n\n const projects = data?.projects?.items ?? [];\n const hasMore = data?.projects?.hasMore ?? false;\n\n // Modal state\n const [createOpen, setCreateOpen] = useState(false);\n const [editTarget, setEditTarget] = useState<ProjectItem | null>(null);\n const [deleteTarget, setDeleteTarget] = useState<ProjectItem | null>(null);\n const [form, setForm] = useState<ProjectFormState>(emptyForm);\n\n const [createProject, { loading: creating, error: createError }] =\n useCreateProjectMutation();\n const [updateProject, { loading: updating, error: updateError }] =\n useUpdateProjectMutation();\n const [archiveProject, { loading: archiving }] = useArchiveProjectMutation();\n const [deleteProject, { loading: deleting, error: deleteError }] =\n useDeleteProjectMutation();\n\n const openCreate = () => {\n setForm(emptyForm);\n setCreateOpen(true);\n };\n\n const openEdit = (project: ProjectItem) => {\n setForm({\n name: project.name,\n clientId: project.clientId ?? \"\",\n description: project.description ?? \"\",\n status: project.status,\n estimatedHours:\n project.estimatedHours != null ? String(project.estimatedHours) : \"\",\n hourlyRate: project.hourlyRate != null ? String(project.hourlyRate) : \"\",\n });\n setEditTarget(project);\n };\n\n const handleCreate = async () => {\n if (!form.name.trim()) return;\n await createProject({\n variables: {\n input: {\n name: form.name.trim(),\n description: form.description.trim() || undefined,\n clientId: form.clientId.trim() || undefined,\n status: form.status,\n estimatedHours: form.estimatedHours\n ? Number(form.estimatedHours)\n : undefined,\n hourlyRate: form.hourlyRate ? Number(form.hourlyRate) : undefined,\n },\n },\n });\n setCreateOpen(false);\n await refetch();\n };\n\n const handleUpdate = async () => {\n if (!editTarget || !form.name.trim()) return;\n await updateProject({\n variables: {\n id: editTarget.id,\n input: {\n name: form.name.trim(),\n description: form.description.trim() || undefined,\n clientId: form.clientId.trim() || undefined,\n status: form.status,\n estimatedHours: form.estimatedHours\n ? Number(form.estimatedHours)\n : undefined,\n hourlyRate: form.hourlyRate ? Number(form.hourlyRate) : undefined,\n },\n },\n });\n setEditTarget(null);\n await refetch();\n };\n\n const handleArchive = async (project: ProjectItem) => {\n await archiveProject({ variables: { id: project.id } });\n await refetch();\n };\n\n const handleDelete = async () => {\n if (!deleteTarget) return;\n await deleteProject({ variables: { id: deleteTarget.id } });\n setDeleteTarget(null);\n await refetch();\n };\n\n const formFields = (\n <div className=\"space-y-4\">\n <div>\n <label className={labelClass}>\n {t(\"timecampus.projects.form.name\", \"Project Name\")}\n </label>\n <input\n type=\"text\"\n placeholder=\"Enter project name\"\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n className={inputClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className={labelClass}>\n {t(\"timecampus.projects.form.client\", \"Client\")}\n </label>\n <input\n type=\"text\"\n placeholder=\"Client name\"\n value={form.clientId}\n onChange={(e) => setForm({ ...form, clientId: e.target.value })}\n className={inputClass}\n />\n </div>\n <div>\n <label className={labelClass}>\n {t(\"timecampus.projects.form.status\", \"Status\")}\n </label>\n <select\n value={form.status}\n onChange={(e) => setForm({ ...form, status: e.target.value })}\n className={inputClass}\n >\n {PROJECT_STATUSES.map((s) => (\n <option key={s} value={s}>\n {s}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div>\n <label className={labelClass}>\n {t(\"timecampus.projects.form.description\", \"Description\")}\n </label>\n <textarea\n placeholder=\"Project description\"\n rows={3}\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n className={inputClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className={labelClass}>\n {t(\"timecampus.projects.form.estimatedHours\", \"Estimated Hours\")}\n </label>\n <input\n type=\"number\"\n placeholder=\"0\"\n value={form.estimatedHours}\n onChange={(e) =>\n setForm({ ...form, estimatedHours: e.target.value })\n }\n className={inputClass}\n />\n </div>\n <div>\n <label className={labelClass}>\n {t(\"timecampus.projects.form.hourlyRate\", \"Hourly Rate\")}\n </label>\n <input\n type=\"number\"\n placeholder=\"0.00\"\n value={form.hourlyRate}\n onChange={(e) => setForm({ ...form, hourlyRate: e.target.value })}\n className={inputClass}\n />\n </div>\n </div>\n </div>\n );\n\n if (error) {\n return (\n <PageLayout\n title={t(\"timecampus.projects.title\", \"Projects\")}\n description={t(\n \"timecampus.projects.description\",\n \"Manage and track project time\",\n )}\n >\n <div className=\"rounded-xl border border-status-error-bg/40 bg-status-error-bg p-6 text-sm text-status-error-text\">\n {t(\n \"timecampus.projects.error.loadFailed\",\n \"Failed to load projects. Please try again later.\",\n )}\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={t(\"timecampus.projects.title\", \"Projects\")}\n description={t(\n \"timecampus.projects.description\",\n \"Manage and track project time\",\n )}\n purpose={t(\n \"timecampus.projects.purpose\",\n \"Group your work into projects so every hour you track rolls up to the right client and budget — use it to estimate effort, watch progress against the plan, and bill accurately.\",\n )}\n actions={\n <button\n onClick={openCreate}\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 {t(\"timecampus.projects.actions.newProject\", \"New Project\")}\n </button>\n }\n >\n {loading && projects.length === 0 ? (\n <LoadingSkeleton variant=\"card\" count={4} />\n ) : projects.length === 0 ? (\n <EmptyState\n illustration=\"empty-generic\"\n title={t(\"timecampus.projects.empty.title\", \"No projects yet\")}\n description={t(\n \"timecampus.projects.empty\",\n \"No projects yet. Create your first project to get started.\",\n )}\n action={\n <button\n onClick={openCreate}\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 {t(\"timecampus.projects.actions.newProject\", \"New Project\")}\n </button>\n }\n />\n ) : (\n <>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {projects.map((project: ProjectItem) => (\n <div\n key={project.id}\n role=\"button\"\n tabIndex={0}\n onClick={() => navigate(`/projects/${project.id}`)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n navigate(`/projects/${project.id}`);\n }\n }}\n className=\"rounded-xl border border-border-seam bg-gloss-card shadow-pop p-5 transition-colors hover:bg-bg-sunken/50 cursor-pointer\"\n >\n <div className=\"flex items-start justify-between\">\n <div className=\"flex items-center gap-3\">\n <div\n className=\"h-3 w-3 rounded-full\"\n style={{\n backgroundColor:\n project.color ?? \"var(--color-accent-blue)\",\n }}\n />\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">\n {project.name}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {project.clientId ??\n t(\"timecampus.projects.card.noClient\", \"No client\")}\n </p>\n </div>\n </div>\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getStatusColor(project.status)}`}\n >\n {project.status}\n </span>\n </div>\n <div className=\"mt-4\">\n <div className=\"flex items-center justify-between text-xs text-text-secondary\">\n <span>\n {t(\"timecampus.projects.card.progress\", \"Progress\")}\n </span>\n <span>\n {project.estimatedHours\n ? Math.round(\n (project.trackedHours / project.estimatedHours) *\n 100,\n )\n : 0}\n %\n </span>\n </div>\n <div className=\"mt-1 h-2 rounded-full bg-bg-sunken\">\n <div\n className=\"h-2 rounded-full bg-action-primary-bg\"\n style={{\n width: `${project.estimatedHours ? Math.min(Math.round((project.trackedHours / project.estimatedHours) * 100), 100) : 0}%`,\n }}\n />\n </div>\n </div>\n <div className=\"mt-3 flex items-center justify-between text-xs text-text-secondary\">\n <span>\n {formatHours(project.trackedHours)} /{\" \"}\n {formatHours(project.estimatedHours ?? 0)}\n </span>\n <span>\n {project.tasks?.length ?? 0}{\" \"}\n {t(\"timecampus.projects.card.tasks\", \"tasks\")}\n </span>\n </div>\n <div className=\"mt-4 flex items-center justify-end gap-1 border-t border-border-subtle pt-3\">\n <button\n onClick={(e) => {\n e.stopPropagation();\n openEdit(project);\n }}\n className=\"inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium text-text-secondary hover:bg-bg-sunken hover:text-text-primary\"\n aria-label={t(\"timecampus.projects.actions.edit\", \"Edit\")}\n >\n <Pencil className=\"h-3.5 w-3.5\" />\n {t(\"timecampus.projects.actions.edit\", \"Edit\")}\n </button>\n {project.status !== \"archived\" && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n void handleArchive(project);\n }}\n disabled={archiving}\n className=\"inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium text-text-secondary hover:bg-bg-sunken hover:text-text-primary disabled:opacity-50\"\n aria-label={t(\n \"timecampus.projects.actions.archive\",\n \"Archive\",\n )}\n >\n <Archive className=\"h-3.5 w-3.5\" />\n {t(\"timecampus.projects.actions.archive\", \"Archive\")}\n </button>\n )}\n <button\n onClick={(e) => {\n e.stopPropagation();\n setDeleteTarget(project);\n }}\n className=\"inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium text-status-error-text hover:bg-status-error-bg\"\n aria-label={t(\n \"timecampus.projects.actions.delete\",\n \"Delete\",\n )}\n >\n <Trash2 className=\"h-3.5 w-3.5\" />\n {t(\"timecampus.projects.actions.delete\", \"Delete\")}\n </button>\n </div>\n </div>\n ))}\n </div>\n {hasMore && (\n <div className=\"mt-6 flex justify-center\">\n <button\n onClick={() => setLimit((prev) => prev + PAGE_SIZE)}\n disabled={loading}\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 {loading\n ? t(\"timecampus.common.loading\", \"Loading…\")\n : t(\"timecampus.common.loadMore\", \"Load more\")}\n </button>\n </div>\n )}\n </>\n )}\n\n {/* Create modal */}\n <Modal\n open={createOpen}\n onClose={() => setCreateOpen(false)}\n title={t(\"timecampus.projects.actions.newProject\", \"New Project\")}\n description={t(\n \"timecampus.projects.form.createHint\",\n \"Define a new project for time tracking.\",\n )}\n size=\"xl\"\n footer={\n <>\n <button\n onClick={() => setCreateOpen(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 {t(\"timecampus.common.cancel\", \"Cancel\")}\n </button>\n <button\n onClick={handleCreate}\n disabled={creating || !form.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 {creating\n ? t(\"timecampus.common.creating\", \"Creating…\")\n : t(\"timecampus.projects.actions.create\", \"Create Project\")}\n </button>\n </>\n }\n >\n {createError && (\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 {t(\n \"timecampus.projects.error.createFailed\",\n \"Failed to create project. Please try again.\",\n )}\n </div>\n )}\n {formFields}\n </Modal>\n\n {/* Edit modal */}\n <Modal\n open={editTarget !== null}\n onClose={() => setEditTarget(null)}\n title={t(\"timecampus.projects.actions.editProject\", \"Edit Project\")}\n size=\"xl\"\n footer={\n <>\n <button\n onClick={() => setEditTarget(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 {t(\"timecampus.common.cancel\", \"Cancel\")}\n </button>\n <button\n onClick={handleUpdate}\n disabled={updating || !form.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 {updating\n ? t(\"timecampus.common.saving\", \"Saving…\")\n : t(\"timecampus.common.saveChanges\", \"Save Changes\")}\n </button>\n </>\n }\n >\n {updateError && (\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 {t(\n \"timecampus.projects.error.updateFailed\",\n \"Failed to update project. Please try again.\",\n )}\n </div>\n )}\n {formFields}\n </Modal>\n\n {/* Delete confirm modal */}\n <Modal\n open={deleteTarget !== null}\n onClose={() => setDeleteTarget(null)}\n title={t(\"timecampus.projects.actions.deleteProject\", \"Delete Project\")}\n size=\"sm\"\n footer={\n <>\n <button\n onClick={() => setDeleteTarget(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 {t(\"timecampus.common.cancel\", \"Cancel\")}\n </button>\n <button\n onClick={handleDelete}\n disabled={deleting}\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/90 disabled:opacity-50\"\n >\n {deleting\n ? t(\"timecampus.common.deleting\", \"Deleting…\")\n : t(\"timecampus.common.delete\", \"Delete\")}\n </button>\n </>\n }\n >\n {deleteError && (\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 {t(\n \"timecampus.projects.error.deleteFailed\",\n \"Failed to delete project. Please try again.\",\n )}\n </div>\n )}\n <p className=\"text-sm text-text-secondary\">\n {t(\n \"timecampus.projects.deleteConfirm\",\n \"Are you sure you want to delete\",\n )}{\" \"}\n <span className=\"font-semibold text-text-primary\">\n {deleteTarget?.name}\n </span>\n ? {t(\"timecampus.common.cannotUndo\", \"This action cannot be undone.\")}\n </p>\n </Modal>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -33,8 +33,10 @@ function E() {
33
33
  let { projectId: E } = g(), [D, re] = d(x), { data: O, loading: k, error: ie, refetch: A } = i({
34
34
  variables: {
35
35
  projectId: E,
36
- limit: D,
37
- offset: 0
36
+ pagination: {
37
+ limit: D,
38
+ offset: 0
39
+ }
38
40
  },
39
41
  skip: !E,
40
42
  notifyOnNetworkStatusChange: !0
@@ -1 +1 @@
1
- {"version":3,"file":"TasksPage.js","names":[],"sources":["../../../../src/timecampus/pages/projects/TasksPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport {\n Plus,\n CheckSquare,\n Pencil,\n Trash2,\n ArrowUp,\n ArrowDown,\n} 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 { getPriorityColor } from \"../../utils/helpers\";\nimport {\n useListTasksQuery,\n useCreateTaskMutation,\n useUpdateTaskMutation,\n useUpdateTaskStatusMutation,\n useDeleteTaskMutation,\n useReorderTasksMutation,\n type TaskFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nconst PAGE_SIZE = 25;\n\nconst TASK_STATUSES = [\"todo\", \"in-progress\", \"review\", \"done\"] as const;\nconst TASK_PRIORITIES = [\"low\", \"medium\", \"high\", \"urgent\"] as const;\n\ninterface TaskFormState {\n title: string;\n description: string;\n status: string;\n priority: string;\n assigneeId: string;\n estimatedHours: string;\n dueDate: string;\n}\n\nconst emptyForm: TaskFormState = {\n title: \"\",\n description: \"\",\n status: \"todo\",\n priority: \"medium\",\n assigneeId: \"\",\n estimatedHours: \"\",\n dueDate: \"\",\n};\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\";\nconst labelClass = \"block text-sm font-medium text-text-primary\";\n\nexport function TasksPage() {\n const { projectId } = useParams<{ projectId: string }>();\n\n const [limit, setLimit] = useState(PAGE_SIZE);\n const { data, loading, error, refetch } = useListTasksQuery({\n variables: { projectId: projectId!, limit, offset: 0 },\n skip: !projectId,\n notifyOnNetworkStatusChange: true,\n });\n\n const tasks = data?.tasks?.items ?? [];\n const hasMore = data?.tasks?.hasMore ?? false;\n\n const [createOpen, setCreateOpen] = useState(false);\n const [editTarget, setEditTarget] = useState<TaskFieldsFragment | null>(null);\n const [deleteTarget, setDeleteTarget] = useState<TaskFieldsFragment | null>(\n null,\n );\n const [form, setForm] = useState<TaskFormState>(emptyForm);\n\n const [createTask, { loading: creating, error: createError }] =\n useCreateTaskMutation();\n const [updateTask, { loading: updating, error: updateError }] =\n useUpdateTaskMutation();\n const [updateTaskStatus] = useUpdateTaskStatusMutation();\n const [deleteTask, { loading: deleting, error: deleteError }] =\n useDeleteTaskMutation();\n const [reorderTasks, { loading: reordering }] = useReorderTasksMutation();\n\n const openCreate = () => {\n setForm(emptyForm);\n setCreateOpen(true);\n };\n\n const openEdit = (task: TaskFieldsFragment) => {\n setForm({\n title: task.title,\n description: task.description ?? \"\",\n status: task.status,\n priority: task.priority,\n assigneeId: task.assigneeId ?? \"\",\n estimatedHours:\n task.estimatedHours != null ? String(task.estimatedHours) : \"\",\n dueDate: task.dueDate ? task.dueDate.slice(0, 10) : \"\",\n });\n setEditTarget(task);\n };\n\n const handleCreate = async () => {\n if (!projectId || !form.title.trim()) return;\n await createTask({\n variables: {\n input: {\n projectId,\n title: form.title.trim(),\n description: form.description.trim() || undefined,\n status: form.status,\n priority: form.priority,\n assigneeId: form.assigneeId.trim() || undefined,\n estimatedHours: form.estimatedHours\n ? Number(form.estimatedHours)\n : undefined,\n dueDate: form.dueDate\n ? new Date(form.dueDate).toISOString()\n : undefined,\n },\n },\n });\n setCreateOpen(false);\n await refetch();\n };\n\n const handleUpdate = async () => {\n if (!editTarget || !form.title.trim()) return;\n await updateTask({\n variables: {\n id: editTarget.id,\n input: {\n title: form.title.trim(),\n description: form.description.trim() || undefined,\n status: form.status,\n priority: form.priority,\n assigneeId: form.assigneeId.trim() || undefined,\n estimatedHours: form.estimatedHours\n ? Number(form.estimatedHours)\n : undefined,\n dueDate: form.dueDate\n ? new Date(form.dueDate).toISOString()\n : undefined,\n },\n },\n });\n setEditTarget(null);\n await refetch();\n };\n\n const handleStatusChange = async (\n task: TaskFieldsFragment,\n status: string,\n ) => {\n await updateTaskStatus({ variables: { id: task.id, status } });\n await refetch();\n };\n\n const handleDelete = async () => {\n if (!deleteTarget) return;\n await deleteTask({ variables: { id: deleteTarget.id } });\n setDeleteTarget(null);\n await refetch();\n };\n\n const handleMove = async (index: number, direction: -1 | 1) => {\n if (!projectId) return;\n const target = index + direction;\n if (target < 0 || target >= tasks.length) return;\n const ordered = tasks.map((task: TaskFieldsFragment) => task.id);\n const [moved] = ordered.splice(index, 1);\n ordered.splice(target, 0, moved);\n await reorderTasks({ variables: { projectId, taskIds: ordered } });\n await refetch();\n };\n\n const formFields = (\n <div className=\"space-y-4\">\n <div>\n <label className={labelClass}>Title</label>\n <input\n type=\"text\"\n placeholder=\"Task title\"\n value={form.title}\n onChange={(e) => setForm({ ...form, title: e.target.value })}\n className={inputClass}\n />\n </div>\n <div>\n <label className={labelClass}>Description</label>\n <textarea\n placeholder=\"Task description\"\n rows={3}\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n className={inputClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className={labelClass}>Status</label>\n <select\n value={form.status}\n onChange={(e) => setForm({ ...form, status: e.target.value })}\n className={inputClass}\n >\n {TASK_STATUSES.map((s) => (\n <option key={s} value={s}>\n {s}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className={labelClass}>Priority</label>\n <select\n value={form.priority}\n onChange={(e) => setForm({ ...form, priority: e.target.value })}\n className={inputClass}\n >\n {TASK_PRIORITIES.map((p) => (\n <option key={p} value={p}>\n {p}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div>\n <label className={labelClass}>Assignee ID</label>\n <input\n type=\"text\"\n placeholder=\"User ID\"\n value={form.assigneeId}\n onChange={(e) => setForm({ ...form, assigneeId: e.target.value })}\n className={inputClass}\n />\n </div>\n <div>\n <label className={labelClass}>Estimated Hours</label>\n <input\n type=\"number\"\n placeholder=\"0\"\n value={form.estimatedHours}\n onChange={(e) =>\n setForm({ ...form, estimatedHours: e.target.value })\n }\n className={inputClass}\n />\n </div>\n <div>\n <label className={labelClass}>Due Date</label>\n <input\n type=\"date\"\n value={form.dueDate}\n onChange={(e) => setForm({ ...form, dueDate: e.target.value })}\n className={inputClass}\n />\n </div>\n </div>\n </div>\n );\n\n if (error) {\n return (\n <PageLayout title=\"Tasks\" description={`Tasks for project ${projectId}`}>\n <div className=\"rounded-xl border border-status-error-bg/40 bg-status-error-bg p-6 text-sm text-status-error-text\">\n Failed to load tasks. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Tasks\"\n description={`Tasks for project ${projectId}`}\n purpose=\"Break a project down into the concrete pieces of work that need doing, then track hours against each one — so you can see what's done, what's blocking, and where effort is actually being spent.\"\n actions={\n <button\n onClick={openCreate}\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 Add Task\n </button>\n }\n >\n {loading && tasks.length === 0 ? (\n <LoadingSkeleton variant=\"list\" count={5} />\n ) : tasks.length === 0 ? (\n <EmptyState\n illustration=\"empty-generic\"\n title=\"No tasks yet\"\n description=\"Add a task to break this project into trackable work.\"\n action={\n <button\n onClick={openCreate}\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 Add Task\n </button>\n }\n />\n ) : (\n <>\n <GlassCard treatment=\"glass\" glow className=\"overflow-hidden\">\n <div className=\"divide-y divide-border\">\n {tasks.map((task: TaskFieldsFragment, index: number) => (\n <div\n key={task.id}\n className=\"flex flex-col gap-3 p-4 transition-colors hover:bg-bg-sunken/50 sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"flex items-center gap-3\">\n <CheckSquare\n className={`h-5 w-5 shrink-0 ${task.status === \"done\" ? \"text-status-success-text\" : \"text-text-secondary\"}`}\n />\n <div>\n <p\n className={`text-sm font-medium ${task.status === \"done\" ? \"text-text-secondary line-through\" : \"text-text-primary\"}`}\n >\n {task.title}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {task.assigneeId ?? \"Unassigned\"} &middot;{\" \"}\n {task.trackedHours}h tracked\n </p>\n </div>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getPriorityColor(task.priority)}`}\n >\n {task.priority}\n </span>\n <select\n value={task.status}\n onChange={(e) =>\n void handleStatusChange(task, e.target.value)\n }\n className=\"rounded-md border border-border-subtle bg-bg-surface px-2 py-1 text-xs font-medium text-text-primary\"\n aria-label=\"Task status\"\n >\n {TASK_STATUSES.map((s) => (\n <option key={s} value={s}>\n {s}\n </option>\n ))}\n </select>\n <button\n onClick={() => void handleMove(index, -1)}\n disabled={index === 0 || reordering}\n className=\"rounded-md p-1 text-text-secondary hover:bg-bg-sunken hover:text-text-primary disabled:opacity-40\"\n aria-label=\"Move up\"\n >\n <ArrowUp className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => void handleMove(index, 1)}\n disabled={index === tasks.length - 1 || reordering}\n className=\"rounded-md p-1 text-text-secondary hover:bg-bg-sunken hover:text-text-primary disabled:opacity-40\"\n aria-label=\"Move down\"\n >\n <ArrowDown className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => openEdit(task)}\n className=\"rounded-md p-1 text-text-secondary hover:bg-bg-sunken hover:text-text-primary\"\n aria-label=\"Edit task\"\n >\n <Pencil className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => setDeleteTarget(task)}\n className=\"rounded-md p-1 text-status-error-text hover:bg-status-error-bg\"\n aria-label=\"Delete task\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n ))}\n </div>\n </GlassCard>\n {hasMore && (\n <div className=\"mt-6 flex justify-center\">\n <button\n onClick={() => setLimit((prev) => prev + PAGE_SIZE)}\n disabled={loading}\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 {loading ? \"Loading…\" : \"Load more\"}\n </button>\n </div>\n )}\n </>\n )}\n\n {/* Create modal */}\n <Modal\n open={createOpen}\n onClose={() => setCreateOpen(false)}\n title=\"Add Task\"\n description=\"Break this project into a trackable piece of work.\"\n size=\"xl\"\n footer={\n <>\n <button\n onClick={() => setCreateOpen(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={handleCreate}\n disabled={creating || !form.title.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 {creating ? \"Creating…\" : \"Create Task\"}\n </button>\n </>\n }\n >\n {createError && (\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 create task. Please try again.\n </div>\n )}\n {formFields}\n </Modal>\n\n {/* Edit modal */}\n <Modal\n open={editTarget !== null}\n onClose={() => setEditTarget(null)}\n title=\"Edit Task\"\n size=\"xl\"\n footer={\n <>\n <button\n onClick={() => setEditTarget(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={handleUpdate}\n disabled={updating || !form.title.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 {updating ? \"Saving…\" : \"Save Changes\"}\n </button>\n </>\n }\n >\n {updateError && (\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 task. Please try again.\n </div>\n )}\n {formFields}\n </Modal>\n\n {/* Delete confirm modal */}\n <Modal\n open={deleteTarget !== null}\n onClose={() => setDeleteTarget(null)}\n title=\"Delete Task\"\n size=\"sm\"\n footer={\n <>\n <button\n onClick={() => setDeleteTarget(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={handleDelete}\n disabled={deleting}\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/90 disabled:opacity-50\"\n >\n {deleting ? \"Deleting…\" : \"Delete\"}\n </button>\n </>\n }\n >\n {deleteError && (\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 delete task. Please try again.\n </div>\n )}\n <p className=\"text-sm text-text-secondary\">\n Are you sure you want to delete{\" \"}\n <span className=\"font-semibold text-text-primary\">\n {deleteTarget?.title}\n </span>\n ? This action cannot be undone.\n </p>\n </Modal>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"TasksPage.js","names":[],"sources":["../../../../src/timecampus/pages/projects/TasksPage.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport {\n Plus,\n CheckSquare,\n Pencil,\n Trash2,\n ArrowUp,\n ArrowDown,\n} 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 { getPriorityColor } from \"../../utils/helpers\";\nimport {\n useListTasksQuery,\n useCreateTaskMutation,\n useUpdateTaskMutation,\n useUpdateTaskStatusMutation,\n useDeleteTaskMutation,\n useReorderTasksMutation,\n type TaskFieldsFragment,\n} from \"@/generated/wspace-operations\";\n\nconst PAGE_SIZE = 25;\n\nconst TASK_STATUSES = [\"todo\", \"in-progress\", \"review\", \"done\"] as const;\nconst TASK_PRIORITIES = [\"low\", \"medium\", \"high\", \"urgent\"] as const;\n\ninterface TaskFormState {\n title: string;\n description: string;\n status: string;\n priority: string;\n assigneeId: string;\n estimatedHours: string;\n dueDate: string;\n}\n\nconst emptyForm: TaskFormState = {\n title: \"\",\n description: \"\",\n status: \"todo\",\n priority: \"medium\",\n assigneeId: \"\",\n estimatedHours: \"\",\n dueDate: \"\",\n};\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\";\nconst labelClass = \"block text-sm font-medium text-text-primary\";\n\nexport function TasksPage() {\n const { projectId } = useParams<{ projectId: string }>();\n\n const [limit, setLimit] = useState(PAGE_SIZE);\n const { data, loading, error, refetch } = useListTasksQuery({\n variables: { projectId: projectId!, pagination: { limit, offset: 0 } },\n skip: !projectId,\n notifyOnNetworkStatusChange: true,\n });\n\n const tasks = data?.tasks?.items ?? [];\n const hasMore = data?.tasks?.hasMore ?? false;\n\n const [createOpen, setCreateOpen] = useState(false);\n const [editTarget, setEditTarget] = useState<TaskFieldsFragment | null>(null);\n const [deleteTarget, setDeleteTarget] = useState<TaskFieldsFragment | null>(\n null,\n );\n const [form, setForm] = useState<TaskFormState>(emptyForm);\n\n const [createTask, { loading: creating, error: createError }] =\n useCreateTaskMutation();\n const [updateTask, { loading: updating, error: updateError }] =\n useUpdateTaskMutation();\n const [updateTaskStatus] = useUpdateTaskStatusMutation();\n const [deleteTask, { loading: deleting, error: deleteError }] =\n useDeleteTaskMutation();\n const [reorderTasks, { loading: reordering }] = useReorderTasksMutation();\n\n const openCreate = () => {\n setForm(emptyForm);\n setCreateOpen(true);\n };\n\n const openEdit = (task: TaskFieldsFragment) => {\n setForm({\n title: task.title,\n description: task.description ?? \"\",\n status: task.status,\n priority: task.priority,\n assigneeId: task.assigneeId ?? \"\",\n estimatedHours:\n task.estimatedHours != null ? String(task.estimatedHours) : \"\",\n dueDate: task.dueDate ? task.dueDate.slice(0, 10) : \"\",\n });\n setEditTarget(task);\n };\n\n const handleCreate = async () => {\n if (!projectId || !form.title.trim()) return;\n await createTask({\n variables: {\n input: {\n projectId,\n title: form.title.trim(),\n description: form.description.trim() || undefined,\n status: form.status,\n priority: form.priority,\n assigneeId: form.assigneeId.trim() || undefined,\n estimatedHours: form.estimatedHours\n ? Number(form.estimatedHours)\n : undefined,\n dueDate: form.dueDate\n ? new Date(form.dueDate).toISOString()\n : undefined,\n },\n },\n });\n setCreateOpen(false);\n await refetch();\n };\n\n const handleUpdate = async () => {\n if (!editTarget || !form.title.trim()) return;\n await updateTask({\n variables: {\n id: editTarget.id,\n input: {\n title: form.title.trim(),\n description: form.description.trim() || undefined,\n status: form.status,\n priority: form.priority,\n assigneeId: form.assigneeId.trim() || undefined,\n estimatedHours: form.estimatedHours\n ? Number(form.estimatedHours)\n : undefined,\n dueDate: form.dueDate\n ? new Date(form.dueDate).toISOString()\n : undefined,\n },\n },\n });\n setEditTarget(null);\n await refetch();\n };\n\n const handleStatusChange = async (\n task: TaskFieldsFragment,\n status: string,\n ) => {\n await updateTaskStatus({ variables: { id: task.id, status } });\n await refetch();\n };\n\n const handleDelete = async () => {\n if (!deleteTarget) return;\n await deleteTask({ variables: { id: deleteTarget.id } });\n setDeleteTarget(null);\n await refetch();\n };\n\n const handleMove = async (index: number, direction: -1 | 1) => {\n if (!projectId) return;\n const target = index + direction;\n if (target < 0 || target >= tasks.length) return;\n const ordered = tasks.map((task: TaskFieldsFragment) => task.id);\n const [moved] = ordered.splice(index, 1);\n ordered.splice(target, 0, moved);\n await reorderTasks({ variables: { projectId, taskIds: ordered } });\n await refetch();\n };\n\n const formFields = (\n <div className=\"space-y-4\">\n <div>\n <label className={labelClass}>Title</label>\n <input\n type=\"text\"\n placeholder=\"Task title\"\n value={form.title}\n onChange={(e) => setForm({ ...form, title: e.target.value })}\n className={inputClass}\n />\n </div>\n <div>\n <label className={labelClass}>Description</label>\n <textarea\n placeholder=\"Task description\"\n rows={3}\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n className={inputClass}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className={labelClass}>Status</label>\n <select\n value={form.status}\n onChange={(e) => setForm({ ...form, status: e.target.value })}\n className={inputClass}\n >\n {TASK_STATUSES.map((s) => (\n <option key={s} value={s}>\n {s}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className={labelClass}>Priority</label>\n <select\n value={form.priority}\n onChange={(e) => setForm({ ...form, priority: e.target.value })}\n className={inputClass}\n >\n {TASK_PRIORITIES.map((p) => (\n <option key={p} value={p}>\n {p}\n </option>\n ))}\n </select>\n </div>\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <div>\n <label className={labelClass}>Assignee ID</label>\n <input\n type=\"text\"\n placeholder=\"User ID\"\n value={form.assigneeId}\n onChange={(e) => setForm({ ...form, assigneeId: e.target.value })}\n className={inputClass}\n />\n </div>\n <div>\n <label className={labelClass}>Estimated Hours</label>\n <input\n type=\"number\"\n placeholder=\"0\"\n value={form.estimatedHours}\n onChange={(e) =>\n setForm({ ...form, estimatedHours: e.target.value })\n }\n className={inputClass}\n />\n </div>\n <div>\n <label className={labelClass}>Due Date</label>\n <input\n type=\"date\"\n value={form.dueDate}\n onChange={(e) => setForm({ ...form, dueDate: e.target.value })}\n className={inputClass}\n />\n </div>\n </div>\n </div>\n );\n\n if (error) {\n return (\n <PageLayout title=\"Tasks\" description={`Tasks for project ${projectId}`}>\n <div className=\"rounded-xl border border-status-error-bg/40 bg-status-error-bg p-6 text-sm text-status-error-text\">\n Failed to load tasks. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Tasks\"\n description={`Tasks for project ${projectId}`}\n purpose=\"Break a project down into the concrete pieces of work that need doing, then track hours against each one — so you can see what's done, what's blocking, and where effort is actually being spent.\"\n actions={\n <button\n onClick={openCreate}\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 Add Task\n </button>\n }\n >\n {loading && tasks.length === 0 ? (\n <LoadingSkeleton variant=\"list\" count={5} />\n ) : tasks.length === 0 ? (\n <EmptyState\n illustration=\"empty-generic\"\n title=\"No tasks yet\"\n description=\"Add a task to break this project into trackable work.\"\n action={\n <button\n onClick={openCreate}\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 Add Task\n </button>\n }\n />\n ) : (\n <>\n <GlassCard treatment=\"glass\" glow className=\"overflow-hidden\">\n <div className=\"divide-y divide-border\">\n {tasks.map((task: TaskFieldsFragment, index: number) => (\n <div\n key={task.id}\n className=\"flex flex-col gap-3 p-4 transition-colors hover:bg-bg-sunken/50 sm:flex-row sm:items-center sm:justify-between\"\n >\n <div className=\"flex items-center gap-3\">\n <CheckSquare\n className={`h-5 w-5 shrink-0 ${task.status === \"done\" ? \"text-status-success-text\" : \"text-text-secondary\"}`}\n />\n <div>\n <p\n className={`text-sm font-medium ${task.status === \"done\" ? \"text-text-secondary line-through\" : \"text-text-primary\"}`}\n >\n {task.title}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {task.assigneeId ?? \"Unassigned\"} &middot;{\" \"}\n {task.trackedHours}h tracked\n </p>\n </div>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <span\n className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${getPriorityColor(task.priority)}`}\n >\n {task.priority}\n </span>\n <select\n value={task.status}\n onChange={(e) =>\n void handleStatusChange(task, e.target.value)\n }\n className=\"rounded-md border border-border-subtle bg-bg-surface px-2 py-1 text-xs font-medium text-text-primary\"\n aria-label=\"Task status\"\n >\n {TASK_STATUSES.map((s) => (\n <option key={s} value={s}>\n {s}\n </option>\n ))}\n </select>\n <button\n onClick={() => void handleMove(index, -1)}\n disabled={index === 0 || reordering}\n className=\"rounded-md p-1 text-text-secondary hover:bg-bg-sunken hover:text-text-primary disabled:opacity-40\"\n aria-label=\"Move up\"\n >\n <ArrowUp className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => void handleMove(index, 1)}\n disabled={index === tasks.length - 1 || reordering}\n className=\"rounded-md p-1 text-text-secondary hover:bg-bg-sunken hover:text-text-primary disabled:opacity-40\"\n aria-label=\"Move down\"\n >\n <ArrowDown className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => openEdit(task)}\n className=\"rounded-md p-1 text-text-secondary hover:bg-bg-sunken hover:text-text-primary\"\n aria-label=\"Edit task\"\n >\n <Pencil className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => setDeleteTarget(task)}\n className=\"rounded-md p-1 text-status-error-text hover:bg-status-error-bg\"\n aria-label=\"Delete task\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n ))}\n </div>\n </GlassCard>\n {hasMore && (\n <div className=\"mt-6 flex justify-center\">\n <button\n onClick={() => setLimit((prev) => prev + PAGE_SIZE)}\n disabled={loading}\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 {loading ? \"Loading…\" : \"Load more\"}\n </button>\n </div>\n )}\n </>\n )}\n\n {/* Create modal */}\n <Modal\n open={createOpen}\n onClose={() => setCreateOpen(false)}\n title=\"Add Task\"\n description=\"Break this project into a trackable piece of work.\"\n size=\"xl\"\n footer={\n <>\n <button\n onClick={() => setCreateOpen(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={handleCreate}\n disabled={creating || !form.title.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 {creating ? \"Creating…\" : \"Create Task\"}\n </button>\n </>\n }\n >\n {createError && (\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 create task. Please try again.\n </div>\n )}\n {formFields}\n </Modal>\n\n {/* Edit modal */}\n <Modal\n open={editTarget !== null}\n onClose={() => setEditTarget(null)}\n title=\"Edit Task\"\n size=\"xl\"\n footer={\n <>\n <button\n onClick={() => setEditTarget(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={handleUpdate}\n disabled={updating || !form.title.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 {updating ? \"Saving…\" : \"Save Changes\"}\n </button>\n </>\n }\n >\n {updateError && (\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 task. Please try again.\n </div>\n )}\n {formFields}\n </Modal>\n\n {/* Delete confirm modal */}\n <Modal\n open={deleteTarget !== null}\n onClose={() => setDeleteTarget(null)}\n title=\"Delete Task\"\n size=\"sm\"\n footer={\n <>\n <button\n onClick={() => setDeleteTarget(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={handleDelete}\n disabled={deleting}\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/90 disabled:opacity-50\"\n >\n {deleting ? \"Deleting…\" : \"Delete\"}\n </button>\n </>\n }\n >\n {deleteError && (\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 delete task. Please try again.\n </div>\n )}\n <p className=\"text-sm text-text-secondary\">\n Are you sure you want to delete{\" \"}\n <span className=\"font-semibold text-text-primary\">\n {deleteTarget?.title}\n </span>\n ? This action cannot be undone.\n </p>\n </Modal>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ResourcePlanningPage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/resource-planning/ResourcePlanningPage.tsx"],"names":[],"mappings":"AAgGA,wBAAgB,oBAAoB,gCAkiCnC"}
1
+ {"version":3,"file":"ResourcePlanningPage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/resource-planning/ResourcePlanningPage.tsx"],"names":[],"mappings":"AAgGA,wBAAgB,oBAAoB,gCAoiCnC"}
@@ -33,10 +33,10 @@ var ye = 25, h = "mt-1 w-full rounded-md border border-border-subtle bg-bg-surfa
33
33
  notes: ""
34
34
  });
35
35
  function b() {
36
- let b = /* @__PURE__ */ new Date(), be = new Date(b.getFullYear(), b.getMonth(), b.getDate()).toISOString(), xe = new Date(b.getFullYear(), b.getMonth(), b.getDate() + 7).toISOString(), { data: x, loading: Se, error: Ce, refetch: we, fetchMore: Te } = ae({ variables: {
36
+ let b = /* @__PURE__ */ new Date(), be = new Date(b.getFullYear(), b.getMonth(), b.getDate()).toISOString(), xe = new Date(b.getFullYear(), b.getMonth(), b.getDate() + 7).toISOString(), { data: x, loading: Se, error: Ce, refetch: we, fetchMore: Te } = ae({ variables: { pagination: {
37
37
  limit: ye,
38
38
  offset: 0
39
- } }), { data: Ee, refetch: De } = ie({ variables: {
39
+ } } }), { data: Ee, refetch: De } = ie({ variables: {
40
40
  startDate: be,
41
41
  endDate: xe
42
42
  } }), [Oe, { loading: S, error: ke }] = te(), [Ae, { loading: C, error: je }] = se(), [Me, { loading: w }] = re(), [Ne, { loading: T }] = ee(), [Pe, { loading: E }] = i(), [Fe, { loading: D, error: Ie }] = oe(), [Le, { loading: O }] = ne(), [Re, k] = c(!1), [A, ze] = c(null), [j, M] = c(v()), [N, P] = c(null), [F, I] = c(null), [L, R] = c([]), [Be, z] = c(!1), [B, V] = c(null), [H, U] = c(null), [W, G] = c(null), [K, q] = c(!1), J = x?.resourcePlans?.items ?? [], Ve = x?.resourcePlans?.hasMore ?? !1, Y = Ee?.capacityOverview, X = async () => {
@@ -136,10 +136,10 @@ function b() {
136
136
  q(!0);
137
137
  try {
138
138
  await Te({
139
- variables: {
139
+ variables: { pagination: {
140
140
  limit: ye,
141
141
  offset: J.length
142
- },
142
+ } },
143
143
  updateQuery: (e, { fetchMoreResult: t }) => t ? { resourcePlans: {
144
144
  ...t.resourcePlans,
145
145
  items: [...e.resourcePlans.items, ...t.resourcePlans.items]