@burdenoff/microfe-timecampus 2026.820.3 → 2026.825.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.
@@ -1 +1 @@
1
- {"version":3,"file":"PomodoroPage.js","names":[],"sources":["../../../../src/timecampus/pages/pomodoro/PomodoroPage.tsx"],"sourcesContent":["import { useState, useEffect, useCallback } from \"react\";\nimport {\n Play,\n Pause,\n SkipForward,\n RotateCcw,\n Target,\n Plus,\n Pencil,\n Trash2,\n} from \"lucide-react\";\nimport { GlassCard, NextSteps } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { Modal } from \"../../components/Modal\";\nimport {\n useTimeCampus,\n useTimeCampusNavigate,\n} from \"../../providers/TimeCampusProvider\";\nimport {\n useGetActivePomodoroSessionQuery,\n useGetPomodoroStatsQuery,\n useStartPomodoroSessionMutation,\n useCompletePomodoroSessionMutation,\n useListPomodoroGoalsQuery,\n useCreatePomodoroGoalMutation,\n useUpdatePomodoroGoalMutation,\n useDeletePomodoroGoalMutation,\n type PomodoroGoalFieldsFragment,\n} from \"@/generated/wspace-operations\";\nimport { PomodoroGoalType } from \"@/generated/wspace-types\";\n\ninterface GoalFormState {\n type: PomodoroGoalType;\n target: string;\n period: string;\n startDate: string;\n endDate: string;\n}\n\nconst inputClasses =\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 labelClasses = \"block text-sm font-medium text-text-primary\";\n\nfunction toDateInput(iso: string): string {\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return \"\";\n const local = new Date(d.getTime() - d.getTimezoneOffset() * 60000);\n return local.toISOString().slice(0, 10);\n}\n\nfunction todayInput(): string {\n const d = new Date();\n const local = new Date(d.getTime() - d.getTimezoneOffset() * 60000);\n return local.toISOString().slice(0, 10);\n}\n\nfunction futureInput(days: number): string {\n const d = new Date();\n d.setDate(d.getDate() + days);\n const local = new Date(d.getTime() - d.getTimezoneOffset() * 60000);\n return local.toISOString().slice(0, 10);\n}\n\nfunction toGoalState(goal?: PomodoroGoalFieldsFragment): GoalFormState {\n return {\n type: goal?.type ?? PomodoroGoalType.Daily,\n target: goal?.target != null ? String(goal.target) : \"8\",\n period: goal?.period ?? \"DAILY\",\n startDate: goal?.startDate ? toDateInput(goal.startDate) : todayInput(),\n endDate: goal?.endDate ? toDateInput(goal.endDate) : futureInput(0),\n };\n}\n\nexport function PomodoroPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const navigate = useTimeCampusNavigate();\n\n const {\n data: statsData,\n loading: statsLoading,\n error: statsError,\n } = useGetPomodoroStatsQuery({\n variables: { workspaceId },\n skip: !workspaceId,\n });\n\n const {\n data: activeData,\n loading: activeLoading,\n error: activeError,\n } = useGetActivePomodoroSessionQuery({\n variables: { workspaceId },\n skip: !workspaceId,\n });\n\n const [startSession] = useStartPomodoroSessionMutation();\n const [completeSession] = useCompletePomodoroSessionMutation();\n\n // Focus goals\n const { data: goalsData, refetch: refetchGoals } = useListPomodoroGoalsQuery({\n variables: { workspaceId },\n skip: !workspaceId,\n });\n const goals = goalsData?.pomodoroGoals ?? [];\n\n const [createGoal, { loading: creatingGoal, error: createGoalError }] =\n useCreatePomodoroGoalMutation();\n const [updateGoal, { loading: updatingGoal, error: updateGoalError }] =\n useUpdatePomodoroGoalMutation();\n const [deleteGoal, { loading: deletingGoal, error: deleteGoalError }] =\n useDeletePomodoroGoalMutation();\n\n const [goalModalOpen, setGoalModalOpen] = useState(false);\n const [editingGoal, setEditingGoal] =\n useState<PomodoroGoalFieldsFragment | null>(null);\n const [deletingGoalEntry, setDeletingGoalEntry] =\n useState<PomodoroGoalFieldsFragment | null>(null);\n const [goalForm, setGoalForm] = useState<GoalFormState>(toGoalState());\n\n const setGoalField = <K extends keyof GoalFormState>(\n key: K,\n v: GoalFormState[K],\n ) => setGoalForm((f) => ({ ...f, [key]: v }));\n\n const openCreateGoal = () => {\n setEditingGoal(null);\n setGoalForm(toGoalState());\n setGoalModalOpen(true);\n };\n\n const openEditGoal = (goal: PomodoroGoalFieldsFragment) => {\n setEditingGoal(goal);\n setGoalForm(toGoalState(goal));\n setGoalModalOpen(true);\n };\n\n const handleSaveGoal = async () => {\n if (!workspaceId) return;\n const target = Number(goalForm.target);\n if (!target || target <= 0) return;\n if (editingGoal) {\n await updateGoal({\n variables: {\n id: editingGoal.id,\n workspaceId,\n input: {\n type: goalForm.type,\n target,\n period: goalForm.period.trim() || goalForm.type,\n startDate: new Date(goalForm.startDate).toISOString(),\n endDate: new Date(goalForm.endDate).toISOString(),\n },\n },\n });\n } else {\n await createGoal({\n variables: {\n input: {\n workspaceId,\n type: goalForm.type,\n target,\n period: goalForm.period.trim() || goalForm.type,\n startDate: new Date(goalForm.startDate).toISOString(),\n endDate: new Date(goalForm.endDate).toISOString(),\n },\n },\n });\n }\n setGoalModalOpen(false);\n await refetchGoals();\n };\n\n const handleDeleteGoal = async () => {\n if (!deletingGoalEntry || !workspaceId) return;\n await deleteGoal({\n variables: { id: deletingGoalEntry.id, workspaceId },\n });\n setDeletingGoalEntry(null);\n await refetchGoals();\n };\n\n const stats = statsData?.pomodoroStats;\n const activeSession = activeData?.activePomodoroSession;\n\n const [timerSeconds, setTimerSeconds] = useState(25 * 60);\n const [isRunning, setIsRunning] = useState(false);\n\n useEffect(() => {\n if (activeSession && !activeSession.completed) {\n const elapsed = Math.floor(\n (Date.now() - new Date(activeSession.startTime).getTime()) / 1000,\n );\n const duration = activeSession.duration ?? 25 * 60;\n const remaining = Math.max(0, duration - elapsed);\n setTimerSeconds(remaining);\n setIsRunning(remaining > 0);\n }\n }, [activeSession]);\n\n useEffect(() => {\n if (!isRunning || timerSeconds <= 0) return;\n const interval = setInterval(() => {\n setTimerSeconds((prev) => {\n if (prev <= 1) {\n setIsRunning(false);\n return 0;\n }\n return prev - 1;\n });\n }, 1000);\n return () => clearInterval(interval);\n }, [isRunning, timerSeconds]);\n\n const formatTimer = (seconds: number) => {\n const m = Math.floor(seconds / 60);\n const s = seconds % 60;\n return `${String(m).padStart(2, \"0\")}:${String(s).padStart(2, \"0\")}`;\n };\n\n const handleStart = useCallback(async () => {\n if (!workspaceId) return;\n if (activeSession && !activeSession.completed) {\n setIsRunning(true);\n return;\n }\n await startSession({\n variables: { input: { workspaceId, type: \"FOCUS\" as never } },\n refetchQueries: [\"GetActivePomodoroSession\", \"GetPomodoroStats\"],\n });\n setTimerSeconds(25 * 60);\n setIsRunning(true);\n }, [workspaceId, activeSession, startSession]);\n\n const handleComplete = useCallback(async () => {\n if (!activeSession || !workspaceId) return;\n await completeSession({\n variables: { id: activeSession.id, workspaceId },\n refetchQueries: [\"GetActivePomodoroSession\", \"GetPomodoroStats\"],\n });\n setIsRunning(false);\n setTimerSeconds(25 * 60);\n }, [activeSession, workspaceId, completeSession]);\n\n const handleReset = () => {\n setIsRunning(false);\n setTimerSeconds(25 * 60);\n };\n\n if (statsLoading || activeLoading) {\n return (\n <PageLayout\n title=\"Pomodoro Timer\"\n description=\"Focus with the Pomodoro Technique\"\n >\n <LoadingSkeleton variant=\"card\" count={3} />\n </PageLayout>\n );\n }\n\n if (statsError || activeError) {\n return (\n <PageLayout\n title=\"Pomodoro Timer\"\n description=\"Focus with the Pomodoro Technique\"\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 pomodoro data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n const totalFocusH = Math.floor((stats?.totalFocusMinutes ?? 0) / 60);\n const totalFocusM = (stats?.totalFocusMinutes ?? 0) % 60;\n\n return (\n <PageLayout\n title=\"Pomodoro Timer\"\n description=\"Focus with the Pomodoro Technique\"\n purpose=\"Work in focused 25-minute sprints with short breaks — the Pomodoro technique helps you beat procrastination and build a streak of deep-work sessions you can actually measure.\"\n >\n <NextSteps\n storageKey=\"timecampus-pomodoro\"\n className=\"mb-6\"\n steps={[\n {\n id: \"start\",\n label: \"Start a focus session\",\n description: \"Begin a 25-minute sprint on one task.\",\n onClick: () => handleStart(),\n },\n {\n id: \"analytics\",\n label: \"Review your pomodoro stats\",\n description: \"See streaks, completion rate and focus time.\",\n onClick: () => navigate(\"/pomodoro/analytics\"),\n },\n {\n id: \"settings\",\n label: \"Tune your intervals\",\n description: \"Adjust focus and break lengths to fit you.\",\n onClick: () => navigate(\"/pomodoro/settings\"),\n },\n ]}\n />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <StatCard\n title=\"Sessions Today\"\n value={String(stats?.completedSessions ?? 0)}\n description={`Total: ${stats?.totalSessions ?? 0} sessions`}\n />\n <StatCard\n title=\"Focus Time\"\n value={`${totalFocusH}h ${totalFocusM}m`}\n trend={{\n value: stats?.completionRate ?? 0,\n label: \"completion rate\",\n }}\n />\n <StatCard\n title=\"Current Streak\"\n value={`${stats?.streakDays ?? 0} days`}\n description={`Avg: ${stats?.averageSessionMinutes ?? 0}m per session`}\n />\n </div>\n\n <div className=\"mx-auto max-w-md\">\n <GlassCard treatment=\"glass\" glow className=\"p-8 text-center\">\n <div className=\"mb-2\">\n <span className=\"inline-flex rounded-full bg-action-primary-bg/10 px-3 py-1 text-xs font-medium text-primary\">\n {activeSession ? \"Focus Session\" : \"Ready\"}\n </span>\n </div>\n <p className=\"text-7xl font-mono font-bold text-text-primary\">\n {formatTimer(timerSeconds)}\n </p>\n <p className=\"mt-2 text-sm text-text-secondary\">\n {stats\n ? `${stats.completedSessions} of ${stats.totalSessions} sessions`\n : \"No sessions yet\"}\n </p>\n <div className=\"mt-6 flex items-center justify-center gap-3\">\n {!isRunning ? (\n <button\n onClick={handleStart}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-6 py-3 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Play className=\"h-4 w-4\" />\n Start\n </button>\n ) : (\n <button\n onClick={() => setIsRunning(false)}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-6 py-3 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Pause className=\"h-4 w-4\" />\n Pause\n </button>\n )}\n <button\n onClick={handleComplete}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-3 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n <SkipForward className=\"h-4 w-4\" />\n Skip\n </button>\n <button\n onClick={handleReset}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-3 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n <RotateCcw className=\"h-4 w-4\" />\n Reset\n </button>\n </div>\n </GlassCard>\n </div>\n\n {/* Focus Goals */}\n <GlassCard treatment=\"glass\" className=\"p-6\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h3 className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Target className=\"h-4 w-4\" />\n Focus Goals\n </h3>\n <button\n onClick={openCreateGoal}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-1.5 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus className=\"h-4 w-4\" />\n Add Goal\n </button>\n </div>\n {goals.length === 0 ? (\n <p className=\"py-6 text-center text-sm text-text-secondary\">\n No focus goals yet. Set a daily or weekly target to build a\n consistent focus habit.\n </p>\n ) : (\n <div className=\"space-y-3\">\n {goals.map((goal: PomodoroGoalFieldsFragment) => {\n const pct = goal.target\n ? Math.min(100, Math.round((goal.current / goal.target) * 100))\n : 0;\n return (\n <div\n key={goal.id}\n className=\"rounded-lg border border-border-subtle p-4\"\n >\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm font-medium capitalize text-text-primary\">\n {goal.type.toLowerCase()} goal\n </p>\n <p className=\"text-xs text-text-secondary\">\n {goal.current} of {goal.target} sessions\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium text-text-primary\">\n {pct}%\n </span>\n <button\n onClick={() => openEditGoal(goal)}\n className=\"rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-text-primary\"\n aria-label=\"Edit goal\"\n >\n <Pencil className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => setDeletingGoalEntry(goal)}\n className=\"rounded-md p-1.5 text-text-secondary hover:bg-status-error-bg hover:text-status-error-text\"\n aria-label=\"Delete goal\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n <div className=\"mt-3 h-2 w-full rounded-full bg-bg-sunken\">\n <div\n className=\"h-2 rounded-full bg-action-primary-bg\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })}\n </div>\n )}\n </GlassCard>\n\n {/* Goal create/edit modal */}\n <Modal\n open={goalModalOpen}\n onClose={() => setGoalModalOpen(false)}\n title={editingGoal ? \"Edit Focus Goal\" : \"Add Focus Goal\"}\n description=\"Set a target number of focus sessions\"\n footer={\n <>\n <button\n onClick={() => setGoalModalOpen(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={handleSaveGoal}\n disabled={\n creatingGoal || updatingGoal || Number(goalForm.target) <= 0\n }\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 {creatingGoal || updatingGoal\n ? \"Saving...\"\n : editingGoal\n ? \"Save Changes\"\n : \"Add Goal\"}\n </button>\n </>\n }\n >\n {(createGoalError || updateGoalError) && (\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 goal. Please try again.\n </div>\n )}\n <div className=\"space-y-4\">\n <div>\n <label className={labelClasses}>Goal Type</label>\n <select\n value={goalForm.type}\n onChange={(e) => {\n const type = e.target.value as PomodoroGoalType;\n setGoalForm((f) => ({\n ...f,\n type,\n period: type,\n endDate:\n type === PomodoroGoalType.Daily\n ? futureInput(0)\n : type === PomodoroGoalType.Weekly\n ? futureInput(7)\n : futureInput(30),\n }));\n }}\n className={inputClasses}\n >\n <option value={PomodoroGoalType.Daily}>Daily</option>\n <option value={PomodoroGoalType.Weekly}>Weekly</option>\n <option value={PomodoroGoalType.Monthly}>Monthly</option>\n </select>\n </div>\n <div>\n <label className={labelClasses}>Target Sessions</label>\n <input\n type=\"number\"\n min=\"1\"\n placeholder=\"8\"\n value={goalForm.target}\n onChange={(e) => setGoalField(\"target\", e.target.value)}\n className={inputClasses}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className={labelClasses}>Start Date</label>\n <input\n type=\"date\"\n value={goalForm.startDate}\n onChange={(e) => setGoalField(\"startDate\", e.target.value)}\n className={inputClasses}\n />\n </div>\n <div>\n <label className={labelClasses}>End Date</label>\n <input\n type=\"date\"\n value={goalForm.endDate}\n onChange={(e) => setGoalField(\"endDate\", e.target.value)}\n className={inputClasses}\n />\n </div>\n </div>\n </div>\n </Modal>\n\n {/* Goal delete confirm modal */}\n <Modal\n open={deletingGoalEntry !== null}\n onClose={() => setDeletingGoalEntry(null)}\n title=\"Delete Focus Goal\"\n size=\"sm\"\n footer={\n <>\n <button\n onClick={() => setDeletingGoalEntry(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={handleDeleteGoal}\n disabled={deletingGoal}\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 {deletingGoal ? \"Deleting...\" : \"Delete\"}\n </button>\n </>\n }\n >\n {deleteGoalError && (\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 goal. Please try again.\n </div>\n )}\n <p className=\"text-sm text-text-secondary\">\n Are you sure you want to delete this{\" \"}\n {deletingGoalEntry?.type.toLowerCase()} focus goal? This action cannot\n be undone.\n </p>\n </Modal>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAyCA,IAAM,IACJ,wIACI,IAAe;AAErB,SAAS,EAAY,GAAqB;CACxC,IAAM,IAAI,IAAI,KAAK,CAAG;CAGtB,OAFI,OAAO,MAAM,EAAE,QAAQ,CAAC,IAAU,sBAE/B,IADW,KAAK,EAAE,QAAQ,IAAI,EAAE,kBAAkB,IAAI,GACtD,EAAA,CAAM,YAAY,CAAC,CAAC,MAAM,GAAG,EAAE;AACxC;AAEA,SAAS,IAAqB;CAC5B,IAAM,oBAAI,IAAI,KAAK;CAEnB,wBAAO,IADW,KAAK,EAAE,QAAQ,IAAI,EAAE,kBAAkB,IAAI,GACtD,EAAA,CAAM,YAAY,CAAC,CAAC,MAAM,GAAG,EAAE;AACxC;AAEA,SAAS,EAAY,GAAsB;CACzC,IAAM,oBAAI,IAAI,KAAK;CAGnB,OAFA,EAAE,QAAQ,EAAE,QAAQ,IAAI,CAAI,oBAErB,IADW,KAAK,EAAE,QAAQ,IAAI,EAAE,kBAAkB,IAAI,GACtD,EAAA,CAAM,YAAY,CAAC,CAAC,MAAM,GAAG,EAAE;AACxC;AAEA,SAAS,EAAY,GAAkD;CACrE,OAAO;EACL,MAAM,GAAM,QAAQ,EAAiB;EACrC,QAAQ,GAAM,UAAU,OAA6B,MAAtB,OAAO,EAAK,MAAM;EACjD,QAAQ,GAAM,UAAU;EACxB,WAAW,GAAM,YAAY,EAAY,EAAK,SAAS,IAAI,EAAW;EACtE,SAAS,GAAM,UAAU,EAAY,EAAK,OAAO,IAAI,EAAY,CAAC;CACpE;AACF;AAEA,SAAgB,IAAe;CAC7B,IAAM,EAAE,iBAAc,OAAO,EAAc,GACrC,IAAW,EAAsB,GAEjC,EACJ,MAAM,GACN,SAAS,IACT,OAAO,OACL,GAAyB;EAC3B,WAAW,EAAE,eAAY;EACzB,MAAM,CAAC;CACT,CAAC,GAEK,EACJ,MAAM,IACN,SAAS,IACT,OAAO,OACL,GAAiC;EACnC,WAAW,EAAE,eAAY;EACzB,MAAM,CAAC;CACT,CAAC,GAEK,CAAC,KAAgB,EAAgC,GACjD,CAAC,KAAmB,EAAmC,GAGvD,EAAE,MAAM,GAAW,SAAS,MAAiB,GAA0B;EAC3E,WAAW,EAAE,eAAY;EACzB,MAAM,CAAC;CACT,CAAC,GACK,IAAQ,GAAW,iBAAiB,CAAC,GAErC,CAAC,GAAY,EAAE,SAAS,GAAc,OAAO,OACjD,GAA8B,GAC1B,CAAC,GAAY,EAAE,SAAS,GAAc,OAAO,OACjD,EAA8B,GAC1B,CAAC,GAAY,EAAE,SAAS,GAAc,OAAO,QACjD,GAA8B,GAE1B,CAAC,IAAe,KAAoB,EAAS,EAAK,GAClD,CAAC,GAAa,KAClB,EAA4C,IAAI,GAC5C,CAAC,GAAmB,KACxB,EAA4C,IAAI,GAC5C,CAAC,GAAU,KAAe,EAAwB,EAAY,CAAC,GAE/D,KACJ,GACA,MACG,GAAa,OAAO;EAAE,GAAG;GAAI,IAAM;CAAE,EAAE,GAEtC,WAAuB;EAG3B,AAFA,EAAe,IAAI,GACnB,EAAY,EAAY,CAAC,GACzB,EAAiB,EAAI;CACvB,GAEM,MAAgB,MAAqC;EAGzD,AAFA,EAAe,CAAI,GACnB,EAAY,EAAY,CAAI,CAAC,GAC7B,EAAiB,EAAI;CACvB,GAEM,KAAiB,YAAY;EACjC,IAAI,CAAC,GAAa;EAClB,IAAM,IAAS,OAAO,EAAS,MAAM;EACjC,CAAC,KAAU,KAAU,MACrB,IACF,MAAM,EAAW,EACf,WAAW;GACT,IAAI,EAAY;GAChB;GACA,OAAO;IACL,MAAM,EAAS;IACf;IACA,QAAQ,EAAS,OAAO,KAAK,KAAK,EAAS;IAC3C,WAAW,IAAI,KAAK,EAAS,SAAS,CAAC,CAAC,YAAY;IACpD,SAAS,IAAI,KAAK,EAAS,OAAO,CAAC,CAAC,YAAY;GAClD;EACF,EACF,CAAC,IAED,MAAM,EAAW,EACf,WAAW,EACT,OAAO;GACL;GACA,MAAM,EAAS;GACf;GACA,QAAQ,EAAS,OAAO,KAAK,KAAK,EAAS;GAC3C,WAAW,IAAI,KAAK,EAAS,SAAS,CAAC,CAAC,YAAY;GACpD,SAAS,IAAI,KAAK,EAAS,OAAO,CAAC,CAAC,YAAY;EAClD,EACF,EACF,CAAC,GAEH,EAAiB,EAAK,GACtB,MAAM,EAAa;CACrB,GAEM,KAAmB,YAAY;EAC/B,CAAC,KAAqB,CAAC,MAC3B,MAAM,EAAW,EACf,WAAW;GAAE,IAAI,EAAkB;GAAI;EAAY,EACrD,CAAC,GACD,EAAqB,IAAI,GACzB,MAAM,EAAa;CACrB,GAEM,IAAQ,GAAW,eACnB,IAAgB,IAAY,uBAE5B,CAAC,GAAc,KAAmB,EAAS,IAAO,GAClD,CAAC,GAAW,KAAgB,EAAS,EAAK;CAchD,AAZA,QAAgB;EACd,IAAI,KAAiB,CAAC,EAAc,WAAW;GAC7C,IAAM,IAAU,KAAK,OAClB,KAAK,IAAI,IAAI,IAAI,KAAK,EAAc,SAAS,CAAC,CAAC,QAAQ,KAAK,GAC/D,GACM,IAAW,EAAc,YAAY,MACrC,IAAY,KAAK,IAAI,GAAG,IAAW,CAAO;GAEhD,AADA,EAAgB,CAAS,GACzB,EAAa,IAAY,CAAC;EAC5B;CACF,GAAG,CAAC,CAAa,CAAC,GAElB,QAAgB;EACd,IAAI,CAAC,KAAa,KAAgB,GAAG;EACrC,IAAM,IAAW,kBAAkB;GACjC,GAAiB,MACX,KAAQ,KACV,EAAa,EAAK,GACX,KAEF,IAAO,CACf;EACH,GAAG,GAAI;EACP,aAAa,cAAc,CAAQ;CACrC,GAAG,CAAC,GAAW,CAAY,CAAC;CAE5B,IAAM,MAAe,MAAoB;EACvC,IAAM,IAAI,KAAK,MAAM,IAAU,EAAE,GAC3B,IAAI,IAAU;EACpB,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;CACnE,GAEM,IAAc,EAAY,YAAY;EACrC,OACL;OAAI,KAAiB,CAAC,EAAc,WAAW;IAC7C,EAAa,EAAI;IACjB;GACF;GAMA,AALA,MAAM,EAAa;IACjB,WAAW,EAAE,OAAO;KAAE;KAAa,MAAM;IAAiB,EAAE;IAC5D,gBAAgB,CAAC,4BAA4B,kBAAkB;GACjE,CAAC,GACD,EAAgB,IAAO,GACvB,EAAa,EAAI;EANjB;CAOF,GAAG;EAAC;EAAa;EAAe;CAAY,CAAC,GAEvC,KAAiB,EAAY,YAAY;EACzC,CAAC,KAAiB,CAAC,MACvB,MAAM,EAAgB;GACpB,WAAW;IAAE,IAAI,EAAc;IAAI;GAAY;GAC/C,gBAAgB,CAAC,4BAA4B,kBAAkB;EACjE,CAAC,GACD,EAAa,EAAK,GAClB,EAAgB,IAAO;CACzB,GAAG;EAAC;EAAe;EAAa;CAAe,CAAC,GAE1C,WAAoB;EAExB,AADA,EAAa,EAAK,GAClB,EAAgB,IAAO;CACzB;CAEA,IAAI,MAAgB,IAClB,OACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EAEZ,UAAA,kBAAC,GAAD;GAAiB,SAAQ;GAAO,OAAO;EAAI,CAAA;CACjC,CAAA;CAIhB,IAAI,MAAc,IAChB,OACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EAEZ,UAAA,kBAAC,OAAD;GAAK,WAAU;GAAoG,UAAA;EAE9G,CAAA;CACK,CAAA;CAIhB,IAAM,KAAc,KAAK,OAAO,GAAO,qBAAqB,KAAK,EAAE,GAC7D,MAAe,GAAO,qBAAqB,KAAK;CAEtD,OACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SAAQ;EAHV,UAAA;GAKE,kBAAC,GAAD;IACE,YAAW;IACX,WAAU;IACV,OAAO;KACL;MACE,IAAI;MACJ,OAAO;MACP,aAAa;MACb,eAAe,EAAY;KAC7B;KACA;MACE,IAAI;MACJ,OAAO;MACP,aAAa;MACb,eAAe,EAAS,qBAAqB;KAC/C;KACA;MACE,IAAI;MACJ,OAAO;MACP,aAAa;MACb,eAAe,EAAS,oBAAoB;KAC9C;IACF;GACD,CAAA;GACD,kBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,OAAO,GAAO,qBAAqB,CAAC;MAC3C,aAAa,UAAU,GAAO,iBAAiB,EAAE;KAClD,CAAA;KACD,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,GAAG,GAAY,IAAI,GAAY;MACtC,OAAO;OACL,OAAO,GAAO,kBAAkB;OAChC,OAAO;MACT;KACD,CAAA;KACD,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,GAAG,GAAO,cAAc,EAAE;MACjC,aAAa,QAAQ,GAAO,yBAAyB,EAAE;KACxD,CAAA;IACE;;GAEL,kBAAC,OAAD;IAAK,WAAU;IACb,UAAA,kBAAC,GAAD;KAAW,WAAU;KAAQ,MAAA;KAAK,WAAU;KAA5C,UAAA;MACE,kBAAC,OAAD;OAAK,WAAU;OACb,UAAA,kBAAC,QAAD;QAAM,WAAU;QACb,UAAA,IAAgB,kBAAkB;OAC/B,CAAA;MACH,CAAA;MACL,kBAAC,KAAD;OAAG,WAAU;OACV,UAAA,GAAY,CAAY;MACxB,CAAA;MACH,kBAAC,KAAD;OAAG,WAAU;OACV,UAAA,IACG,GAAG,EAAM,kBAAkB,MAAM,EAAM,cAAc,aACrD;MACH,CAAA;MACH,kBAAC,OAAD;OAAK,WAAU;OAAf,UAAA;QACI,IASA,kBAAC,UAAD;SACE,eAAe,EAAa,EAAK;SACjC,WAAU;SAFZ,UAAA,CAIE,kBAAC,IAAD,EAAO,WAAU,UAAW,CAAA,GAAC,OAEvB;aAdR,kBAAC,UAAD;SACE,SAAS;SACT,WAAU;SAFZ,UAAA,CAIE,kBAAC,IAAD,EAAM,WAAU,UAAW,CAAA,GAAC,OAEtB;QAER,CAAA;QAQF,kBAAC,UAAD;SACE,SAAS;SACT,WAAU;SAFZ,UAAA,CAIE,kBAAC,IAAD,EAAa,WAAU,UAAW,CAAA,GAAC,MAE7B;;QACR,kBAAC,UAAD;SACE,SAAS;SACT,WAAU;SAFZ,UAAA,CAIE,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA,GAAC,OAE3B;;OACL;;KACI;;GACR,CAAA;GAGL,kBAAC,GAAD;IAAW,WAAU;IAAQ,WAAU;IAAvC,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,kBAAC,MAAD;MAAI,WAAU;MAAd,UAAA,CACE,kBAAC,IAAD,EAAQ,WAAU,UAAW,CAAA,GAAC,aAE5B;KACJ,CAAA,GAAA,kBAAC,UAAD;MACE,SAAS;MACT,WAAU;MAFZ,UAAA,CAIE,kBAAC,IAAD,EAAM,WAAU,UAAW,CAAA,GAAC,UAEtB;KACL,CAAA,CAAA;IACJ,CAAA,GAAA,EAAM,WAAW,IAChB,kBAAC,KAAD;KAAG,WAAU;KAA+C,UAAA;IAGzD,CAAA,IAEH,kBAAC,OAAD;KAAK,WAAU;KACZ,UAAA,EAAM,KAAK,MAAqC;MAC/C,IAAM,IAAM,EAAK,SACb,KAAK,IAAI,KAAK,KAAK,MAAO,EAAK,UAAU,EAAK,SAAU,GAAG,CAAC,IAC5D;MACJ,OACE,kBAAC,OAAD;OAEE,WAAU;OAFZ,UAAA,CAIE,kBAAC,OAAD;QAAK,WAAU;QAAf,UAAA,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;SAAb,UAAA,CACG,EAAK,KAAK,YAAY,GAAE,OACxB;QACH,CAAA,GAAA,kBAAC,KAAD;SAAG,WAAU;SAAb,UAAA;UACG,EAAK;UAAQ;UAAK,EAAK;UAAO;SAC9B;QACA,CAAA,CAAA,EAAA,CAAA,GACL,kBAAC,OAAD;SAAK,WAAU;SAAf,UAAA;UACE,kBAAC,QAAD;WAAM,WAAU;WAAhB,UAAA,CACG,GAAI,GACD;;UACN,kBAAC,UAAD;WACE,eAAe,GAAa,CAAI;WAChC,WAAU;WACV,cAAW;WAEX,UAAA,kBAAC,IAAD,EAAQ,WAAU,UAAW,CAAA;UACvB,CAAA;UACR,kBAAC,UAAD;WACE,eAAe,EAAqB,CAAI;WACxC,WAAU;WACV,cAAW;WAEX,UAAA,kBAAC,IAAD,EAAQ,WAAU,UAAW,CAAA;UACvB,CAAA;SACL;QACF,CAAA,CAAA;OACL,CAAA,GAAA,kBAAC,OAAD;QAAK,WAAU;QACb,UAAA,kBAAC,OAAD;SACE,WAAU;SACV,OAAO,EAAE,OAAO,GAAG,EAAI,GAAG;QAC3B,CAAA;OACE,CAAA,CACF;MAtCE,GAAA,EAAK,EAsCP;KAET,CAAC;IACE,CAAA,CAEE;;GAGX,kBAAC,GAAD;IACE,MAAM;IACN,eAAe,EAAiB,EAAK;IACrC,OAAO,IAAc,oBAAoB;IACzC,aAAY;IACZ,QACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,UAAD;KACE,eAAe,EAAiB,EAAK;KACrC,WAAU;KACX,UAAA;IAEO,CAAA,GACR,kBAAC,UAAD;KACE,SAAS;KACT,UACE,KAAgB,KAAgB,OAAO,EAAS,MAAM,KAAK;KAE7D,WAAU;KAET,UAAA,KAAgB,IACb,cACA,IACE,iBACA;IACA,CAAA,CACR,EAAA,CAAA;IA1BN,UAAA,EA6BI,KAAmB,MACnB,kBAAC,OAAD;KAAK,WAAU;KAAyG,UAAA;IAEnH,CAAA,GAEP,kBAAC,OAAD;KAAK,WAAU;KAAf,UAAA;MACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,WAAW;OAAc,UAAA;MAAgB,CAAA,GAChD,kBAAC,UAAD;OACE,OAAO,EAAS;OAChB,WAAW,MAAM;QACf,IAAM,IAAO,EAAE,OAAO;QACtB,GAAa,OAAO;SAClB,GAAG;SACH;SACA,QAAQ;SACR,SACE,MAAS,EAAiB,QACtB,EAAY,CAAC,IACb,MAAS,EAAiB,SACxB,EAAY,CAAC,IACb,EAAY,EAAE;QACxB,EAAE;OACJ;OACA,WAAW;OAhBb,UAAA;QAkBE,kBAAC,UAAD;SAAQ,OAAO,EAAiB;SAAO,UAAA;QAAa,CAAA;QACpD,kBAAC,UAAD;SAAQ,OAAO,EAAiB;SAAQ,UAAA;QAAc,CAAA;QACtD,kBAAC,UAAD;SAAQ,OAAO,EAAiB;SAAS,UAAA;QAAe,CAAA;OAClD;MACL,CAAA,CAAA,EAAA,CAAA;MACL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,WAAW;OAAc,UAAA;MAAsB,CAAA,GACtD,kBAAC,SAAD;OACE,MAAK;OACL,KAAI;OACJ,aAAY;OACZ,OAAO,EAAS;OAChB,WAAW,MAAM,EAAa,UAAU,EAAE,OAAO,KAAK;OACtD,WAAW;MACZ,CAAA,CACE,EAAA,CAAA;MACL,kBAAC,OAAD;OAAK,WAAU;OAAf,UAAA,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QAAO,WAAW;QAAc,UAAA;OAAiB,CAAA,GACjD,kBAAC,SAAD;QACE,MAAK;QACL,OAAO,EAAS;QAChB,WAAW,MAAM,EAAa,aAAa,EAAE,OAAO,KAAK;QACzD,WAAW;OACZ,CAAA,CACE,EAAA,CAAA,GACL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QAAO,WAAW;QAAc,UAAA;OAAe,CAAA,GAC/C,kBAAC,SAAD;QACE,MAAK;QACL,OAAO,EAAS;QAChB,WAAW,MAAM,EAAa,WAAW,EAAE,OAAO,KAAK;QACvD,WAAW;OACZ,CAAA,CACE,EAAA,CAAA,CACF;;KACF;IACA,CAAA,CAAA;;GAGP,kBAAC,GAAD;IACE,MAAM,MAAsB;IAC5B,eAAe,EAAqB,IAAI;IACxC,OAAM;IACN,MAAK;IACL,QACE,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,UAAD;KACE,eAAe,EAAqB,IAAI;KACxC,WAAU;KACX,UAAA;IAEO,CAAA,GACR,kBAAC,UAAD;KACE,SAAS;KACT,UAAU;KACV,WAAU;KAET,UAAA,IAAe,gBAAgB;IAC1B,CAAA,CACR,EAAA,CAAA;IApBN,UAAA,CAuBG,MACC,kBAAC,OAAD;KAAK,WAAU;KAAyG,UAAA;IAEnH,CAAA,GAEP,kBAAC,KAAD;KAAG,WAAU;KAAb,UAAA;MAA2C;MACJ;MACpC,GAAmB,KAAK,YAAY;MAAE;KAEtC;IACE,CAAA,CAAA;;EACG;;AAEhB"}
1
+ {"version":3,"file":"PomodoroPage.js","names":[],"sources":["../../../../src/timecampus/pages/pomodoro/PomodoroPage.tsx"],"sourcesContent":["import { useState, useEffect, useCallback, useRef } from \"react\";\nimport {\n Play,\n Pause,\n SkipForward,\n RotateCcw,\n Target,\n Plus,\n Pencil,\n Trash2,\n} from \"lucide-react\";\nimport { GlassCard, NextSteps } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { StatCard } from \"../../components/StatCard\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { Modal } from \"../../components/Modal\";\nimport {\n useTimeCampus,\n useTimeCampusNavigate,\n} from \"../../providers/TimeCampusProvider\";\nimport {\n useGetActivePomodoroSessionQuery,\n useGetPomodoroSettingsQuery,\n useGetPomodoroStatsQuery,\n useListPomodoroSessionsQuery,\n useStartPomodoroSessionMutation,\n useCompletePomodoroSessionMutation,\n useInterruptPomodoroSessionMutation,\n useListPomodoroGoalsQuery,\n useCreatePomodoroGoalMutation,\n useUpdatePomodoroGoalMutation,\n useDeletePomodoroGoalMutation,\n type PomodoroGoalFieldsFragment,\n} from \"@/generated/wspace-operations\";\nimport {\n PomodoroGoalType,\n PomodoroSessionOrderByField,\n PomodoroSessionType,\n TimeCampusSortDirection,\n} from \"@/generated/wspace-types\";\nimport { durationForSession, nextSessionAfter } from \"./pomodoroDuration\";\n\ninterface GoalFormState {\n type: PomodoroGoalType;\n target: string;\n period: string;\n startDate: string;\n endDate: string;\n}\n\nconst inputClasses =\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 labelClasses = \"block text-sm font-medium text-text-primary\";\n\nfunction toDateInput(iso: string): string {\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return \"\";\n const local = new Date(d.getTime() - d.getTimezoneOffset() * 60000);\n return local.toISOString().slice(0, 10);\n}\n\nfunction todayInput(): string {\n const d = new Date();\n const local = new Date(d.getTime() - d.getTimezoneOffset() * 60000);\n return local.toISOString().slice(0, 10);\n}\n\nfunction futureInput(days: number): string {\n const d = new Date();\n d.setDate(d.getDate() + days);\n const local = new Date(d.getTime() - d.getTimezoneOffset() * 60000);\n return local.toISOString().slice(0, 10);\n}\n\nfunction toGoalState(goal?: PomodoroGoalFieldsFragment): GoalFormState {\n return {\n type: goal?.type ?? PomodoroGoalType.Daily,\n target: goal?.target != null ? String(goal.target) : \"8\",\n period: goal?.period ?? \"DAILY\",\n startDate: goal?.startDate ? toDateInput(goal.startDate) : todayInput(),\n endDate: goal?.endDate ? toDateInput(goal.endDate) : futureInput(0),\n };\n}\n\nexport function PomodoroPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n const navigate = useTimeCampusNavigate();\n\n const {\n data: statsData,\n loading: statsLoading,\n error: statsError,\n } = useGetPomodoroStatsQuery({\n variables: { workspaceId },\n skip: !workspaceId,\n });\n\n const {\n data: activeData,\n loading: activeLoading,\n error: activeError,\n } = useGetActivePomodoroSessionQuery({\n variables: { workspaceId },\n skip: !workspaceId,\n });\n\n const {\n data: settingsData,\n loading: settingsLoading,\n error: settingsError,\n } = useGetPomodoroSettingsQuery({\n variables: { workspaceId },\n skip: !workspaceId,\n });\n\n const { data: recentSessionsData } = useListPomodoroSessionsQuery({\n variables: {\n workspaceId,\n orderBy: {\n field: PomodoroSessionOrderByField.StartTime,\n direction: TimeCampusSortDirection.Desc,\n },\n pagination: { limit: 1, offset: 0 },\n },\n skip: !workspaceId,\n });\n\n const [startSession] = useStartPomodoroSessionMutation();\n const [completeSession] = useCompletePomodoroSessionMutation();\n const [interruptSession] = useInterruptPomodoroSessionMutation();\n\n // Focus goals\n const { data: goalsData, refetch: refetchGoals } = useListPomodoroGoalsQuery({\n variables: { workspaceId },\n skip: !workspaceId,\n });\n const goals = goalsData?.pomodoroGoals ?? [];\n\n const [createGoal, { loading: creatingGoal, error: createGoalError }] =\n useCreatePomodoroGoalMutation();\n const [updateGoal, { loading: updatingGoal, error: updateGoalError }] =\n useUpdatePomodoroGoalMutation();\n const [deleteGoal, { loading: deletingGoal, error: deleteGoalError }] =\n useDeletePomodoroGoalMutation();\n\n const [goalModalOpen, setGoalModalOpen] = useState(false);\n const [editingGoal, setEditingGoal] =\n useState<PomodoroGoalFieldsFragment | null>(null);\n const [deletingGoalEntry, setDeletingGoalEntry] =\n useState<PomodoroGoalFieldsFragment | null>(null);\n const [goalForm, setGoalForm] = useState<GoalFormState>(toGoalState());\n\n const setGoalField = <K extends keyof GoalFormState>(\n key: K,\n v: GoalFormState[K],\n ) => setGoalForm((f) => ({ ...f, [key]: v }));\n\n const openCreateGoal = () => {\n setEditingGoal(null);\n setGoalForm(toGoalState());\n setGoalModalOpen(true);\n };\n\n const openEditGoal = (goal: PomodoroGoalFieldsFragment) => {\n setEditingGoal(goal);\n setGoalForm(toGoalState(goal));\n setGoalModalOpen(true);\n };\n\n const handleSaveGoal = async () => {\n if (!workspaceId) return;\n const target = Number(goalForm.target);\n if (!target || target <= 0) return;\n if (editingGoal) {\n await updateGoal({\n variables: {\n id: editingGoal.id,\n workspaceId,\n input: {\n type: goalForm.type,\n target,\n period: goalForm.period.trim() || goalForm.type,\n startDate: new Date(goalForm.startDate).toISOString(),\n endDate: new Date(goalForm.endDate).toISOString(),\n },\n },\n });\n } else {\n await createGoal({\n variables: {\n input: {\n workspaceId,\n type: goalForm.type,\n target,\n period: goalForm.period.trim() || goalForm.type,\n startDate: new Date(goalForm.startDate).toISOString(),\n endDate: new Date(goalForm.endDate).toISOString(),\n },\n },\n });\n }\n setGoalModalOpen(false);\n await refetchGoals();\n };\n\n const handleDeleteGoal = async () => {\n if (!deletingGoalEntry || !workspaceId) return;\n await deleteGoal({\n variables: { id: deletingGoalEntry.id, workspaceId },\n });\n setDeletingGoalEntry(null);\n await refetchGoals();\n };\n\n const stats = statsData?.pomodoroStats;\n const activeSession = activeData?.activePomodoroSession;\n const settings = settingsData?.pomodoroSettings;\n const lastSession = recentSessionsData?.pomodoroSessions[0];\n\n const [timerSeconds, setTimerSeconds] = useState(25 * 60);\n const [isRunning, setIsRunning] = useState(false);\n const [nextSessionType, setNextSessionType] = useState(\n PomodoroSessionType.Focus,\n );\n const completingSessionRef = useRef<string | null>(null);\n\n const getDuration = useCallback(\n (type: PomodoroSessionType) => durationForSession(type, settings),\n [settings],\n );\n\n const getNextSessionType = useCallback(\n (completedType: PomodoroSessionType) => {\n const focusSessions = (stats?.completedSessions ?? 0) + 1;\n return nextSessionAfter(\n completedType,\n focusSessions,\n settings?.sessionsBeforeLongBreak ?? 4,\n );\n },\n [settings?.sessionsBeforeLongBreak, stats?.completedSessions],\n );\n\n useEffect(() => {\n if (activeSession || !lastSession?.completed) return;\n const next = nextSessionAfter(\n lastSession.type,\n stats?.completedSessions ?? 0,\n settings?.sessionsBeforeLongBreak ?? 4,\n );\n setNextSessionType(next);\n setTimerSeconds(getDuration(next));\n }, [activeSession, getDuration, lastSession, settings, stats]);\n\n useEffect(() => {\n if (activeSession && !activeSession.completed) {\n const elapsed = Math.floor(\n (Date.now() - new Date(activeSession.startTime).getTime()) / 1000,\n );\n const duration =\n activeSession.duration ?? getDuration(activeSession.type);\n const remaining = Math.max(0, duration - elapsed);\n setTimerSeconds(remaining);\n setIsRunning(remaining > 0);\n }\n }, [activeSession, getDuration]);\n\n const handleComplete = useCallback(async () => {\n if (!activeSession || !workspaceId) return;\n if (completingSessionRef.current === activeSession.id) return;\n completingSessionRef.current = activeSession.id;\n try {\n await completeSession({\n variables: { id: activeSession.id, workspaceId },\n refetchQueries: [\n \"GetActivePomodoroSession\",\n \"GetPomodoroStats\",\n \"ListPomodoroSessions\",\n ],\n awaitRefetchQueries: true,\n });\n const next = getNextSessionType(activeSession.type);\n setNextSessionType(next);\n setTimerSeconds(getDuration(next));\n setIsRunning(false);\n } finally {\n completingSessionRef.current = null;\n }\n }, [\n activeSession,\n completeSession,\n getDuration,\n getNextSessionType,\n workspaceId,\n ]);\n\n useEffect(() => {\n if (timerSeconds <= 0) {\n setIsRunning(false);\n void handleComplete();\n return;\n }\n if (!isRunning) return;\n const timeout = window.setTimeout(\n () => setTimerSeconds((previous) => Math.max(0, previous - 1)),\n 1000,\n );\n return () => window.clearTimeout(timeout);\n }, [handleComplete, isRunning, timerSeconds]);\n\n const formatTimer = (seconds: number) => {\n const m = Math.floor(seconds / 60);\n const s = seconds % 60;\n return `${String(m).padStart(2, \"0\")}:${String(s).padStart(2, \"0\")}`;\n };\n\n const handleStart = useCallback(async () => {\n if (!workspaceId) return;\n if (activeSession && !activeSession.completed) {\n setIsRunning(true);\n return;\n }\n const type = nextSessionType;\n await startSession({\n variables: { input: { workspaceId, type } },\n refetchQueries: [\"GetActivePomodoroSession\", \"GetPomodoroStats\"],\n awaitRefetchQueries: true,\n });\n setTimerSeconds(getDuration(type));\n setIsRunning(true);\n }, [workspaceId, activeSession, getDuration, nextSessionType, startSession]);\n\n const handleReset = async () => {\n setIsRunning(false);\n if (activeSession && workspaceId) {\n await interruptSession({\n variables: {\n id: activeSession.id,\n workspaceId,\n reason: \"Reset by user\",\n },\n refetchQueries: [\n \"GetActivePomodoroSession\",\n \"GetPomodoroStats\",\n \"ListPomodoroSessions\",\n ],\n awaitRefetchQueries: true,\n });\n setNextSessionType(activeSession.type);\n }\n setTimerSeconds(getDuration(activeSession?.type ?? nextSessionType));\n };\n\n if (statsLoading || activeLoading || settingsLoading) {\n return (\n <PageLayout\n title=\"Pomodoro Timer\"\n description=\"Focus with the Pomodoro Technique\"\n >\n <LoadingSkeleton variant=\"card\" count={3} />\n </PageLayout>\n );\n }\n\n if (statsError || activeError || settingsError) {\n return (\n <PageLayout\n title=\"Pomodoro Timer\"\n description=\"Focus with the Pomodoro Technique\"\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 pomodoro data. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n const totalFocusH = Math.floor((stats?.totalFocusMinutes ?? 0) / 60);\n const totalFocusM = (stats?.totalFocusMinutes ?? 0) % 60;\n const displayedSessionType = activeSession?.type ?? nextSessionType;\n const phaseLabel =\n displayedSessionType === PomodoroSessionType.ShortBreak\n ? activeSession\n ? \"Short Break\"\n : \"Short Break Ready\"\n : displayedSessionType === PomodoroSessionType.LongBreak\n ? activeSession\n ? \"Long Break\"\n : \"Long Break Ready\"\n : activeSession\n ? \"Focus Session\"\n : \"Focus Ready\";\n const startLabel =\n displayedSessionType === PomodoroSessionType.ShortBreak\n ? \"Start Short Break\"\n : displayedSessionType === PomodoroSessionType.LongBreak\n ? \"Start Long Break\"\n : \"Start Focus\";\n\n return (\n <PageLayout\n title=\"Pomodoro Timer\"\n description=\"Focus with the Pomodoro Technique\"\n purpose=\"Work in focused sprints with prompted short and long breaks — configure a cadence that fits your rhythm and build a deep-work streak you can measure.\"\n >\n <NextSteps\n storageKey=\"timecampus-pomodoro\"\n className=\"mb-6\"\n steps={[\n {\n id: \"start\",\n label: \"Start a focus session\",\n description: \"Begin a 25-minute sprint on one task.\",\n onClick: () => handleStart(),\n },\n {\n id: \"analytics\",\n label: \"Review your pomodoro stats\",\n description: \"See streaks, completion rate and focus time.\",\n onClick: () => navigate(\"/pomodoro/analytics\"),\n },\n {\n id: \"settings\",\n label: \"Tune your intervals\",\n description: \"Adjust focus and break lengths to fit you.\",\n onClick: () => navigate(\"/pomodoro/settings\"),\n },\n ]}\n />\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <StatCard\n title=\"Sessions Today\"\n value={String(stats?.completedSessions ?? 0)}\n description={`Total: ${stats?.totalSessions ?? 0} sessions`}\n />\n <StatCard\n title=\"Focus Time\"\n value={`${totalFocusH}h ${totalFocusM}m`}\n trend={{\n value: stats?.completionRate ?? 0,\n label: \"completion rate\",\n }}\n />\n <StatCard\n title=\"Current Streak\"\n value={`${stats?.streakDays ?? 0} days`}\n description={`Avg: ${stats?.averageSessionMinutes ?? 0}m per session`}\n />\n </div>\n\n <div className=\"mx-auto max-w-md\">\n <GlassCard treatment=\"glass\" glow className=\"p-8 text-center\">\n <div className=\"mb-2\">\n <span\n aria-live=\"polite\"\n className=\"inline-flex rounded-full bg-action-primary-bg/10 px-3 py-1 text-xs font-medium text-primary\"\n >\n {phaseLabel}\n </span>\n </div>\n <p className=\"text-7xl font-mono font-bold text-text-primary\">\n {formatTimer(timerSeconds)}\n </p>\n <p className=\"mt-2 text-sm text-text-secondary\">\n {stats\n ? `${stats.completedSessions} of ${stats.totalSessions} sessions`\n : \"No sessions yet\"}\n </p>\n <div className=\"mt-6 flex items-center justify-center gap-3\">\n {!isRunning ? (\n <button\n onClick={handleStart}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-6 py-3 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Play className=\"h-4 w-4\" />\n {startLabel}\n </button>\n ) : (\n <button\n onClick={() => setIsRunning(false)}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-6 py-3 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Pause className=\"h-4 w-4\" />\n Pause\n </button>\n )}\n <button\n onClick={handleComplete}\n disabled={!activeSession}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-3 text-sm font-medium text-text-primary hover:bg-bg-sunken/50 disabled:opacity-50\"\n >\n <SkipForward className=\"h-4 w-4\" />\n Skip\n </button>\n <button\n onClick={() => void handleReset()}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-4 py-3 text-sm font-medium text-text-primary hover:bg-bg-sunken/50\"\n >\n <RotateCcw className=\"h-4 w-4\" />\n Reset\n </button>\n </div>\n </GlassCard>\n </div>\n\n {/* Focus Goals */}\n <GlassCard treatment=\"glass\" className=\"p-6\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h3 className=\"flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Target className=\"h-4 w-4\" />\n Focus Goals\n </h3>\n <button\n onClick={openCreateGoal}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-action-primary-bg px-3 py-1.5 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus className=\"h-4 w-4\" />\n Add Goal\n </button>\n </div>\n {goals.length === 0 ? (\n <p className=\"py-6 text-center text-sm text-text-secondary\">\n No focus goals yet. Set a daily or weekly target to build a\n consistent focus habit.\n </p>\n ) : (\n <div className=\"space-y-3\">\n {goals.map((goal: PomodoroGoalFieldsFragment) => {\n const pct = goal.target\n ? Math.min(100, Math.round((goal.current / goal.target) * 100))\n : 0;\n return (\n <div\n key={goal.id}\n className=\"rounded-lg border border-border-subtle p-4\"\n >\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm font-medium capitalize text-text-primary\">\n {goal.type.toLowerCase()} goal\n </p>\n <p className=\"text-xs text-text-secondary\">\n {goal.current} of {goal.target} sessions\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium text-text-primary\">\n {pct}%\n </span>\n <button\n onClick={() => openEditGoal(goal)}\n className=\"rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-text-primary\"\n aria-label=\"Edit goal\"\n >\n <Pencil className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => setDeletingGoalEntry(goal)}\n className=\"rounded-md p-1.5 text-text-secondary hover:bg-status-error-bg hover:text-status-error-text\"\n aria-label=\"Delete goal\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n <div className=\"mt-3 h-2 w-full rounded-full bg-bg-sunken\">\n <div\n className=\"h-2 rounded-full bg-action-primary-bg\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })}\n </div>\n )}\n </GlassCard>\n\n {/* Goal create/edit modal */}\n <Modal\n open={goalModalOpen}\n onClose={() => setGoalModalOpen(false)}\n title={editingGoal ? \"Edit Focus Goal\" : \"Add Focus Goal\"}\n description=\"Set a target number of focus sessions\"\n footer={\n <>\n <button\n onClick={() => setGoalModalOpen(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={handleSaveGoal}\n disabled={\n creatingGoal || updatingGoal || Number(goalForm.target) <= 0\n }\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 {creatingGoal || updatingGoal\n ? \"Saving...\"\n : editingGoal\n ? \"Save Changes\"\n : \"Add Goal\"}\n </button>\n </>\n }\n >\n {(createGoalError || updateGoalError) && (\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 goal. Please try again.\n </div>\n )}\n <div className=\"space-y-4\">\n <div>\n <label className={labelClasses}>Goal Type</label>\n <select\n value={goalForm.type}\n onChange={(e) => {\n const type = e.target.value as PomodoroGoalType;\n setGoalForm((f) => ({\n ...f,\n type,\n period: type,\n endDate:\n type === PomodoroGoalType.Daily\n ? futureInput(0)\n : type === PomodoroGoalType.Weekly\n ? futureInput(7)\n : futureInput(30),\n }));\n }}\n className={inputClasses}\n >\n <option value={PomodoroGoalType.Daily}>Daily</option>\n <option value={PomodoroGoalType.Weekly}>Weekly</option>\n <option value={PomodoroGoalType.Monthly}>Monthly</option>\n </select>\n </div>\n <div>\n <label className={labelClasses}>Target Sessions</label>\n <input\n type=\"number\"\n min=\"1\"\n placeholder=\"8\"\n value={goalForm.target}\n onChange={(e) => setGoalField(\"target\", e.target.value)}\n className={inputClasses}\n />\n </div>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <div>\n <label className={labelClasses}>Start Date</label>\n <input\n type=\"date\"\n value={goalForm.startDate}\n onChange={(e) => setGoalField(\"startDate\", e.target.value)}\n className={inputClasses}\n />\n </div>\n <div>\n <label className={labelClasses}>End Date</label>\n <input\n type=\"date\"\n value={goalForm.endDate}\n onChange={(e) => setGoalField(\"endDate\", e.target.value)}\n className={inputClasses}\n />\n </div>\n </div>\n </div>\n </Modal>\n\n {/* Goal delete confirm modal */}\n <Modal\n open={deletingGoalEntry !== null}\n onClose={() => setDeletingGoalEntry(null)}\n title=\"Delete Focus Goal\"\n size=\"sm\"\n footer={\n <>\n <button\n onClick={() => setDeletingGoalEntry(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={handleDeleteGoal}\n disabled={deletingGoal}\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 {deletingGoal ? \"Deleting...\" : \"Delete\"}\n </button>\n </>\n }\n >\n {deleteGoalError && (\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 goal. Please try again.\n </div>\n )}\n <p className=\"text-sm text-text-secondary\">\n Are you sure you want to delete this{\" \"}\n {deletingGoalEntry?.type.toLowerCase()} focus goal? This action cannot\n be undone.\n </p>\n </Modal>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"PomodoroSettingsPage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/pomodoro/PomodoroSettingsPage.tsx"],"names":[],"mappings":"AAWA,wBAAgB,oBAAoB,gCAqMnC"}
1
+ {"version":3,"file":"PomodoroSettingsPage.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/pomodoro/PomodoroSettingsPage.tsx"],"names":[],"mappings":"AAYA,wBAAgB,oBAAoB,gCAwMnC"}
@@ -2,172 +2,176 @@ import { useTimeCampus as e } from "../../providers/TimeCampusProvider.js";
2
2
  import { LoadingSkeleton as t } from "../../components/LoadingSkeleton.js";
3
3
  import { useGetPomodoroSettingsQuery as n, useUpdatePomodoroSettingsMutation as r } from "../../../generated/wspace-operations.js";
4
4
  import { PageLayout as i } from "../../components/PageLayout.js";
5
- import { useCallback as a, useEffect as o, useState as s } from "react";
6
- import { Settings as c } from "lucide-react";
7
- import { jsx as l, jsxs as u } from "react/jsx-runtime";
8
- import { GlassCard as d } from "@burdenoff/fe-libs/ui";
5
+ import { minutesToSeconds as a, secondsToMinutes as o } from "./pomodoroDuration.js";
6
+ import { useCallback as s, useEffect as c, useState as l } from "react";
7
+ import { Settings as u } from "lucide-react";
8
+ import { jsx as d, jsxs as f } from "react/jsx-runtime";
9
+ import { GlassCard as p } from "@burdenoff/fe-libs/ui";
9
10
  //#region src/timecampus/pages/pomodoro/PomodoroSettingsPage.tsx
10
- function f() {
11
- let { workspaceId: f = "" } = e(), { data: p, loading: m, error: h } = n({
12
- variables: { workspaceId: f },
13
- skip: !f
14
- }), [g, { loading: _ }] = r(), v = p?.pomodoroSettings, [y, b] = s(25), [x, S] = s(5), [C, w] = s(15), [T, E] = s(!0), [D, O] = s(!0), [k, A] = s(!1);
15
- o(() => {
16
- v && (b(v.focusDuration), S(v.shortBreakDuration), w(v.longBreakDuration), E(v.soundEnabled), O(v.notificationsEnabled), A(v.autoStartBreaks));
17
- }, [v]);
18
- let j = a(async () => {
19
- f && await g({
11
+ function m() {
12
+ let { workspaceId: m = "" } = e(), { data: h, loading: g, error: _ } = n({
13
+ variables: { workspaceId: m },
14
+ skip: !m
15
+ }), [v, { loading: y }] = r(), b = h?.pomodoroSettings, [x, S] = l(25), [C, w] = l(5), [T, E] = l(15), [D, O] = l(!0), [k, A] = l(!0), [j, M] = l(!1);
16
+ c(() => {
17
+ b && (S(o(b.focusDuration)), w(o(b.shortBreakDuration)), E(o(b.longBreakDuration)), O(b.soundEnabled), A(b.notificationsEnabled), M(b.autoStartBreaks));
18
+ }, [b]);
19
+ let N = s(async () => {
20
+ m && await v({
20
21
  variables: { input: {
21
- workspaceId: f,
22
- focusDuration: y,
23
- shortBreakDuration: x,
24
- longBreakDuration: C,
25
- soundEnabled: T,
26
- notificationsEnabled: D,
27
- autoStartBreaks: k
22
+ workspaceId: m,
23
+ focusDuration: a(x),
24
+ shortBreakDuration: a(C),
25
+ longBreakDuration: a(T),
26
+ soundEnabled: D,
27
+ notificationsEnabled: k,
28
+ autoStartBreaks: j
28
29
  } },
29
30
  refetchQueries: ["GetPomodoroSettings"]
30
31
  });
31
32
  }, [
32
- f,
33
- y,
33
+ m,
34
34
  x,
35
35
  C,
36
36
  T,
37
37
  D,
38
38
  k,
39
- g
39
+ j,
40
+ v
40
41
  ]);
41
- return m ? /* @__PURE__ */ l(i, {
42
+ return g ? /* @__PURE__ */ d(i, {
42
43
  title: "Pomodoro Settings",
43
44
  description: "Configure your Pomodoro timer preferences",
44
- children: /* @__PURE__ */ l(t, { variant: "detail" })
45
- }) : h ? /* @__PURE__ */ l(i, {
45
+ children: /* @__PURE__ */ d(t, { variant: "detail" })
46
+ }) : _ ? /* @__PURE__ */ d(i, {
46
47
  title: "Pomodoro Settings",
47
48
  description: "Configure your Pomodoro timer preferences",
48
- children: /* @__PURE__ */ l("div", {
49
+ children: /* @__PURE__ */ d("div", {
49
50
  className: "rounded-lg border border-status-error-bg/40 bg-status-error-bg p-4 text-sm text-status-error-text",
50
51
  children: "Failed to load settings. Please try again later."
51
52
  })
52
- }) : /* @__PURE__ */ l(i, {
53
+ }) : /* @__PURE__ */ d(i, {
53
54
  title: "Pomodoro Settings",
54
55
  description: "Configure your Pomodoro timer preferences",
55
56
  purpose: "Shape the Pomodoro technique around how you actually work — set focus and break lengths and decide how you're nudged — so the timer fits your rhythm instead of fighting it.",
56
- children: /* @__PURE__ */ u("div", {
57
+ children: /* @__PURE__ */ f("div", {
57
58
  className: "mx-auto max-w-2xl space-y-6",
58
59
  children: [
59
- /* @__PURE__ */ u(d, {
60
+ /* @__PURE__ */ f(p, {
60
61
  treatment: "glass",
61
62
  glow: !0,
62
63
  className: "p-6",
63
- children: [/* @__PURE__ */ u("h3", {
64
+ children: [/* @__PURE__ */ f("h3", {
64
65
  className: "mb-4 flex items-center gap-2 text-sm font-semibold text-text-primary",
65
- children: [/* @__PURE__ */ l(c, { className: "h-4 w-4" }), "Timer Durations"]
66
- }), /* @__PURE__ */ u("div", {
66
+ children: [/* @__PURE__ */ d(u, { className: "h-4 w-4" }), "Timer Durations"]
67
+ }), /* @__PURE__ */ f("div", {
67
68
  className: "space-y-4",
68
69
  children: [
69
- /* @__PURE__ */ u("div", {
70
+ /* @__PURE__ */ f("div", {
70
71
  className: "flex items-center justify-between",
71
- children: [/* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("p", {
72
+ children: [/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("p", {
72
73
  className: "text-sm font-medium text-text-primary",
73
74
  children: "Focus Duration"
74
- }), /* @__PURE__ */ l("p", {
75
+ }), /* @__PURE__ */ d("p", {
75
76
  className: "text-xs text-text-secondary",
76
77
  children: "Length of each focus session"
77
- })] }), /* @__PURE__ */ l("input", {
78
+ })] }), /* @__PURE__ */ d("input", {
78
79
  type: "number",
79
- value: y,
80
- onChange: (e) => b(Number(e.target.value)),
80
+ min: "1",
81
+ value: x,
82
+ onChange: (e) => S(Number(e.target.value)),
81
83
  className: "w-20 rounded-md border border-border-subtle bg-bg-surface px-3 py-1.5 text-sm text-text-primary text-right"
82
84
  })]
83
85
  }),
84
- /* @__PURE__ */ u("div", {
86
+ /* @__PURE__ */ f("div", {
85
87
  className: "flex items-center justify-between",
86
- children: [/* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("p", {
88
+ children: [/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("p", {
87
89
  className: "text-sm font-medium text-text-primary",
88
90
  children: "Short Break"
89
- }), /* @__PURE__ */ l("p", {
91
+ }), /* @__PURE__ */ d("p", {
90
92
  className: "text-xs text-text-secondary",
91
93
  children: "Break between focus sessions"
92
- })] }), /* @__PURE__ */ l("input", {
94
+ })] }), /* @__PURE__ */ d("input", {
93
95
  type: "number",
94
- value: x,
95
- onChange: (e) => S(Number(e.target.value)),
96
+ min: "1",
97
+ value: C,
98
+ onChange: (e) => w(Number(e.target.value)),
96
99
  className: "w-20 rounded-md border border-border-subtle bg-bg-surface px-3 py-1.5 text-sm text-text-primary text-right"
97
100
  })]
98
101
  }),
99
- /* @__PURE__ */ u("div", {
102
+ /* @__PURE__ */ f("div", {
100
103
  className: "flex items-center justify-between",
101
- children: [/* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("p", {
104
+ children: [/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("p", {
102
105
  className: "text-sm font-medium text-text-primary",
103
106
  children: "Long Break"
104
- }), /* @__PURE__ */ l("p", {
107
+ }), /* @__PURE__ */ d("p", {
105
108
  className: "text-xs text-text-secondary",
106
109
  children: "After every 4 focus sessions"
107
- })] }), /* @__PURE__ */ l("input", {
110
+ })] }), /* @__PURE__ */ d("input", {
108
111
  type: "number",
109
- value: C,
110
- onChange: (e) => w(Number(e.target.value)),
112
+ min: "1",
113
+ value: T,
114
+ onChange: (e) => E(Number(e.target.value)),
111
115
  className: "w-20 rounded-md border border-border-subtle bg-bg-surface px-3 py-1.5 text-sm text-text-primary text-right"
112
116
  })]
113
117
  })
114
118
  ]
115
119
  })]
116
120
  }),
117
- /* @__PURE__ */ u("div", {
121
+ /* @__PURE__ */ f("div", {
118
122
  className: "rounded-xl border border-border-seam bg-bg-surface shadow-elevation-1 p-6",
119
- children: [/* @__PURE__ */ l("h3", {
123
+ children: [/* @__PURE__ */ d("h3", {
120
124
  className: "mb-4 text-sm font-semibold text-text-primary",
121
125
  children: "Notifications"
122
- }), /* @__PURE__ */ u("div", {
126
+ }), /* @__PURE__ */ f("div", {
123
127
  className: "space-y-3",
124
128
  children: [
125
- /* @__PURE__ */ u("label", {
129
+ /* @__PURE__ */ f("label", {
126
130
  className: "flex items-center justify-between",
127
- children: [/* @__PURE__ */ l("span", {
131
+ children: [/* @__PURE__ */ d("span", {
128
132
  className: "text-sm text-text-primary",
129
133
  children: "Sound alerts"
130
- }), /* @__PURE__ */ l("input", {
134
+ }), /* @__PURE__ */ d("input", {
131
135
  type: "checkbox",
132
- checked: T,
133
- onChange: (e) => E(e.target.checked),
136
+ checked: D,
137
+ onChange: (e) => O(e.target.checked),
134
138
  className: "h-4 w-4 rounded border-border-subtle"
135
139
  })]
136
140
  }),
137
- /* @__PURE__ */ u("label", {
141
+ /* @__PURE__ */ f("label", {
138
142
  className: "flex items-center justify-between",
139
- children: [/* @__PURE__ */ l("span", {
143
+ children: [/* @__PURE__ */ d("span", {
140
144
  className: "text-sm text-text-primary",
141
145
  children: "Desktop notifications"
142
- }), /* @__PURE__ */ l("input", {
146
+ }), /* @__PURE__ */ d("input", {
143
147
  type: "checkbox",
144
- checked: D,
145
- onChange: (e) => O(e.target.checked),
148
+ checked: k,
149
+ onChange: (e) => A(e.target.checked),
146
150
  className: "h-4 w-4 rounded border-border-subtle"
147
151
  })]
148
152
  }),
149
- /* @__PURE__ */ u("label", {
153
+ /* @__PURE__ */ f("label", {
150
154
  className: "flex items-center justify-between",
151
- children: [/* @__PURE__ */ l("span", {
155
+ children: [/* @__PURE__ */ d("span", {
152
156
  className: "text-sm text-text-primary",
153
157
  children: "Auto-start breaks"
154
- }), /* @__PURE__ */ l("input", {
158
+ }), /* @__PURE__ */ d("input", {
155
159
  type: "checkbox",
156
- checked: k,
157
- onChange: (e) => A(e.target.checked),
160
+ checked: j,
161
+ onChange: (e) => M(e.target.checked),
158
162
  className: "h-4 w-4 rounded border-border-subtle"
159
163
  })]
160
164
  })
161
165
  ]
162
166
  })]
163
167
  }),
164
- /* @__PURE__ */ l("div", {
168
+ /* @__PURE__ */ d("div", {
165
169
  className: "flex justify-end",
166
- children: /* @__PURE__ */ l("button", {
167
- onClick: j,
168
- disabled: _,
170
+ children: /* @__PURE__ */ d("button", {
171
+ onClick: N,
172
+ disabled: y,
169
173
  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",
170
- children: _ ? "Saving..." : "Save Settings"
174
+ children: y ? "Saving..." : "Save Settings"
171
175
  })
172
176
  })
173
177
  ]
@@ -175,6 +179,6 @@ function f() {
175
179
  });
176
180
  }
177
181
  //#endregion
178
- export { f as PomodoroSettingsPage };
182
+ export { m as PomodoroSettingsPage };
179
183
 
180
184
  //# sourceMappingURL=PomodoroSettingsPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PomodoroSettingsPage.js","names":[],"sources":["../../../../src/timecampus/pages/pomodoro/PomodoroSettingsPage.tsx"],"sourcesContent":["import { useState, useEffect, useCallback } from \"react\";\nimport { Settings } from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useGetPomodoroSettingsQuery,\n useUpdatePomodoroSettingsMutation,\n} from \"@/generated/wspace-operations\";\n\nexport function PomodoroSettingsPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const { data, loading, error } = useGetPomodoroSettingsQuery({\n variables: { workspaceId },\n skip: !workspaceId,\n });\n\n const [updateSettings, { loading: saving }] =\n useUpdatePomodoroSettingsMutation();\n\n const settings = data?.pomodoroSettings;\n\n const [focusDuration, setFocusDuration] = useState(25);\n const [shortBreak, setShortBreak] = useState(5);\n const [longBreak, setLongBreak] = useState(15);\n const [soundEnabled, setSoundEnabled] = useState(true);\n const [notificationsEnabled, setNotificationsEnabled] = useState(true);\n const [autoStartBreaks, setAutoStartBreaks] = useState(false);\n\n useEffect(() => {\n if (settings) {\n setFocusDuration(settings.focusDuration);\n setShortBreak(settings.shortBreakDuration);\n setLongBreak(settings.longBreakDuration);\n setSoundEnabled(settings.soundEnabled);\n setNotificationsEnabled(settings.notificationsEnabled);\n setAutoStartBreaks(settings.autoStartBreaks);\n }\n }, [settings]);\n\n const handleSave = useCallback(async () => {\n if (!workspaceId) return;\n await updateSettings({\n variables: {\n input: {\n workspaceId,\n focusDuration,\n shortBreakDuration: shortBreak,\n longBreakDuration: longBreak,\n soundEnabled,\n notificationsEnabled,\n autoStartBreaks,\n },\n },\n refetchQueries: [\"GetPomodoroSettings\"],\n });\n }, [\n workspaceId,\n focusDuration,\n shortBreak,\n longBreak,\n soundEnabled,\n notificationsEnabled,\n autoStartBreaks,\n updateSettings,\n ]);\n\n if (loading) {\n return (\n <PageLayout\n title=\"Pomodoro Settings\"\n description=\"Configure your Pomodoro timer preferences\"\n >\n <LoadingSkeleton variant=\"detail\" />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"Pomodoro Settings\"\n description=\"Configure your Pomodoro timer preferences\"\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 settings. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Pomodoro Settings\"\n description=\"Configure your Pomodoro timer preferences\"\n purpose=\"Shape the Pomodoro technique around how you actually work — set focus and break lengths and decide how you're nudged — so the timer fits your rhythm instead of fighting it.\"\n >\n <div className=\"mx-auto max-w-2xl space-y-6\">\n <GlassCard treatment=\"glass\" glow className=\"p-6\">\n <h3 className=\"mb-4 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Settings className=\"h-4 w-4\" />\n Timer Durations\n </h3>\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n Focus Duration\n </p>\n <p className=\"text-xs text-text-secondary\">\n Length of each focus session\n </p>\n </div>\n <input\n type=\"number\"\n value={focusDuration}\n onChange={(e) => setFocusDuration(Number(e.target.value))}\n className=\"w-20 rounded-md border border-border-subtle bg-bg-surface px-3 py-1.5 text-sm text-text-primary text-right\"\n />\n </div>\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n Short Break\n </p>\n <p className=\"text-xs text-text-secondary\">\n Break between focus sessions\n </p>\n </div>\n <input\n type=\"number\"\n value={shortBreak}\n onChange={(e) => setShortBreak(Number(e.target.value))}\n className=\"w-20 rounded-md border border-border-subtle bg-bg-surface px-3 py-1.5 text-sm text-text-primary text-right\"\n />\n </div>\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n Long Break\n </p>\n <p className=\"text-xs text-text-secondary\">\n After every 4 focus sessions\n </p>\n </div>\n <input\n type=\"number\"\n value={longBreak}\n onChange={(e) => setLongBreak(Number(e.target.value))}\n className=\"w-20 rounded-md border border-border-subtle bg-bg-surface px-3 py-1.5 text-sm text-text-primary text-right\"\n />\n </div>\n </div>\n </GlassCard>\n\n <div className=\"rounded-xl border border-border-seam bg-bg-surface shadow-elevation-1 p-6\">\n <h3 className=\"mb-4 text-sm font-semibold text-text-primary\">\n Notifications\n </h3>\n <div className=\"space-y-3\">\n <label className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-primary\">Sound alerts</span>\n <input\n type=\"checkbox\"\n checked={soundEnabled}\n onChange={(e) => setSoundEnabled(e.target.checked)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n </label>\n <label className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-primary\">\n Desktop notifications\n </span>\n <input\n type=\"checkbox\"\n checked={notificationsEnabled}\n onChange={(e) => setNotificationsEnabled(e.target.checked)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n </label>\n <label className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-primary\">\n Auto-start breaks\n </span>\n <input\n type=\"checkbox\"\n checked={autoStartBreaks}\n onChange={(e) => setAutoStartBreaks(e.target.checked)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n </label>\n </div>\n </div>\n\n <div className=\"flex justify-end\">\n <button\n onClick={handleSave}\n disabled={saving}\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 {saving ? \"Saving...\" : \"Save Settings\"}\n </button>\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"PomodoroSettingsPage.js","names":[],"sources":["../../../../src/timecampus/pages/pomodoro/PomodoroSettingsPage.tsx"],"sourcesContent":["import { useState, useEffect, useCallback } from \"react\";\nimport { Settings } from \"lucide-react\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../../components/PageLayout\";\nimport { LoadingSkeleton } from \"../../components/LoadingSkeleton\";\nimport { useTimeCampus } from \"../../providers/TimeCampusProvider\";\nimport {\n useGetPomodoroSettingsQuery,\n useUpdatePomodoroSettingsMutation,\n} from \"@/generated/wspace-operations\";\nimport { minutesToSeconds, secondsToMinutes } from \"./pomodoroDuration\";\n\nexport function PomodoroSettingsPage() {\n const { workspaceId = \"\" } = useTimeCampus();\n\n const { data, loading, error } = useGetPomodoroSettingsQuery({\n variables: { workspaceId },\n skip: !workspaceId,\n });\n\n const [updateSettings, { loading: saving }] =\n useUpdatePomodoroSettingsMutation();\n\n const settings = data?.pomodoroSettings;\n\n const [focusDuration, setFocusDuration] = useState(25);\n const [shortBreak, setShortBreak] = useState(5);\n const [longBreak, setLongBreak] = useState(15);\n const [soundEnabled, setSoundEnabled] = useState(true);\n const [notificationsEnabled, setNotificationsEnabled] = useState(true);\n const [autoStartBreaks, setAutoStartBreaks] = useState(false);\n\n useEffect(() => {\n if (settings) {\n setFocusDuration(secondsToMinutes(settings.focusDuration));\n setShortBreak(secondsToMinutes(settings.shortBreakDuration));\n setLongBreak(secondsToMinutes(settings.longBreakDuration));\n setSoundEnabled(settings.soundEnabled);\n setNotificationsEnabled(settings.notificationsEnabled);\n setAutoStartBreaks(settings.autoStartBreaks);\n }\n }, [settings]);\n\n const handleSave = useCallback(async () => {\n if (!workspaceId) return;\n await updateSettings({\n variables: {\n input: {\n workspaceId,\n focusDuration: minutesToSeconds(focusDuration),\n shortBreakDuration: minutesToSeconds(shortBreak),\n longBreakDuration: minutesToSeconds(longBreak),\n soundEnabled,\n notificationsEnabled,\n autoStartBreaks,\n },\n },\n refetchQueries: [\"GetPomodoroSettings\"],\n });\n }, [\n workspaceId,\n focusDuration,\n shortBreak,\n longBreak,\n soundEnabled,\n notificationsEnabled,\n autoStartBreaks,\n updateSettings,\n ]);\n\n if (loading) {\n return (\n <PageLayout\n title=\"Pomodoro Settings\"\n description=\"Configure your Pomodoro timer preferences\"\n >\n <LoadingSkeleton variant=\"detail\" />\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title=\"Pomodoro Settings\"\n description=\"Configure your Pomodoro timer preferences\"\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 settings. Please try again later.\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Pomodoro Settings\"\n description=\"Configure your Pomodoro timer preferences\"\n purpose=\"Shape the Pomodoro technique around how you actually work — set focus and break lengths and decide how you're nudged — so the timer fits your rhythm instead of fighting it.\"\n >\n <div className=\"mx-auto max-w-2xl space-y-6\">\n <GlassCard treatment=\"glass\" glow className=\"p-6\">\n <h3 className=\"mb-4 flex items-center gap-2 text-sm font-semibold text-text-primary\">\n <Settings className=\"h-4 w-4\" />\n Timer Durations\n </h3>\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n Focus Duration\n </p>\n <p className=\"text-xs text-text-secondary\">\n Length of each focus session\n </p>\n </div>\n <input\n type=\"number\"\n min=\"1\"\n value={focusDuration}\n onChange={(e) => setFocusDuration(Number(e.target.value))}\n className=\"w-20 rounded-md border border-border-subtle bg-bg-surface px-3 py-1.5 text-sm text-text-primary text-right\"\n />\n </div>\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n Short Break\n </p>\n <p className=\"text-xs text-text-secondary\">\n Break between focus sessions\n </p>\n </div>\n <input\n type=\"number\"\n min=\"1\"\n value={shortBreak}\n onChange={(e) => setShortBreak(Number(e.target.value))}\n className=\"w-20 rounded-md border border-border-subtle bg-bg-surface px-3 py-1.5 text-sm text-text-primary text-right\"\n />\n </div>\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n Long Break\n </p>\n <p className=\"text-xs text-text-secondary\">\n After every 4 focus sessions\n </p>\n </div>\n <input\n type=\"number\"\n min=\"1\"\n value={longBreak}\n onChange={(e) => setLongBreak(Number(e.target.value))}\n className=\"w-20 rounded-md border border-border-subtle bg-bg-surface px-3 py-1.5 text-sm text-text-primary text-right\"\n />\n </div>\n </div>\n </GlassCard>\n\n <div className=\"rounded-xl border border-border-seam bg-bg-surface shadow-elevation-1 p-6\">\n <h3 className=\"mb-4 text-sm font-semibold text-text-primary\">\n Notifications\n </h3>\n <div className=\"space-y-3\">\n <label className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-primary\">Sound alerts</span>\n <input\n type=\"checkbox\"\n checked={soundEnabled}\n onChange={(e) => setSoundEnabled(e.target.checked)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n </label>\n <label className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-primary\">\n Desktop notifications\n </span>\n <input\n type=\"checkbox\"\n checked={notificationsEnabled}\n onChange={(e) => setNotificationsEnabled(e.target.checked)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n </label>\n <label className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-primary\">\n Auto-start breaks\n </span>\n <input\n type=\"checkbox\"\n checked={autoStartBreaks}\n onChange={(e) => setAutoStartBreaks(e.target.checked)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n </label>\n </div>\n </div>\n\n <div className=\"flex justify-end\">\n <button\n onClick={handleSave}\n disabled={saving}\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 {saving ? \"Saving...\" : \"Save Settings\"}\n </button>\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { PomodoroSessionType } from '../../../generated/wspace-types';
2
+ export interface PomodoroDurations {
3
+ focusDuration: number;
4
+ shortBreakDuration: number;
5
+ longBreakDuration: number;
6
+ }
7
+ export declare function secondsToMinutes(seconds: number): number;
8
+ export declare function minutesToSeconds(minutes: number): number;
9
+ export declare function durationForSession(type: PomodoroSessionType, settings?: PomodoroDurations | null): number;
10
+ export declare function nextSessionAfter(completedType: PomodoroSessionType, completedFocusSessions: number, sessionsBeforeLongBreak: number): PomodoroSessionType;
11
+ //# sourceMappingURL=pomodoroDuration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pomodoroDuration.d.ts","sourceRoot":"","sources":["../../../../src/timecampus/pages/pomodoro/pomodoroDuration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAEtE,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,mBAAmB,EACzB,QAAQ,CAAC,EAAE,iBAAiB,GAAG,IAAI,GAClC,MAAM,CAQR;AAED,wBAAgB,gBAAgB,CAC9B,aAAa,EAAE,mBAAmB,EAClC,sBAAsB,EAAE,MAAM,EAC9B,uBAAuB,EAAE,MAAM,GAC9B,mBAAmB,CAQrB"}
@@ -0,0 +1,18 @@
1
+ import { PomodoroSessionType as e } from "../../../generated/wspace-types.js";
2
+ //#region src/timecampus/pages/pomodoro/pomodoroDuration.ts
3
+ function t(e) {
4
+ return Math.max(1, Math.round(e / 60));
5
+ }
6
+ function n(e) {
7
+ return Math.max(1, Math.round(e)) * 60;
8
+ }
9
+ function r(t, n) {
10
+ return t === e.ShortBreak ? n?.shortBreakDuration ?? 300 : t === e.LongBreak ? n?.longBreakDuration ?? 900 : n?.focusDuration ?? 1500;
11
+ }
12
+ function i(t, n, r) {
13
+ return t === e.Focus ? n % Math.max(1, r) === 0 ? e.LongBreak : e.ShortBreak : e.Focus;
14
+ }
15
+ //#endregion
16
+ export { r as durationForSession, n as minutesToSeconds, i as nextSessionAfter, t as secondsToMinutes };
17
+
18
+ //# sourceMappingURL=pomodoroDuration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pomodoroDuration.js","names":[],"sources":["../../../../src/timecampus/pages/pomodoro/pomodoroDuration.ts"],"sourcesContent":["import { PomodoroSessionType } from \"../../../generated/wspace-types\";\n\nexport interface PomodoroDurations {\n focusDuration: number;\n shortBreakDuration: number;\n longBreakDuration: number;\n}\n\nexport function secondsToMinutes(seconds: number): number {\n return Math.max(1, Math.round(seconds / 60));\n}\n\nexport function minutesToSeconds(minutes: number): number {\n return Math.max(1, Math.round(minutes)) * 60;\n}\n\nexport function durationForSession(\n type: PomodoroSessionType,\n settings?: PomodoroDurations | null,\n): number {\n if (type === PomodoroSessionType.ShortBreak) {\n return settings?.shortBreakDuration ?? 5 * 60;\n }\n if (type === PomodoroSessionType.LongBreak) {\n return settings?.longBreakDuration ?? 15 * 60;\n }\n return settings?.focusDuration ?? 25 * 60;\n}\n\nexport function nextSessionAfter(\n completedType: PomodoroSessionType,\n completedFocusSessions: number,\n sessionsBeforeLongBreak: number,\n): PomodoroSessionType {\n if (completedType !== PomodoroSessionType.Focus) {\n return PomodoroSessionType.Focus;\n }\n const cadence = Math.max(1, sessionsBeforeLongBreak);\n return completedFocusSessions % cadence === 0\n ? PomodoroSessionType.LongBreak\n : PomodoroSessionType.ShortBreak;\n}\n"],"mappings":";;AAQA,SAAgB,EAAiB,GAAyB;CACxD,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,IAAU,EAAE,CAAC;AAC7C;AAEA,SAAgB,EAAiB,GAAyB;CACxD,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,CAAO,CAAC,IAAI;AAC5C;AAEA,SAAgB,EACd,GACA,GACQ;CAOR,OANI,MAAS,EAAoB,aACxB,GAAU,sBAAsB,MAErC,MAAS,EAAoB,YACxB,GAAU,qBAAqB,MAEjC,GAAU,iBAAiB;AACpC;AAEA,SAAgB,EACd,GACA,GACA,GACqB;CAKrB,OAJI,MAAkB,EAAoB,QAInC,IADS,KAAK,IAAI,GAAG,CACI,MAAY,IACxC,EAAoB,YACpB,EAAoB,aALf,EAAoB;AAM/B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-timecampus",
3
- "version": "2026.820.3",
3
+ "version": "2026.825.2",
4
4
  "description": "Burdenoff TimeCampus Microfrontend - AI-powered time management and productivity platform",
5
5
  "type": "module",
6
6
  "files": [
@@ -26,7 +26,7 @@
26
26
  "types": "./dist/index.d.ts",
27
27
  "scripts": {
28
28
  "dev": "vite",
29
- "build": "tsc && bash scripts/build-limits.sh --gate vite build",
29
+ "build": "tsc && bash scripts/build-limits.sh --gate bash -c 'vite build && bun run verify:dist'",
30
30
  "lint": "eslint .",
31
31
  "lint:sanity": "if [ \"${CI:-}\" = \"true\" ]; then bun run lint; else bun run lint -- --cache --cache-location .eslintcache; fi",
32
32
  "format": "prettier --write \"src/**/*.{ts,tsx}\"",
@@ -46,7 +46,8 @@
46
46
  "switch:remote": "bun scripts/deps-mode.ts remote",
47
47
  "switch:local": "bun scripts/deps-mode.ts local",
48
48
  "switch:check": "bun scripts/deps-mode.ts check",
49
- "format:check": "prettier --check \"src/**/*.{ts,tsx}\""
49
+ "format:check": "prettier --check \"src/**/*.{ts,tsx}\"",
50
+ "verify:dist": "bun scripts/verify-dist-apollo-hooks.ts"
50
51
  },
51
52
  "dependencies": {
52
53
  "@apollo/client": "^4.2.12",