mirror of
https://github.com/hex248/sprint.git
synced 2026-02-07 18:23:03 +00:00
toasts all over
This commit is contained in:
@@ -50,14 +50,18 @@ function AccountDialog({ trigger }: { trigger?: ReactNode }) {
|
|||||||
setUser(data);
|
setUser(data);
|
||||||
setPassword("");
|
setPassword("");
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
},
|
|
||||||
onError: (errorMessage) => {
|
|
||||||
setError(errorMessage);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
toast.success(`Account updated successfully`, {
|
toast.success(`Account updated successfully`, {
|
||||||
dismissible: false,
|
dismissible: false,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
setError(error);
|
||||||
|
|
||||||
|
toast.error(`Error updating account: ${error}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { UserRecord } from "@issue/shared";
|
import type { UserRecord } from "@issue/shared";
|
||||||
import { type FormEvent, useState } from "react";
|
import { type FormEvent, useState } from "react";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -67,9 +68,13 @@ export function AddMemberDialog({
|
|||||||
userData = data;
|
userData = data;
|
||||||
userId = data.id;
|
userId = data.id;
|
||||||
},
|
},
|
||||||
onError: (err) => {
|
onError: (error) => {
|
||||||
setError(err || "user not found");
|
setError(error || "user not found");
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
|
||||||
|
toast.error(`Error adding member: ${error}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -90,9 +95,13 @@ export function AddMemberDialog({
|
|||||||
console.error(actionErr);
|
console.error(actionErr);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (err) => {
|
onError: (error) => {
|
||||||
setError(err || "failed to add member");
|
setError(error || "failed to add member");
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
|
||||||
|
toast.error(`Error adding member: ${error}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
} from "@issue/shared";
|
} from "@issue/shared";
|
||||||
|
|
||||||
import { type FormEvent, useState } from "react";
|
import { type FormEvent, useState } from "react";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { useAuthenticatedSession } from "@/components/session-provider";
|
import { useAuthenticatedSession } from "@/components/session-provider";
|
||||||
import { StatusSelect } from "@/components/status-select";
|
import { StatusSelect } from "@/components/status-select";
|
||||||
import StatusTag from "@/components/status-tag";
|
import StatusTag from "@/components/status-tag";
|
||||||
@@ -33,6 +34,7 @@ export function CreateIssue({
|
|||||||
statuses,
|
statuses,
|
||||||
trigger,
|
trigger,
|
||||||
completeAction,
|
completeAction,
|
||||||
|
errorAction,
|
||||||
}: {
|
}: {
|
||||||
projectId?: number;
|
projectId?: number;
|
||||||
sprints?: SprintRecord[];
|
sprints?: SprintRecord[];
|
||||||
@@ -40,6 +42,7 @@ export function CreateIssue({
|
|||||||
statuses: Record<string, string>;
|
statuses: Record<string, string>;
|
||||||
trigger?: React.ReactNode;
|
trigger?: React.ReactNode;
|
||||||
completeAction?: (issueNumber: number) => void | Promise<void>;
|
completeAction?: (issueNumber: number) => void | Promise<void>;
|
||||||
|
errorAction?: (errorMessage: string) => void | Promise<void>;
|
||||||
}) {
|
}) {
|
||||||
const { user } = useAuthenticatedSession();
|
const { user } = useAuthenticatedSession();
|
||||||
|
|
||||||
@@ -113,9 +116,13 @@ export function CreateIssue({
|
|||||||
console.error(actionErr);
|
console.error(actionErr);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (message) => {
|
onError: (error) => {
|
||||||
setError(message);
|
setError(error);
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
|
||||||
|
toast.error(`Error creating issue: ${error}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -31,9 +31,11 @@ const slugify = (value: string) =>
|
|||||||
export function CreateOrganisation({
|
export function CreateOrganisation({
|
||||||
trigger,
|
trigger,
|
||||||
completeAction,
|
completeAction,
|
||||||
|
errorAction,
|
||||||
}: {
|
}: {
|
||||||
trigger?: React.ReactNode;
|
trigger?: React.ReactNode;
|
||||||
completeAction?: (org: OrganisationRecord) => void | Promise<void>;
|
completeAction?: (org: OrganisationRecord) => void | Promise<void>;
|
||||||
|
errorAction?: (errorMessage: string) => void | Promise<void>;
|
||||||
}) {
|
}) {
|
||||||
const { user } = useAuthenticatedSession();
|
const { user } = useAuthenticatedSession();
|
||||||
|
|
||||||
@@ -93,9 +95,14 @@ export function CreateOrganisation({
|
|||||||
console.error(actionErr);
|
console.error(actionErr);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (err) => {
|
onError: async (err) => {
|
||||||
setError(err || "failed to create organisation");
|
setError(err || "failed to create organisation");
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
try {
|
||||||
|
await errorAction?.(err || "failed to create organisation");
|
||||||
|
} catch (actionErr) {
|
||||||
|
console.error(actionErr);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { PROJECT_NAME_MAX_LENGTH, type ProjectRecord } from "@issue/shared";
|
import { PROJECT_NAME_MAX_LENGTH, type ProjectRecord } from "@issue/shared";
|
||||||
import { type FormEvent, useState } from "react";
|
import { type FormEvent, useState } from "react";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { useAuthenticatedSession } from "@/components/session-provider";
|
import { useAuthenticatedSession } from "@/components/session-provider";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
@@ -98,9 +99,13 @@ export function CreateProject({
|
|||||||
console.error(actionErr);
|
console.error(actionErr);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (message) => {
|
onError: (error) => {
|
||||||
setError(message);
|
setError(error);
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
|
||||||
|
toast.error(`Error creating project: ${error}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { DEFAULT_SPRINT_COLOUR, type SprintRecord } from "@issue/shared";
|
import { DEFAULT_SPRINT_COLOUR, type SprintRecord } from "@issue/shared";
|
||||||
import { type FormEvent, useMemo, useState } from "react";
|
import { type FormEvent, useMemo, useState } from "react";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { useAuthenticatedSession } from "@/components/session-provider";
|
import { useAuthenticatedSession } from "@/components/session-provider";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Calendar } from "@/components/ui/calendar";
|
import { Calendar } from "@/components/ui/calendar";
|
||||||
@@ -134,9 +135,13 @@ export function CreateSprint({
|
|||||||
console.error(actionErr);
|
console.error(actionErr);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (message) => {
|
onError: (error) => {
|
||||||
setError(message);
|
setError(error);
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
|
||||||
|
toast.error(`Error creating sprint: ${error}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (submitError) {
|
} catch (submitError) {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ 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 } from "@/lib/server";
|
||||||
import { issueID } from "@/lib/utils";
|
import { issueID } from "@/lib/utils";
|
||||||
|
import SmallSprintDisplay from "./small-sprint-display";
|
||||||
import { SprintSelect } from "./sprint-select";
|
import { SprintSelect } from "./sprint-select";
|
||||||
|
|
||||||
export function IssueDetailPane({
|
export function IssueDetailPane({
|
||||||
@@ -68,10 +69,40 @@ export function IssueDetailPane({
|
|||||||
sprintId: newSprintId,
|
sprintId: newSprintId,
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
onIssueUpdate?.();
|
onIssueUpdate?.();
|
||||||
|
|
||||||
|
toast.success(
|
||||||
|
<>
|
||||||
|
Successfully updated sprint to{" "}
|
||||||
|
{value === "unassigned" ? (
|
||||||
|
"Unassigned"
|
||||||
|
) : (
|
||||||
|
<SmallSprintDisplay sprint={sprints.find((s) => s.id === newSprintId)} />
|
||||||
|
)}{" "}
|
||||||
|
for {issueID(project.Project.key, issueData.Issue.number)}
|
||||||
|
</>,
|
||||||
|
{
|
||||||
|
dismissible: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error("error updating sprint:", error);
|
console.error("error updating sprint:", error);
|
||||||
setSprintId(issueData.Issue.sprintId?.toString() ?? "unassigned");
|
setSprintId(issueData.Issue.sprintId?.toString() ?? "unassigned");
|
||||||
|
|
||||||
|
toast.error(
|
||||||
|
<>
|
||||||
|
Error updating sprint to{" "}
|
||||||
|
{value === "unassigned" ? (
|
||||||
|
"Unassigned"
|
||||||
|
) : (
|
||||||
|
<SmallSprintDisplay sprint={sprints.find((s) => s.id === newSprintId)} />
|
||||||
|
)}{" "}
|
||||||
|
for {issueID(project.Project.key, issueData.Issue.number)}
|
||||||
|
</>,
|
||||||
|
{
|
||||||
|
dismissible: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -96,6 +127,10 @@ export function IssueDetailPane({
|
|||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error("error updating assignee:", error);
|
console.error("error updating assignee:", error);
|
||||||
setAssigneeId(issueData.Issue.assigneeId?.toString() ?? "unassigned");
|
setAssigneeId(issueData.Issue.assigneeId?.toString() ?? "unassigned");
|
||||||
|
|
||||||
|
toast.error(`Error updating assignee: ${error}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -119,6 +154,10 @@ export function IssueDetailPane({
|
|||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error("error updating status:", error);
|
console.error("error updating status:", error);
|
||||||
setStatus(issueData.Issue.status);
|
setStatus(issueData.Issue.status);
|
||||||
|
|
||||||
|
toast.error(`Error updating status: ${error}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -148,9 +187,23 @@ export function IssueDetailPane({
|
|||||||
issueId: issueData.Issue.id,
|
issueId: issueData.Issue.id,
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
await onIssueDelete?.(issueData.Issue.id);
|
await onIssueDelete?.(issueData.Issue.id);
|
||||||
|
|
||||||
|
toast.success(`Deleted issue ${issueID(project.Project.key, issueData.Issue.number)}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error("error deleting issue:", error);
|
console.error(
|
||||||
|
`error deleting issue ${issueID(project.Project.key, issueData.Issue.number)}`,
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
|
||||||
|
toast.error(
|
||||||
|
`Error deleting issue ${issueID(project.Project.key, issueData.Issue.number)}: ${error}`,
|
||||||
|
{
|
||||||
|
dismissible: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
setDeleteOpen(false);
|
setDeleteOpen(false);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { OrganisationRecord, OrganisationResponse } from "@issue/shared";
|
import type { OrganisationRecord, OrganisationResponse } from "@issue/shared";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { toast } from "sonner";
|
||||||
import { CreateOrganisation } from "@/components/create-organisation";
|
import { CreateOrganisation } from "@/components/create-organisation";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
@@ -86,6 +87,11 @@ export function OrganisationSelect({
|
|||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
errorAction={async (errorMessage) => {
|
||||||
|
toast.error(`Error creating organisation: ${errorMessage}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
|||||||
@@ -98,6 +98,10 @@ function OrganisationsDialog({
|
|||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
setMembers([]);
|
setMembers([]);
|
||||||
|
|
||||||
|
toast.error(`Error fetching members: ${error}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -125,15 +129,23 @@ function OrganisationsDialog({
|
|||||||
role: newRole,
|
role: newRole,
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
closeConfirmDialog();
|
closeConfirmDialog();
|
||||||
|
|
||||||
|
toast.success(`${capitalise(action)}d ${memberName} to ${newRole} successfully`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
|
|
||||||
void refetchMembers();
|
void refetchMembers();
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
toast.success(`${capitalise(action)}d ${memberName} to ${newRole} successfully`, {
|
toast.error(
|
||||||
dismissible: false,
|
`Error ${action.slice(0, -1)}ing ${memberName} to ${newRole}: ${error}`,
|
||||||
|
{
|
||||||
|
dismissible: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@@ -162,19 +174,27 @@ function OrganisationsDialog({
|
|||||||
userId: memberUserId,
|
userId: memberUserId,
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
closeConfirmDialog();
|
closeConfirmDialog();
|
||||||
|
|
||||||
|
toast.success(
|
||||||
|
`Removed ${memberName} from ${selectedOrganisation.Organisation.name} successfully`,
|
||||||
|
{
|
||||||
|
dismissible: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
void refetchMembers();
|
void refetchMembers();
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|
||||||
|
toast.error(
|
||||||
|
`Error removing member from ${selectedOrganisation.Organisation.name}: ${error}`,
|
||||||
|
{
|
||||||
|
dismissible: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
toast.success(
|
|
||||||
`Removed ${memberName} from ${selectedOrganisation.Organisation.name} successfully`,
|
|
||||||
{
|
|
||||||
dismissible: false,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
@@ -191,6 +211,8 @@ function OrganisationsDialog({
|
|||||||
const updateStatuses = async (
|
const updateStatuses = async (
|
||||||
newStatuses: Record<string, string>,
|
newStatuses: Record<string, string>,
|
||||||
statusRemoved?: { name: string; colour: string },
|
statusRemoved?: { name: string; colour: string },
|
||||||
|
statusAdded?: { name: string; colour: string },
|
||||||
|
statusMoved?: { name: string; colour: string; currentIndex: number; nextIndex: number },
|
||||||
) => {
|
) => {
|
||||||
if (!selectedOrganisation) return;
|
if (!selectedOrganisation) return;
|
||||||
|
|
||||||
@@ -200,7 +222,17 @@ function OrganisationsDialog({
|
|||||||
statuses: newStatuses,
|
statuses: newStatuses,
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
setStatuses(newStatuses);
|
setStatuses(newStatuses);
|
||||||
if (statusRemoved) {
|
if (statusAdded) {
|
||||||
|
toast.success(
|
||||||
|
<>
|
||||||
|
Created <StatusTag status={statusAdded.name} colour={statusAdded.colour} />{" "}
|
||||||
|
status successfully
|
||||||
|
</>,
|
||||||
|
{
|
||||||
|
dismissible: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else if (statusRemoved) {
|
||||||
toast.success(
|
toast.success(
|
||||||
<>
|
<>
|
||||||
Removed{" "}
|
Removed{" "}
|
||||||
@@ -211,11 +243,58 @@ function OrganisationsDialog({
|
|||||||
dismissible: false,
|
dismissible: false,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
} else if (statusMoved) {
|
||||||
|
toast.success(
|
||||||
|
<>
|
||||||
|
Moved <StatusTag status={statusMoved.name} colour={statusMoved.colour} /> from
|
||||||
|
position {statusMoved.currentIndex + 1} to {statusMoved.nextIndex + 1}{" "}
|
||||||
|
successfully
|
||||||
|
</>,
|
||||||
|
{
|
||||||
|
dismissible: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
void refetchOrganisations();
|
void refetchOrganisations();
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error("error updating statuses:", error);
|
console.error("error updating statuses:", error);
|
||||||
|
|
||||||
|
if (statusAdded) {
|
||||||
|
toast.error(
|
||||||
|
<>
|
||||||
|
Error adding{" "}
|
||||||
|
<StatusTag status={statusAdded.name} colour={statusAdded.colour} /> to{" "}
|
||||||
|
{selectedOrganisation.Organisation.name}: {error}
|
||||||
|
</>,
|
||||||
|
{
|
||||||
|
dismissible: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else if (statusRemoved) {
|
||||||
|
toast.error(
|
||||||
|
<>
|
||||||
|
Error removing{" "}
|
||||||
|
<StatusTag status={statusRemoved.name} colour={statusRemoved.colour} /> from{" "}
|
||||||
|
{selectedOrganisation.Organisation.name}: {error}
|
||||||
|
</>,
|
||||||
|
{
|
||||||
|
dismissible: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else if (statusMoved) {
|
||||||
|
toast.error(
|
||||||
|
<>
|
||||||
|
Error moving{" "}
|
||||||
|
<StatusTag status={statusMoved.name} colour={statusMoved.colour} />
|
||||||
|
from position {statusMoved.currentIndex + 1} to {statusMoved.nextIndex + 1}{" "}
|
||||||
|
{selectedOrganisation.Organisation.name}: {error}
|
||||||
|
</>,
|
||||||
|
{
|
||||||
|
dismissible: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -240,17 +319,7 @@ function OrganisationsDialog({
|
|||||||
}
|
}
|
||||||
const newStatuses = { ...statuses };
|
const newStatuses = { ...statuses };
|
||||||
newStatuses[trimmed] = newStatusColour;
|
newStatuses[trimmed] = newStatusColour;
|
||||||
await updateStatuses(newStatuses);
|
await updateStatuses(newStatuses, undefined, { name: trimmed, colour: newStatusColour });
|
||||||
|
|
||||||
toast.success(
|
|
||||||
<>
|
|
||||||
Created <StatusTag status={newStatusName.trim()} colour={newStatusColour} /> status
|
|
||||||
successfully
|
|
||||||
</>,
|
|
||||||
{
|
|
||||||
dismissible: false,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
setNewStatusName("");
|
setNewStatusName("");
|
||||||
setNewStatusColour(DEFAULT_STATUS_COLOUR);
|
setNewStatusColour(DEFAULT_STATUS_COLOUR);
|
||||||
@@ -282,6 +351,16 @@ function OrganisationsDialog({
|
|||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error("error checking status usage:", error);
|
console.error("error checking status usage:", error);
|
||||||
|
|
||||||
|
toast.error(
|
||||||
|
<>
|
||||||
|
Error checking status usage for{" "}
|
||||||
|
<StatusTag status={status} colour={statuses[status]} />: {error}
|
||||||
|
</>,
|
||||||
|
{
|
||||||
|
dismissible: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -301,15 +380,11 @@ function OrganisationsDialog({
|
|||||||
nextStatuses[currentIndex],
|
nextStatuses[currentIndex],
|
||||||
];
|
];
|
||||||
|
|
||||||
await updateStatuses(Object.fromEntries(nextStatuses.map((status) => [status, statuses[status]])));
|
await updateStatuses(
|
||||||
toast.success(
|
Object.fromEntries(nextStatuses.map((status) => [status, statuses[status]])),
|
||||||
<>
|
undefined,
|
||||||
Moved <StatusTag status={status} colour={statuses[status]} /> from position {currentIndex + 1}{" "}
|
undefined,
|
||||||
to {nextIndex + 1} successfully
|
{ name: status, colour: statuses[status], currentIndex, nextIndex },
|
||||||
</>,
|
|
||||||
{
|
|
||||||
dismissible: false,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -331,6 +406,17 @@ function OrganisationsDialog({
|
|||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error("error replacing status:", error);
|
console.error("error replacing status:", error);
|
||||||
|
|
||||||
|
toast.error(
|
||||||
|
<>
|
||||||
|
Error removing <StatusTag status={statusToRemove} colour={statuses[statusToRemove]} />{" "}
|
||||||
|
from
|
||||||
|
{selectedOrganisation.Organisation.name}: {error}{" "}
|
||||||
|
</>,
|
||||||
|
{
|
||||||
|
dismissible: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -482,6 +568,7 @@ function OrganisationsDialog({
|
|||||||
dismissible: false,
|
dismissible: false,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
refetchMembers();
|
refetchMembers();
|
||||||
}}
|
}}
|
||||||
trigger={
|
trigger={
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
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 { timer } from "@/lib/server";
|
import { timer } from "@/lib/server";
|
||||||
import { formatTime } from "@/lib/utils";
|
import { formatTime } from "@/lib/utils";
|
||||||
|
|
||||||
@@ -24,9 +25,13 @@ export function TimerDisplay({ issueId }: { issueId: number }) {
|
|||||||
setWorkTimeMs(data?.workTimeMs ?? 0);
|
setWorkTimeMs(data?.workTimeMs ?? 0);
|
||||||
setError(null);
|
setError(null);
|
||||||
},
|
},
|
||||||
onError: (message) => {
|
onError: (error) => {
|
||||||
if (!isMounted) return;
|
if (!isMounted) return;
|
||||||
setError(message);
|
setError(error);
|
||||||
|
|
||||||
|
toast.error(`Error fetching timer data: ${error}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -42,9 +47,13 @@ export function TimerDisplay({ issueId }: { issueId: number }) {
|
|||||||
setInactiveWorkTimeMs(totalWorkTime);
|
setInactiveWorkTimeMs(totalWorkTime);
|
||||||
setError(null);
|
setError(null);
|
||||||
},
|
},
|
||||||
onError: (message) => {
|
onError: (error) => {
|
||||||
if (!isMounted) return;
|
if (!isMounted) return;
|
||||||
setError(message);
|
setError(error);
|
||||||
|
|
||||||
|
toast.error(`Error fetching timer data: ${error}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -38,15 +38,25 @@ export function UploadAvatar({
|
|||||||
onSuccess: (url) => {
|
onSuccess: (url) => {
|
||||||
onAvatarUploaded(url);
|
onAvatarUploaded(url);
|
||||||
setUploading(false);
|
setUploading(false);
|
||||||
},
|
|
||||||
onError: (msg) => {
|
|
||||||
setError(msg);
|
|
||||||
setUploading(false);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
toast.success(`Avatar uploaded successfully`, {
|
toast.success(
|
||||||
dismissible: false,
|
<div className="flex flex-col items-center gap-4">
|
||||||
|
<img src={url} alt="Avatar" className="w-32 h-32" />
|
||||||
|
Avatar uploaded successfully
|
||||||
|
</div>,
|
||||||
|
{
|
||||||
|
dismissible: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
setError(error);
|
||||||
|
setUploading(false);
|
||||||
|
|
||||||
|
toast.error(`Error uploading avatar: ${error}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -168,6 +168,12 @@ export default function App() {
|
|||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error("error fetching organisations:", error);
|
console.error("error fetching organisations:", error);
|
||||||
|
setOrganisations([]);
|
||||||
|
setSelectedOrganisation(null);
|
||||||
|
|
||||||
|
toast.error(`Error fetching organisations: ${error}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -240,6 +246,12 @@ export default function App() {
|
|||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error("error fetching projects:", error);
|
console.error("error fetching projects:", error);
|
||||||
|
setProjects([]);
|
||||||
|
setSelectedProject(null);
|
||||||
|
|
||||||
|
toast.error(`Error fetching projects: ${error}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -258,6 +270,10 @@ export default function App() {
|
|||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error("error fetching members:", error);
|
console.error("error fetching members:", error);
|
||||||
setMembers([]);
|
setMembers([]);
|
||||||
|
|
||||||
|
toast.error(`Error fetching members: ${error}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -276,6 +292,10 @@ export default function App() {
|
|||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error("error fetching sprints:", error);
|
console.error("error fetching sprints:", error);
|
||||||
setSprints([]);
|
setSprints([]);
|
||||||
|
|
||||||
|
toast.error(`Error fetching sprints: ${error}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -324,6 +344,11 @@ export default function App() {
|
|||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error("error fetching issues:", error);
|
console.error("error fetching issues:", error);
|
||||||
setIssues([]);
|
setIssues([]);
|
||||||
|
setSelectedIssue(null);
|
||||||
|
|
||||||
|
toast.error(`Error fetching issues: ${error}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -413,9 +438,11 @@ export default function App() {
|
|||||||
}}
|
}}
|
||||||
onCreateProject={async (project) => {
|
onCreateProject={async (project) => {
|
||||||
if (!selectedOrganisation) return;
|
if (!selectedOrganisation) return;
|
||||||
|
|
||||||
toast.success(`Created Project ${project.name}`, {
|
toast.success(`Created Project ${project.name}`, {
|
||||||
dismissible: false,
|
dismissible: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
await refetchProjects(selectedOrganisation.Organisation.id, {
|
await refetchProjects(selectedOrganisation.Organisation.id, {
|
||||||
selectProjectId: project.id,
|
selectProjectId: project.id,
|
||||||
});
|
});
|
||||||
@@ -440,6 +467,11 @@ export default function App() {
|
|||||||
);
|
);
|
||||||
await refetchIssues();
|
await refetchIssues();
|
||||||
}}
|
}}
|
||||||
|
errorAction={async (errorMessage) => {
|
||||||
|
toast.error(`Error creating issue: ${errorMessage}`, {
|
||||||
|
dismissible: false,
|
||||||
|
});
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<CreateSprint
|
<CreateSprint
|
||||||
|
|||||||
Reference in New Issue
Block a user