diff --git a/packages/frontend/src/components/account-dialog.tsx b/packages/frontend/src/components/account-dialog.tsx index 45f130c..fdaff60 100644 --- a/packages/frontend/src/components/account-dialog.tsx +++ b/packages/frontend/src/components/account-dialog.tsx @@ -7,7 +7,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from import { Field } from "@/components/ui/field"; import { Label } from "@/components/ui/label"; import { UploadAvatar } from "@/components/upload-avatar"; -import { user } from "@/lib/server"; +import { parseError, user } from "@/lib/server"; function AccountDialog({ trigger }: { trigger?: ReactNode }) { const { user: currentUser, setUser } = useAuthenticatedSession(); @@ -41,9 +41,8 @@ function AccountDialog({ trigger }: { trigger?: ReactNode }) { } await user.update({ - id: currentUser.id, name: name.trim(), - password: password.trim(), + password: password.trim() || undefined, avatarURL, onSuccess: (data) => { setError(""); @@ -55,10 +54,11 @@ function AccountDialog({ trigger }: { trigger?: ReactNode }) { dismissible: false, }); }, - onError: (error) => { - setError(error); + onError: (err) => { + const message = parseError(err); + setError(message); - toast.error(`Error updating account: ${error}`, { + toast.error(`Error updating account: ${message}`, { dismissible: false, }); }, diff --git a/packages/frontend/src/components/add-member-dialog.tsx b/packages/frontend/src/components/add-member-dialog.tsx index 5752d91..69fd8b5 100644 --- a/packages/frontend/src/components/add-member-dialog.tsx +++ b/packages/frontend/src/components/add-member-dialog.tsx @@ -11,7 +11,7 @@ import { DialogTrigger, } from "@/components/ui/dialog"; import { Field } from "@/components/ui/field"; -import { organisation, user } from "@/lib/server"; +import { organisation, parseError, user } from "@/lib/server"; export function AddMemberDialog({ organisationId, @@ -68,11 +68,12 @@ export function AddMemberDialog({ userData = data; userId = data.id; }, - onError: (error) => { - setError(error || "user not found"); + onError: (err) => { + const message = parseError(err); + setError(message || "user not found"); setSubmitting(false); - toast.error(`Error adding member: ${error}`, { + toast.error(`Error adding member: ${message}`, { dismissible: false, }); }, @@ -95,11 +96,12 @@ export function AddMemberDialog({ console.error(actionErr); } }, - onError: (error) => { - setError(error || "failed to add member"); + onError: (err) => { + const message = parseError(err); + setError(message || "failed to add member"); setSubmitting(false); - toast.error(`Error adding member: ${error}`, { + toast.error(`Error adding member: ${message}`, { dismissible: false, }); }, diff --git a/packages/frontend/src/components/create-issue.tsx b/packages/frontend/src/components/create-issue.tsx index 95b6655..18544cf 100644 --- a/packages/frontend/src/components/create-issue.tsx +++ b/packages/frontend/src/components/create-issue.tsx @@ -23,7 +23,7 @@ import { Field } from "@/components/ui/field"; import { Label } from "@/components/ui/label"; import { SelectTrigger } from "@/components/ui/select"; import { UserSelect } from "@/components/user-select"; -import { issue } from "@/lib/server"; +import { issue, parseError } from "@/lib/server"; import { cn } from "@/lib/utils"; import { SprintSelect } from "./sprint-select"; @@ -116,16 +116,17 @@ export function CreateIssue({ console.error(actionErr); } }, - onError: async (error) => { - setError(error); + onError: async (err) => { + const message = parseError(err); + setError(message); setSubmitting(false); - toast.error(`Error creating issue: ${error}`, { + toast.error(`Error creating issue: ${message}`, { dismissible: false, }); try { - await errorAction?.(error); + await errorAction?.(message); } catch (actionErr) { console.error(actionErr); } diff --git a/packages/frontend/src/components/create-organisation.tsx b/packages/frontend/src/components/create-organisation.tsx index 3041ed5..0746216 100644 --- a/packages/frontend/src/components/create-organisation.tsx +++ b/packages/frontend/src/components/create-organisation.tsx @@ -17,7 +17,7 @@ import { } from "@/components/ui/dialog"; import { Field } from "@/components/ui/field"; import { Label } from "@/components/ui/label"; -import { organisation } from "@/lib/server"; +import { organisation, parseError } from "@/lib/server"; import { cn } from "@/lib/utils"; const slugify = (value: string) => @@ -85,7 +85,6 @@ export function CreateOrganisation({ name, slug, description, - userId: user.id, onSuccess: async (data) => { setOpen(false); reset(); @@ -96,10 +95,11 @@ export function CreateOrganisation({ } }, onError: async (err) => { - setError(err || "failed to create organisation"); + const message = parseError(err); + setError(message || "failed to create organisation"); setSubmitting(false); try { - await errorAction?.(err || "failed to create organisation"); + await errorAction?.(message || "failed to create organisation"); } catch (actionErr) { console.error(actionErr); } diff --git a/packages/frontend/src/components/create-project.tsx b/packages/frontend/src/components/create-project.tsx index 97b09ad..3bc211f 100644 --- a/packages/frontend/src/components/create-project.tsx +++ b/packages/frontend/src/components/create-project.tsx @@ -13,7 +13,7 @@ import { } from "@/components/ui/dialog"; import { Field } from "@/components/ui/field"; import { Label } from "@/components/ui/label"; -import { project } from "@/lib/server"; +import { parseError, project } from "@/lib/server"; import { cn } from "@/lib/utils"; const keyify = (value: string) => @@ -86,24 +86,24 @@ export function CreateProject({ await project.create({ key, name, - creatorId: user.id, organisationId, onSuccess: async (data) => { - const project = data as ProjectRecord; + const proj = data as ProjectRecord; setOpen(false); reset(); try { - await completeAction?.(project); + await completeAction?.(proj); } catch (actionErr) { console.error(actionErr); } }, - onError: (error) => { - setError(error); + onError: (err) => { + const message = parseError(err); + setError(message); setSubmitting(false); - toast.error(`Error creating project: ${error}`, { + toast.error(`Error creating project: ${message}`, { dismissible: false, }); }, diff --git a/packages/frontend/src/components/create-sprint.tsx b/packages/frontend/src/components/create-sprint.tsx index aa708ac..9dc29ad 100644 --- a/packages/frontend/src/components/create-sprint.tsx +++ b/packages/frontend/src/components/create-sprint.tsx @@ -16,7 +16,7 @@ import { import { Field } from "@/components/ui/field"; import { Label } from "@/components/ui/label"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; -import { sprint } from "@/lib/server"; +import { parseError, sprint } from "@/lib/server"; import { cn } from "@/lib/utils"; const SPRINT_NAME_MAX_LENGTH = 64; @@ -135,11 +135,12 @@ export function CreateSprint({ console.error(actionErr); } }, - onError: (error) => { - setError(error); + onError: (err) => { + const message = parseError(err); + setError(message); setSubmitting(false); - toast.error(`Error creating sprint: ${error}`, { + toast.error(`Error creating sprint: ${message}`, { dismissible: false, }); }, diff --git a/packages/frontend/src/components/issue-timer.tsx b/packages/frontend/src/components/issue-timer.tsx index ca65814..1cc9f5b 100644 --- a/packages/frontend/src/components/issue-timer.tsx +++ b/packages/frontend/src/components/issue-timer.tsx @@ -1,7 +1,7 @@ import type { TimerState } from "@issue/shared"; import { useEffect, useState } from "react"; import { Button } from "@/components/ui/button"; -import { timer } from "@/lib/server"; +import { parseError, timer } from "@/lib/server"; import { cn, formatTime } from "@/lib/utils"; export function IssueTimer({ issueId, onEnd }: { issueId: number; onEnd?: (data: TimerState) => void }) { @@ -19,7 +19,7 @@ export function IssueTimer({ issueId, onEnd }: { issueId: number; onEnd?: (data: setDisplayTime(data.workTimeMs); } }, - onError: setError, + onError: (err) => setError(parseError(err)), }); }, [issueId]); @@ -41,11 +41,13 @@ export function IssueTimer({ issueId, onEnd }: { issueId: number; onEnd?: (data: timer.toggle({ issueId, onSuccess: (data) => { - setTimerState(data); - setDisplayTime(data.workTimeMs); + if (data) { + setTimerState(data); + setDisplayTime(data.workTimeMs); + } setError(null); }, - onError: setError, + onError: (err) => setError(parseError(err)), }); }; @@ -53,12 +55,14 @@ export function IssueTimer({ issueId, onEnd }: { issueId: number; onEnd?: (data: timer.end({ issueId, onSuccess: (data) => { - setTimerState(data); - setDisplayTime(data.workTimeMs); + if (data) { + setTimerState(data); + setDisplayTime(data.workTimeMs); + onEnd?.(data); + } setError(null); - onEnd?.(data); }, - onError: setError, + onError: (err) => setError(parseError(err)), }); }; diff --git a/packages/frontend/src/components/timer-display.tsx b/packages/frontend/src/components/timer-display.tsx index ef25fa6..8e61afd 100644 --- a/packages/frontend/src/components/timer-display.tsx +++ b/packages/frontend/src/components/timer-display.tsx @@ -1,7 +1,7 @@ import type { TimerState } from "@issue/shared"; import { useEffect, useState } from "react"; import { toast } from "sonner"; -import { timer } from "@/lib/server"; +import { parseError, timer } from "@/lib/server"; import { formatTime } from "@/lib/utils"; const FALLBACK_TIME = "--:--:--"; @@ -25,11 +25,12 @@ export function TimerDisplay({ issueId }: { issueId: number }) { setWorkTimeMs(data?.workTimeMs ?? 0); setError(null); }, - onError: (error) => { + onError: (err) => { if (!isMounted) return; - setError(error); + const message = parseError(err); + setError(message); - toast.error(`Error fetching timer data: ${error}`, { + toast.error(`Error fetching timer data: ${message}`, { dismissible: false, }); }, @@ -39,19 +40,19 @@ export function TimerDisplay({ issueId }: { issueId: number }) { issueId, onSuccess: (data) => { if (!isMounted) return; - const sessions = (data ?? []) as TimerState[]; - const totalWorkTime = sessions.reduce( + const totalWorkTime = data.reduce( (total, session) => total + (session?.workTimeMs ?? 0), 0, ); setInactiveWorkTimeMs(totalWorkTime); setError(null); }, - onError: (error) => { + onError: (err) => { if (!isMounted) return; - setError(error); + const message = parseError(err); + setError(message); - toast.error(`Error fetching timer data: ${error}`, { + toast.error(`Error fetching timer data: ${message}`, { dismissible: false, }); }, diff --git a/packages/frontend/src/components/upload-avatar.tsx b/packages/frontend/src/components/upload-avatar.tsx index 79396ae..ab629d3 100644 --- a/packages/frontend/src/components/upload-avatar.tsx +++ b/packages/frontend/src/components/upload-avatar.tsx @@ -4,7 +4,7 @@ import { toast } from "sonner"; import Avatar from "@/components/avatar"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; -import { user } from "@/lib/server"; +import { parseError, user } from "@/lib/server"; import { cn } from "@/lib/utils"; export function UploadAvatar({ @@ -49,11 +49,12 @@ export function UploadAvatar({ }, ); }, - onError: (error) => { - setError(error); + onError: (err) => { + const message = parseError(err); + setError(message); setUploading(false); - toast.error(`Error uploading avatar: ${error}`, { + toast.error(`Error uploading avatar: ${message}`, { dismissible: false, }); }, diff --git a/packages/frontend/src/pages/App.tsx b/packages/frontend/src/pages/App.tsx index ce62332..c3625c3 100644 --- a/packages/frontend/src/pages/App.tsx +++ b/packages/frontend/src/pages/App.tsx @@ -2,7 +2,6 @@ import type { IssueResponse, - OrganisationMemberResponse, OrganisationResponse, ProjectRecord, ProjectResponse, @@ -107,25 +106,21 @@ export default function App() { const refetchOrganisations = async (options?: { selectOrganisationId?: number }) => { try { await organisation.byUser({ - userId: user.id, onSuccess: (data) => { - const organisations = data as OrganisationResponse[]; - organisations.sort((a, b) => a.Organisation.name.localeCompare(b.Organisation.name)); - setOrganisations(organisations); + data.sort((a, b) => a.Organisation.name.localeCompare(b.Organisation.name)); + setOrganisations(data); let selected: OrganisationResponse | null = null; if (options?.selectOrganisationId) { - const created = organisations.find( - (o) => o.Organisation.id === options.selectOrganisationId, - ); + const created = data.find((o) => o.Organisation.id === options.selectOrganisationId); if (created) { selected = created; } } else { const deepLinkState = deepLinkStateRef.current; if (deepLinkParams.orgSlug && !deepLinkState.appliedOrg) { - const match = organisations.find( + const match = data.find( (org) => org.Organisation.slug.toLowerCase() === deepLinkParams.orgSlug, ); deepLinkState.appliedOrg = true; @@ -139,9 +134,7 @@ export default function App() { if (!selected) { const savedId = localStorage.getItem("selectedOrganisationId"); if (savedId) { - const saved = organisations.find( - (o) => o.Organisation.id === Number(savedId), - ); + const saved = data.find((o) => o.Organisation.id === Number(savedId)); if (saved) { selected = saved; } @@ -150,7 +143,7 @@ export default function App() { } if (!selected) { - selected = organisations[0] || null; + selected = data[0] || null; } setSelectedOrganisation(selected); @@ -260,7 +253,7 @@ export default function App() { try { await organisation.members({ organisationId, - onSuccess: (data: OrganisationMemberResponse[]) => { + onSuccess: (data) => { setMembers(data.map((m) => m.User)); }, onError: (error) => { @@ -282,7 +275,7 @@ export default function App() { try { await sprint.byProject({ projectId, - onSuccess: (data: SprintRecord[]) => { + onSuccess: (data) => { setSprints(data); }, onError: (error) => {