use parseError

This commit is contained in:
Oliver Bryan
2026-01-13 15:34:15 +00:00
parent ca371b1751
commit dc566260d8
10 changed files with 73 additions and 70 deletions

View File

@@ -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,
});
},

View File

@@ -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,
});
},

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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,
});
},

View File

@@ -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,
});
},

View File

@@ -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) => {
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) => {
if (data) {
setTimerState(data);
setDisplayTime(data.workTimeMs);
setError(null);
onEnd?.(data);
}
setError(null);
},
onError: setError,
onError: (err) => setError(parseError(err)),
});
};

View File

@@ -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,
});
},

View File

@@ -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,
});
},

View File

@@ -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) => {