mirror of
https://github.com/hex248/sprint.git
synced 2026-02-08 02:33:01 +00:00
use parseError
This commit is contained in:
@@ -7,7 +7,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from
|
|||||||
import { Field } from "@/components/ui/field";
|
import { Field } from "@/components/ui/field";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { UploadAvatar } from "@/components/upload-avatar";
|
import { UploadAvatar } from "@/components/upload-avatar";
|
||||||
import { user } from "@/lib/server";
|
import { parseError, user } from "@/lib/server";
|
||||||
|
|
||||||
function AccountDialog({ trigger }: { trigger?: ReactNode }) {
|
function AccountDialog({ trigger }: { trigger?: ReactNode }) {
|
||||||
const { user: currentUser, setUser } = useAuthenticatedSession();
|
const { user: currentUser, setUser } = useAuthenticatedSession();
|
||||||
@@ -41,9 +41,8 @@ function AccountDialog({ trigger }: { trigger?: ReactNode }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await user.update({
|
await user.update({
|
||||||
id: currentUser.id,
|
|
||||||
name: name.trim(),
|
name: name.trim(),
|
||||||
password: password.trim(),
|
password: password.trim() || undefined,
|
||||||
avatarURL,
|
avatarURL,
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
setError("");
|
setError("");
|
||||||
@@ -55,10 +54,11 @@ function AccountDialog({ trigger }: { trigger?: ReactNode }) {
|
|||||||
dismissible: false,
|
dismissible: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (err) => {
|
||||||
setError(error);
|
const message = parseError(err);
|
||||||
|
setError(message);
|
||||||
|
|
||||||
toast.error(`Error updating account: ${error}`, {
|
toast.error(`Error updating account: ${message}`, {
|
||||||
dismissible: false,
|
dismissible: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Field } from "@/components/ui/field";
|
import { Field } from "@/components/ui/field";
|
||||||
import { organisation, user } from "@/lib/server";
|
import { organisation, parseError, user } from "@/lib/server";
|
||||||
|
|
||||||
export function AddMemberDialog({
|
export function AddMemberDialog({
|
||||||
organisationId,
|
organisationId,
|
||||||
@@ -68,11 +68,12 @@ export function AddMemberDialog({
|
|||||||
userData = data;
|
userData = data;
|
||||||
userId = data.id;
|
userId = data.id;
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (err) => {
|
||||||
setError(error || "user not found");
|
const message = parseError(err);
|
||||||
|
setError(message || "user not found");
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
|
||||||
toast.error(`Error adding member: ${error}`, {
|
toast.error(`Error adding member: ${message}`, {
|
||||||
dismissible: false,
|
dismissible: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -95,11 +96,12 @@ export function AddMemberDialog({
|
|||||||
console.error(actionErr);
|
console.error(actionErr);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (err) => {
|
||||||
setError(error || "failed to add member");
|
const message = parseError(err);
|
||||||
|
setError(message || "failed to add member");
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
|
||||||
toast.error(`Error adding member: ${error}`, {
|
toast.error(`Error adding member: ${message}`, {
|
||||||
dismissible: false,
|
dismissible: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import { Field } from "@/components/ui/field";
|
|||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { SelectTrigger } from "@/components/ui/select";
|
import { SelectTrigger } from "@/components/ui/select";
|
||||||
import { UserSelect } from "@/components/user-select";
|
import { UserSelect } from "@/components/user-select";
|
||||||
import { issue } from "@/lib/server";
|
import { issue, parseError } from "@/lib/server";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { SprintSelect } from "./sprint-select";
|
import { SprintSelect } from "./sprint-select";
|
||||||
|
|
||||||
@@ -116,16 +116,17 @@ export function CreateIssue({
|
|||||||
console.error(actionErr);
|
console.error(actionErr);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: async (error) => {
|
onError: async (err) => {
|
||||||
setError(error);
|
const message = parseError(err);
|
||||||
|
setError(message);
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
|
||||||
toast.error(`Error creating issue: ${error}`, {
|
toast.error(`Error creating issue: ${message}`, {
|
||||||
dismissible: false,
|
dismissible: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await errorAction?.(error);
|
await errorAction?.(message);
|
||||||
} catch (actionErr) {
|
} catch (actionErr) {
|
||||||
console.error(actionErr);
|
console.error(actionErr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Field } from "@/components/ui/field";
|
import { Field } from "@/components/ui/field";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { organisation } from "@/lib/server";
|
import { organisation, parseError } from "@/lib/server";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const slugify = (value: string) =>
|
const slugify = (value: string) =>
|
||||||
@@ -85,7 +85,6 @@ export function CreateOrganisation({
|
|||||||
name,
|
name,
|
||||||
slug,
|
slug,
|
||||||
description,
|
description,
|
||||||
userId: user.id,
|
|
||||||
onSuccess: async (data) => {
|
onSuccess: async (data) => {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
reset();
|
reset();
|
||||||
@@ -96,10 +95,11 @@ export function CreateOrganisation({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: async (err) => {
|
onError: async (err) => {
|
||||||
setError(err || "failed to create organisation");
|
const message = parseError(err);
|
||||||
|
setError(message || "failed to create organisation");
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
try {
|
try {
|
||||||
await errorAction?.(err || "failed to create organisation");
|
await errorAction?.(message || "failed to create organisation");
|
||||||
} catch (actionErr) {
|
} catch (actionErr) {
|
||||||
console.error(actionErr);
|
console.error(actionErr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Field } from "@/components/ui/field";
|
import { Field } from "@/components/ui/field";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { project } from "@/lib/server";
|
import { parseError, project } from "@/lib/server";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const keyify = (value: string) =>
|
const keyify = (value: string) =>
|
||||||
@@ -86,24 +86,24 @@ export function CreateProject({
|
|||||||
await project.create({
|
await project.create({
|
||||||
key,
|
key,
|
||||||
name,
|
name,
|
||||||
creatorId: user.id,
|
|
||||||
organisationId,
|
organisationId,
|
||||||
onSuccess: async (data) => {
|
onSuccess: async (data) => {
|
||||||
const project = data as ProjectRecord;
|
const proj = data as ProjectRecord;
|
||||||
|
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
reset();
|
reset();
|
||||||
try {
|
try {
|
||||||
await completeAction?.(project);
|
await completeAction?.(proj);
|
||||||
} catch (actionErr) {
|
} catch (actionErr) {
|
||||||
console.error(actionErr);
|
console.error(actionErr);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (err) => {
|
||||||
setError(error);
|
const message = parseError(err);
|
||||||
|
setError(message);
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
|
||||||
toast.error(`Error creating project: ${error}`, {
|
toast.error(`Error creating project: ${message}`, {
|
||||||
dismissible: false,
|
dismissible: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
import { Field } from "@/components/ui/field";
|
import { Field } from "@/components/ui/field";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
import { sprint } from "@/lib/server";
|
import { parseError, sprint } from "@/lib/server";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const SPRINT_NAME_MAX_LENGTH = 64;
|
const SPRINT_NAME_MAX_LENGTH = 64;
|
||||||
@@ -135,11 +135,12 @@ export function CreateSprint({
|
|||||||
console.error(actionErr);
|
console.error(actionErr);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (err) => {
|
||||||
setError(error);
|
const message = parseError(err);
|
||||||
|
setError(message);
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
|
||||||
toast.error(`Error creating sprint: ${error}`, {
|
toast.error(`Error creating sprint: ${message}`, {
|
||||||
dismissible: false,
|
dismissible: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { TimerState } from "@issue/shared";
|
import type { TimerState } from "@issue/shared";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { timer } from "@/lib/server";
|
import { parseError, timer } from "@/lib/server";
|
||||||
import { cn, formatTime } from "@/lib/utils";
|
import { cn, formatTime } from "@/lib/utils";
|
||||||
|
|
||||||
export function IssueTimer({ issueId, onEnd }: { issueId: number; onEnd?: (data: TimerState) => void }) {
|
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);
|
setDisplayTime(data.workTimeMs);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: setError,
|
onError: (err) => setError(parseError(err)),
|
||||||
});
|
});
|
||||||
}, [issueId]);
|
}, [issueId]);
|
||||||
|
|
||||||
@@ -41,11 +41,13 @@ export function IssueTimer({ issueId, onEnd }: { issueId: number; onEnd?: (data:
|
|||||||
timer.toggle({
|
timer.toggle({
|
||||||
issueId,
|
issueId,
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
setTimerState(data);
|
if (data) {
|
||||||
setDisplayTime(data.workTimeMs);
|
setTimerState(data);
|
||||||
|
setDisplayTime(data.workTimeMs);
|
||||||
|
}
|
||||||
setError(null);
|
setError(null);
|
||||||
},
|
},
|
||||||
onError: setError,
|
onError: (err) => setError(parseError(err)),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -53,12 +55,14 @@ export function IssueTimer({ issueId, onEnd }: { issueId: number; onEnd?: (data:
|
|||||||
timer.end({
|
timer.end({
|
||||||
issueId,
|
issueId,
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
setTimerState(data);
|
if (data) {
|
||||||
setDisplayTime(data.workTimeMs);
|
setTimerState(data);
|
||||||
|
setDisplayTime(data.workTimeMs);
|
||||||
|
onEnd?.(data);
|
||||||
|
}
|
||||||
setError(null);
|
setError(null);
|
||||||
onEnd?.(data);
|
|
||||||
},
|
},
|
||||||
onError: setError,
|
onError: (err) => setError(parseError(err)),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { TimerState } from "@issue/shared";
|
import type { TimerState } from "@issue/shared";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { timer } from "@/lib/server";
|
import { parseError, timer } from "@/lib/server";
|
||||||
import { formatTime } from "@/lib/utils";
|
import { formatTime } from "@/lib/utils";
|
||||||
|
|
||||||
const FALLBACK_TIME = "--:--:--";
|
const FALLBACK_TIME = "--:--:--";
|
||||||
@@ -25,11 +25,12 @@ export function TimerDisplay({ issueId }: { issueId: number }) {
|
|||||||
setWorkTimeMs(data?.workTimeMs ?? 0);
|
setWorkTimeMs(data?.workTimeMs ?? 0);
|
||||||
setError(null);
|
setError(null);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (err) => {
|
||||||
if (!isMounted) return;
|
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,
|
dismissible: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -39,19 +40,19 @@ export function TimerDisplay({ issueId }: { issueId: number }) {
|
|||||||
issueId,
|
issueId,
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
if (!isMounted) return;
|
if (!isMounted) return;
|
||||||
const sessions = (data ?? []) as TimerState[];
|
const totalWorkTime = data.reduce(
|
||||||
const totalWorkTime = sessions.reduce(
|
|
||||||
(total, session) => total + (session?.workTimeMs ?? 0),
|
(total, session) => total + (session?.workTimeMs ?? 0),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
setInactiveWorkTimeMs(totalWorkTime);
|
setInactiveWorkTimeMs(totalWorkTime);
|
||||||
setError(null);
|
setError(null);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (err) => {
|
||||||
if (!isMounted) return;
|
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,
|
dismissible: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { toast } from "sonner";
|
|||||||
import Avatar from "@/components/avatar";
|
import Avatar from "@/components/avatar";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { user } from "@/lib/server";
|
import { parseError, user } from "@/lib/server";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
export function UploadAvatar({
|
export function UploadAvatar({
|
||||||
@@ -49,11 +49,12 @@ export function UploadAvatar({
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (err) => {
|
||||||
setError(error);
|
const message = parseError(err);
|
||||||
|
setError(message);
|
||||||
setUploading(false);
|
setUploading(false);
|
||||||
|
|
||||||
toast.error(`Error uploading avatar: ${error}`, {
|
toast.error(`Error uploading avatar: ${message}`, {
|
||||||
dismissible: false,
|
dismissible: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import type {
|
import type {
|
||||||
IssueResponse,
|
IssueResponse,
|
||||||
OrganisationMemberResponse,
|
|
||||||
OrganisationResponse,
|
OrganisationResponse,
|
||||||
ProjectRecord,
|
ProjectRecord,
|
||||||
ProjectResponse,
|
ProjectResponse,
|
||||||
@@ -107,25 +106,21 @@ export default function App() {
|
|||||||
const refetchOrganisations = async (options?: { selectOrganisationId?: number }) => {
|
const refetchOrganisations = async (options?: { selectOrganisationId?: number }) => {
|
||||||
try {
|
try {
|
||||||
await organisation.byUser({
|
await organisation.byUser({
|
||||||
userId: user.id,
|
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
const organisations = data as OrganisationResponse[];
|
data.sort((a, b) => a.Organisation.name.localeCompare(b.Organisation.name));
|
||||||
organisations.sort((a, b) => a.Organisation.name.localeCompare(b.Organisation.name));
|
setOrganisations(data);
|
||||||
setOrganisations(organisations);
|
|
||||||
|
|
||||||
let selected: OrganisationResponse | null = null;
|
let selected: OrganisationResponse | null = null;
|
||||||
|
|
||||||
if (options?.selectOrganisationId) {
|
if (options?.selectOrganisationId) {
|
||||||
const created = organisations.find(
|
const created = data.find((o) => o.Organisation.id === options.selectOrganisationId);
|
||||||
(o) => o.Organisation.id === options.selectOrganisationId,
|
|
||||||
);
|
|
||||||
if (created) {
|
if (created) {
|
||||||
selected = created;
|
selected = created;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const deepLinkState = deepLinkStateRef.current;
|
const deepLinkState = deepLinkStateRef.current;
|
||||||
if (deepLinkParams.orgSlug && !deepLinkState.appliedOrg) {
|
if (deepLinkParams.orgSlug && !deepLinkState.appliedOrg) {
|
||||||
const match = organisations.find(
|
const match = data.find(
|
||||||
(org) => org.Organisation.slug.toLowerCase() === deepLinkParams.orgSlug,
|
(org) => org.Organisation.slug.toLowerCase() === deepLinkParams.orgSlug,
|
||||||
);
|
);
|
||||||
deepLinkState.appliedOrg = true;
|
deepLinkState.appliedOrg = true;
|
||||||
@@ -139,9 +134,7 @@ export default function App() {
|
|||||||
if (!selected) {
|
if (!selected) {
|
||||||
const savedId = localStorage.getItem("selectedOrganisationId");
|
const savedId = localStorage.getItem("selectedOrganisationId");
|
||||||
if (savedId) {
|
if (savedId) {
|
||||||
const saved = organisations.find(
|
const saved = data.find((o) => o.Organisation.id === Number(savedId));
|
||||||
(o) => o.Organisation.id === Number(savedId),
|
|
||||||
);
|
|
||||||
if (saved) {
|
if (saved) {
|
||||||
selected = saved;
|
selected = saved;
|
||||||
}
|
}
|
||||||
@@ -150,7 +143,7 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!selected) {
|
if (!selected) {
|
||||||
selected = organisations[0] || null;
|
selected = data[0] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedOrganisation(selected);
|
setSelectedOrganisation(selected);
|
||||||
@@ -260,7 +253,7 @@ export default function App() {
|
|||||||
try {
|
try {
|
||||||
await organisation.members({
|
await organisation.members({
|
||||||
organisationId,
|
organisationId,
|
||||||
onSuccess: (data: OrganisationMemberResponse[]) => {
|
onSuccess: (data) => {
|
||||||
setMembers(data.map((m) => m.User));
|
setMembers(data.map((m) => m.User));
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
@@ -282,7 +275,7 @@ export default function App() {
|
|||||||
try {
|
try {
|
||||||
await sprint.byProject({
|
await sprint.byProject({
|
||||||
projectId,
|
projectId,
|
||||||
onSuccess: (data: SprintRecord[]) => {
|
onSuccess: (data) => {
|
||||||
setSprints(data);
|
setSprints(data);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user