mirror of
https://github.com/hex248/sprint.git
synced 2026-02-07 18:23:03 +00:00
refactored frontend api helpers to promise interface
This commit is contained in:
@@ -7,13 +7,26 @@ export * as sprint from "@/lib/server/sprint";
|
||||
export * as timer from "@/lib/server/timer";
|
||||
export * as user from "@/lib/server/user";
|
||||
|
||||
export type ServerQueryInput<T = unknown> = {
|
||||
onSuccess?: (data: T, res: Response) => void;
|
||||
onError?: (error: ApiError | string) => void;
|
||||
};
|
||||
export async function getErrorMessage(res: Response, fallback: string): Promise<string> {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
if (typeof error === "string") {
|
||||
return error || fallback;
|
||||
}
|
||||
if (error && typeof error === "object") {
|
||||
if ("details" in error && error.details) {
|
||||
const messages = Object.values(error.details as Record<string, string[]>).flat();
|
||||
if (messages.length > 0) return messages.join(", ");
|
||||
}
|
||||
if ("error" in error && typeof error.error === "string") {
|
||||
return error.error || fallback;
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
export function parseError(error: ApiError | string): string {
|
||||
export function parseError(error: ApiError | string | Error): string {
|
||||
if (typeof error === "string") return error;
|
||||
if (error instanceof Error) return error.message;
|
||||
if (error.details) {
|
||||
const messages = Object.values(error.details).flat();
|
||||
return messages.join(", ");
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import type { IssueResponse } from "@sprint/shared";
|
||||
import { getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function byProject({
|
||||
projectId,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
projectId: number;
|
||||
} & ServerQueryInput) {
|
||||
export async function byProject(projectId: number): Promise<IssueResponse[]> {
|
||||
const url = new URL(`${getServerURL()}/issues/by-project`);
|
||||
url.searchParams.set("projectId", `${projectId}`);
|
||||
|
||||
@@ -16,11 +11,9 @@ export async function byProject({
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to get issues by project (${res.status})`);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
const message = await getErrorMessage(res, `failed to get issues by project (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
onSuccess?.(data, res);
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import type { IssueCreateRequest, IssueRecord } from "@sprint/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function create(request: IssueCreateRequest & ServerQueryInput<IssueRecord>) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
export async function create(request: IssueCreateRequest): Promise<IssueRecord> {
|
||||
const csrfToken = getCsrfToken();
|
||||
|
||||
const res = await fetch(`${getServerURL()}/issue/create`, {
|
||||
@@ -13,23 +11,18 @@ export async function create(request: IssueCreateRequest & ServerQueryInput<Issu
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
body: JSON.stringify(request),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to create issue (${res.status})`;
|
||||
toast.error(message);
|
||||
onError?.(error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
const message = await getErrorMessage(res, `failed to create issue (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
const data = (await res.json()) as IssueRecord;
|
||||
if (!data.id) {
|
||||
toast.error(`failed to create issue (${res.status})`);
|
||||
onError?.(`failed to create issue (${res.status})`);
|
||||
return;
|
||||
}
|
||||
onSuccess?.(data, res);
|
||||
throw new Error(`failed to create issue (${res.status})`);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
import type { SuccessResponse } from "@sprint/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function remove({
|
||||
issueId,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
issueId: number;
|
||||
} & ServerQueryInput<SuccessResponse>) {
|
||||
export async function remove(issueId: number): Promise<SuccessResponse> {
|
||||
const csrfToken = getCsrfToken();
|
||||
|
||||
const res = await fetch(`${getServerURL()}/issue/delete`, {
|
||||
@@ -23,13 +16,9 @@ export async function remove({
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to delete issue (${res.status})`;
|
||||
toast.error(message);
|
||||
onError?.(error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to delete issue (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import type { IssuesReplaceStatusRequest, ReplaceStatusResponse } from "@sprint/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function replaceStatus(
|
||||
request: IssuesReplaceStatusRequest & ServerQueryInput<ReplaceStatusResponse>,
|
||||
) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
export async function replaceStatus(request: IssuesReplaceStatusRequest): Promise<ReplaceStatusResponse> {
|
||||
const csrfToken = getCsrfToken();
|
||||
|
||||
const res = await fetch(`${getServerURL()}/issues/replace-status`, {
|
||||
@@ -15,18 +11,14 @@ export async function replaceStatus(
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
body: JSON.stringify(request),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to replace status (${res.status})`;
|
||||
toast.error(message);
|
||||
onError?.(error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to replace status (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
import type { StatusCountResponse } from "@sprint/shared";
|
||||
import { getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function statusCount({
|
||||
organisationId,
|
||||
status,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
organisationId: number;
|
||||
status: string;
|
||||
} & ServerQueryInput) {
|
||||
export async function statusCount(organisationId: number, status: string): Promise<StatusCountResponse> {
|
||||
const url = new URL(`${getServerURL()}/issues/status-count`);
|
||||
url.searchParams.set("organisationId", `${organisationId}`);
|
||||
url.searchParams.set("status", status);
|
||||
@@ -19,10 +12,9 @@ export async function statusCount({
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to get issue status count (${res.status})`);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to get issue status count (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,25 +1,8 @@
|
||||
import type { IssueRecord } from "@sprint/shared";
|
||||
import { toast } from "sonner";
|
||||
import type { IssueRecord, IssueUpdateRequest } from "@sprint/shared";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function update({
|
||||
issueId,
|
||||
title,
|
||||
description,
|
||||
sprintId,
|
||||
assigneeIds,
|
||||
status,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
issueId: number;
|
||||
title?: string;
|
||||
description?: string;
|
||||
sprintId?: number | null;
|
||||
assigneeIds?: number[] | null;
|
||||
status?: string;
|
||||
} & ServerQueryInput<IssueRecord>) {
|
||||
export async function update(input: IssueUpdateRequest): Promise<IssueRecord> {
|
||||
const csrfToken = getCsrfToken();
|
||||
|
||||
const res = await fetch(`${getServerURL()}/issue/update`, {
|
||||
@@ -28,25 +11,14 @@ export async function update({
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: issueId,
|
||||
title,
|
||||
description,
|
||||
sprintId,
|
||||
assigneeIds,
|
||||
status,
|
||||
}),
|
||||
body: JSON.stringify(input),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to update issue (${res.status})`;
|
||||
toast.error(message);
|
||||
onError?.(error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to update issue (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import type { OrgAddMemberRequest, OrganisationMemberRecord } from "@sprint/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function addMember(request: OrgAddMemberRequest & ServerQueryInput<OrganisationMemberRecord>) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
export async function addMember(request: OrgAddMemberRequest): Promise<OrganisationMemberRecord> {
|
||||
const csrfToken = getCsrfToken();
|
||||
|
||||
const res = await fetch(`${getServerURL()}/organisation/add-member`, {
|
||||
@@ -13,18 +11,14 @@ export async function addMember(request: OrgAddMemberRequest & ServerQueryInput<
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
body: JSON.stringify(request),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to add member (${res.status})`;
|
||||
toast.error(message);
|
||||
onError?.(error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to add member (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
import type { OrganisationResponse } from "@sprint/shared";
|
||||
import { getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function byUser({ onSuccess, onError }: ServerQueryInput<OrganisationResponse[]>) {
|
||||
export async function byUser(): Promise<OrganisationResponse[]> {
|
||||
const res = await fetch(`${getServerURL()}/organisations/by-user`, {
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to get organisations (${res.status})`;
|
||||
onError?.(message);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to get organisations (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import type { OrganisationRecord, OrgCreateRequest } from "@sprint/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function create(request: OrgCreateRequest & ServerQueryInput<OrganisationRecord>) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
export async function create(request: OrgCreateRequest): Promise<OrganisationRecord> {
|
||||
const csrfToken = getCsrfToken();
|
||||
|
||||
const res = await fetch(`${getServerURL()}/organisation/create`, {
|
||||
@@ -13,25 +11,18 @@ export async function create(request: OrgCreateRequest & ServerQueryInput<Organi
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
body: JSON.stringify(request),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string"
|
||||
? error
|
||||
: error.error || `failed to create organisation (${res.status})`;
|
||||
toast.error(message);
|
||||
onError?.(error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
const message = await getErrorMessage(res, `failed to create organisation (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
const data = (await res.json()) as OrganisationRecord;
|
||||
if (!data.id) {
|
||||
toast.error(`failed to create organisation (${res.status})`);
|
||||
onError?.(`failed to create organisation (${res.status})`);
|
||||
return;
|
||||
}
|
||||
onSuccess?.(data, res);
|
||||
throw new Error(`failed to create organisation (${res.status})`);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
import type { SuccessResponse } from "@sprint/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function remove({
|
||||
organisationId,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
organisationId: number;
|
||||
} & ServerQueryInput<SuccessResponse>) {
|
||||
export async function remove(organisationId: number): Promise<SuccessResponse> {
|
||||
const csrfToken = getCsrfToken();
|
||||
|
||||
const res = await fetch(`${getServerURL()}/organisation/delete`, {
|
||||
@@ -23,15 +16,9 @@ export async function remove({
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string"
|
||||
? error
|
||||
: error.error || `failed to delete organisation (${res.status})`;
|
||||
toast.error(message);
|
||||
onError?.(error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to delete organisation (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import type { OrganisationMemberResponse } from "@sprint/shared";
|
||||
import { getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function members({
|
||||
organisationId,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
organisationId: number;
|
||||
} & ServerQueryInput<OrganisationMemberResponse[]>) {
|
||||
export async function members(organisationId: number): Promise<OrganisationMemberResponse[]> {
|
||||
const url = new URL(`${getServerURL()}/organisation/members`);
|
||||
url.searchParams.set("organisationId", `${organisationId}`);
|
||||
|
||||
@@ -17,10 +11,9 @@ export async function members({
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to get members (${res.status})`);
|
||||
} else {
|
||||
const data = (await res.json()) as OrganisationMemberResponse[];
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to get members (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import type { OrgRemoveMemberRequest, SuccessResponse } from "@sprint/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function removeMember(request: OrgRemoveMemberRequest & ServerQueryInput<SuccessResponse>) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
export async function removeMember(request: OrgRemoveMemberRequest): Promise<SuccessResponse> {
|
||||
const csrfToken = getCsrfToken();
|
||||
|
||||
const res = await fetch(`${getServerURL()}/organisation/remove-member`, {
|
||||
@@ -13,18 +11,14 @@ export async function removeMember(request: OrgRemoveMemberRequest & ServerQuery
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
body: JSON.stringify(request),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to remove member (${res.status})`;
|
||||
toast.error(message);
|
||||
onError?.(error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to remove member (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,23 +1,8 @@
|
||||
import type { OrganisationRecord } from "@sprint/shared";
|
||||
import { toast } from "sonner";
|
||||
import type { OrganisationRecord, OrgUpdateRequest } from "@sprint/shared";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function update({
|
||||
organisationId,
|
||||
name,
|
||||
description,
|
||||
slug,
|
||||
statuses,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
organisationId: number;
|
||||
name?: string;
|
||||
description?: string;
|
||||
slug?: string;
|
||||
statuses?: Record<string, string>;
|
||||
} & ServerQueryInput<OrganisationRecord>) {
|
||||
export async function update(input: OrgUpdateRequest): Promise<OrganisationRecord> {
|
||||
const csrfToken = getCsrfToken();
|
||||
|
||||
const res = await fetch(`${getServerURL()}/organisation/update`, {
|
||||
@@ -26,26 +11,14 @@ export async function update({
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: organisationId,
|
||||
name,
|
||||
description,
|
||||
slug,
|
||||
statuses,
|
||||
}),
|
||||
body: JSON.stringify(input),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string"
|
||||
? error
|
||||
: error.error || `failed to update organisation (${res.status})`;
|
||||
toast.error(message);
|
||||
onError?.(error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to update organisation (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import type { OrganisationMemberRecord, OrgUpdateMemberRoleRequest } from "@sprint/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function updateMemberRole(
|
||||
request: OrgUpdateMemberRoleRequest & ServerQueryInput<OrganisationMemberRecord>,
|
||||
) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
request: OrgUpdateMemberRoleRequest,
|
||||
): Promise<OrganisationMemberRecord> {
|
||||
const csrfToken = getCsrfToken();
|
||||
|
||||
const res = await fetch(`${getServerURL()}/organisation/update-member-role`, {
|
||||
@@ -15,18 +13,14 @@ export async function updateMemberRole(
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
body: JSON.stringify(request),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to update member role (${res.status})`;
|
||||
toast.error(message);
|
||||
onError?.(error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to update member role (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import type { ProjectResponse } from "@sprint/shared";
|
||||
import { getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function byOrganisation({
|
||||
organisationId,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
organisationId: number;
|
||||
} & ServerQueryInput) {
|
||||
export async function byOrganisation(organisationId: number): Promise<ProjectResponse[]> {
|
||||
const url = new URL(`${getServerURL()}/projects/by-organisation`);
|
||||
url.searchParams.set("organisationId", `${organisationId}`);
|
||||
|
||||
@@ -16,11 +11,9 @@ export async function byOrganisation({
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to get projects by organisation (${res.status})`);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
const message = await getErrorMessage(res, `failed to get projects by organisation (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
onSuccess?.(data, res);
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import type { ProjectCreateRequest, ProjectRecord } from "@sprint/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function create(request: ProjectCreateRequest & ServerQueryInput<ProjectRecord>) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
export async function create(request: ProjectCreateRequest): Promise<ProjectRecord> {
|
||||
const csrfToken = getCsrfToken();
|
||||
|
||||
const res = await fetch(`${getServerURL()}/project/create`, {
|
||||
@@ -13,23 +11,18 @@ export async function create(request: ProjectCreateRequest & ServerQueryInput<Pr
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
body: JSON.stringify(request),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to create project (${res.status})`;
|
||||
toast.error(message);
|
||||
onError?.(error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
const message = await getErrorMessage(res, `failed to create project (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
const data = (await res.json()) as ProjectRecord;
|
||||
if (!data.id) {
|
||||
toast.error(`failed to create project (${res.status})`);
|
||||
onError?.(`failed to create project (${res.status})`);
|
||||
return;
|
||||
}
|
||||
onSuccess?.(data, res);
|
||||
throw new Error(`failed to create project (${res.status})`);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
import type { SuccessResponse } from "@sprint/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function remove({
|
||||
projectId,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
projectId: number;
|
||||
} & ServerQueryInput<SuccessResponse>) {
|
||||
export async function remove(projectId: number): Promise<SuccessResponse> {
|
||||
const csrfToken = getCsrfToken();
|
||||
|
||||
const res = await fetch(`${getServerURL()}/project/delete`, {
|
||||
@@ -23,13 +16,9 @@ export async function remove({
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to delete project (${res.status})`;
|
||||
toast.error(message);
|
||||
onError?.(error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to delete project (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,19 +1,8 @@
|
||||
import type { ProjectRecord } from "@sprint/shared";
|
||||
import { toast } from "sonner";
|
||||
import type { ProjectRecord, ProjectUpdateRequest } from "@sprint/shared";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function update({
|
||||
projectId,
|
||||
key,
|
||||
name,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
projectId: number;
|
||||
key?: string;
|
||||
name?: string;
|
||||
} & ServerQueryInput<ProjectRecord>) {
|
||||
export async function update(input: ProjectUpdateRequest): Promise<ProjectRecord> {
|
||||
const csrfToken = getCsrfToken();
|
||||
|
||||
const res = await fetch(`${getServerURL()}/project/update`, {
|
||||
@@ -22,22 +11,14 @@ export async function update({
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: projectId,
|
||||
key,
|
||||
name,
|
||||
}),
|
||||
body: JSON.stringify(input),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to update project (${res.status})`;
|
||||
toast.error(message);
|
||||
onError?.(error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to update project (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import type { SprintRecord } from "@sprint/shared";
|
||||
import { getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function byProject({
|
||||
projectId,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
projectId: number;
|
||||
} & ServerQueryInput<SprintRecord[]>) {
|
||||
export async function byProject(projectId: number): Promise<SprintRecord[]> {
|
||||
const url = new URL(`${getServerURL()}/sprints/by-project`);
|
||||
url.searchParams.set("projectId", `${projectId}`);
|
||||
|
||||
@@ -17,10 +11,9 @@ export async function byProject({
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to get sprints (${res.status})`);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to get sprints (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,23 +1,8 @@
|
||||
import type { SprintRecord } from "@sprint/shared";
|
||||
import { toast } from "sonner";
|
||||
import type { SprintCreateRequest, SprintRecord } from "@sprint/shared";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function create({
|
||||
projectId,
|
||||
name,
|
||||
color,
|
||||
startDate,
|
||||
endDate,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
projectId: number;
|
||||
name: string;
|
||||
color?: string;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
} & ServerQueryInput<SprintRecord>) {
|
||||
export async function create(input: SprintCreateRequest): Promise<SprintRecord> {
|
||||
const csrfToken = getCsrfToken();
|
||||
|
||||
const res = await fetch(`${getServerURL()}/sprint/create`, {
|
||||
@@ -27,28 +12,20 @@ export async function create({
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
projectId,
|
||||
name: name.trim(),
|
||||
color,
|
||||
startDate: startDate.toISOString(),
|
||||
endDate: endDate.toISOString(),
|
||||
...input,
|
||||
name: input.name.trim(),
|
||||
}),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to create sprint (${res.status})`;
|
||||
toast.error(message);
|
||||
onError?.(error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
const message = await getErrorMessage(res, `failed to create sprint (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
const data = (await res.json()) as SprintRecord;
|
||||
if (!data.id) {
|
||||
toast.error(`failed to create sprint (${res.status})`);
|
||||
onError?.(`failed to create sprint (${res.status})`);
|
||||
return;
|
||||
}
|
||||
onSuccess?.(data, res);
|
||||
throw new Error(`failed to create sprint (${res.status})`);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
import type { SuccessResponse } from "@sprint/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function remove({
|
||||
sprintId,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
sprintId: number;
|
||||
} & ServerQueryInput<SuccessResponse>) {
|
||||
export async function remove(sprintId: number): Promise<SuccessResponse> {
|
||||
const csrfToken = getCsrfToken();
|
||||
|
||||
const res = await fetch(`${getServerURL()}/sprint/delete`, {
|
||||
@@ -23,13 +16,9 @@ export async function remove({
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to delete sprint (${res.status})`;
|
||||
toast.error(message);
|
||||
onError?.(error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to delete sprint (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,23 +1,8 @@
|
||||
import type { SprintRecord } from "@sprint/shared";
|
||||
import { toast } from "sonner";
|
||||
import type { SprintRecord, SprintUpdateRequest } from "@sprint/shared";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function update({
|
||||
sprintId,
|
||||
name,
|
||||
color,
|
||||
startDate,
|
||||
endDate,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
sprintId: number;
|
||||
name?: string;
|
||||
color?: string;
|
||||
startDate?: Date;
|
||||
endDate?: Date;
|
||||
} & ServerQueryInput<SprintRecord>) {
|
||||
export async function update(input: SprintUpdateRequest): Promise<SprintRecord> {
|
||||
const csrfToken = getCsrfToken();
|
||||
|
||||
const res = await fetch(`${getServerURL()}/sprint/update`, {
|
||||
@@ -27,23 +12,16 @@ export async function update({
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: sprintId,
|
||||
name: name?.trim(),
|
||||
color,
|
||||
startDate: startDate?.toISOString(),
|
||||
endDate: endDate?.toISOString(),
|
||||
...input,
|
||||
name: input.name?.trim(),
|
||||
}),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to update sprint (${res.status})`;
|
||||
toast.error(message);
|
||||
onError?.(error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to update sprint (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import type { TimerEndRequest, TimerState } from "@sprint/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function end(request: TimerEndRequest & ServerQueryInput<TimerState>) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
export async function end(request: TimerEndRequest): Promise<TimerState> {
|
||||
const csrfToken = getCsrfToken();
|
||||
|
||||
const res = await fetch(`${getServerURL()}/timer/end`, {
|
||||
@@ -13,18 +11,14 @@ export async function end(request: TimerEndRequest & ServerQueryInput<TimerState
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
body: JSON.stringify(request),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to end timer (${res.status})`;
|
||||
toast.error(message);
|
||||
onError?.(error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to end timer (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import type { TimerState } from "@sprint/shared";
|
||||
import { getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function get({
|
||||
issueId,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
issueId: number;
|
||||
} & ServerQueryInput<TimerState>) {
|
||||
export async function get(issueId: number): Promise<TimerState> {
|
||||
const url = new URL(`${getServerURL()}/timer/get`);
|
||||
url.searchParams.set("issueId", `${issueId}`);
|
||||
|
||||
@@ -17,12 +11,9 @@ export async function get({
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to get timer (${res.status})`;
|
||||
onError?.(message);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to get timer (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import type { TimerState } from "@sprint/shared";
|
||||
import { getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function getInactive({
|
||||
issueId,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
issueId: number;
|
||||
} & ServerQueryInput<TimerState[]>) {
|
||||
export async function getInactive(issueId: number): Promise<TimerState[]> {
|
||||
const url = new URL(`${getServerURL()}/timer/get-inactive`);
|
||||
url.searchParams.set("issueId", `${issueId}`);
|
||||
|
||||
@@ -17,12 +11,10 @@ export async function getInactive({
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to get timers (${res.status})`;
|
||||
onError?.(message);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data || [], res);
|
||||
const message = await getErrorMessage(res, `failed to get timers (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
const data = (await res.json()) as TimerState[];
|
||||
return data ?? [];
|
||||
}
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function list({
|
||||
limit,
|
||||
offset,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
type TimerListInput = {
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
} & ServerQueryInput) {
|
||||
};
|
||||
|
||||
export async function list(input: TimerListInput = {}): Promise<unknown> {
|
||||
const url = new URL(`${getServerURL()}/timers`);
|
||||
if (limit != null) url.searchParams.set("limit", `${limit}`);
|
||||
if (offset != null) url.searchParams.set("offset", `${offset}`);
|
||||
if (input.limit != null) url.searchParams.set("limit", `${input.limit}`);
|
||||
if (input.offset != null) url.searchParams.set("offset", `${input.offset}`);
|
||||
|
||||
const csrfToken = getCsrfToken();
|
||||
const headers: HeadersInit = {};
|
||||
@@ -24,10 +21,9 @@ export async function list({
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to get timers (${res.status})`);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to get timers (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import type { TimerState, TimerToggleRequest } from "@sprint/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function toggle(request: TimerToggleRequest & ServerQueryInput<TimerState>) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
export async function toggle(request: TimerToggleRequest): Promise<TimerState> {
|
||||
const csrfToken = getCsrfToken();
|
||||
|
||||
const res = await fetch(`${getServerURL()}/timer/toggle`, {
|
||||
@@ -13,18 +11,14 @@ export async function toggle(request: TimerToggleRequest & ServerQueryInput<Time
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
body: JSON.stringify(request),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to toggle timer (${res.status})`;
|
||||
toast.error(message);
|
||||
onError?.(error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to toggle timer (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
import type { UserRecord } from "@sprint/shared";
|
||||
import { getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function byUsername({
|
||||
username,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
username: string;
|
||||
} & ServerQueryInput<UserRecord>) {
|
||||
export async function byUsername(username: string): Promise<UserRecord> {
|
||||
const url = new URL(`${getServerURL()}/user/by-username`);
|
||||
url.searchParams.set("username", username);
|
||||
|
||||
@@ -17,12 +11,9 @@ export async function byUsername({
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to get user (${res.status})`;
|
||||
onError?.(message);
|
||||
} else {
|
||||
const data = (await res.json()) as UserRecord;
|
||||
onSuccess?.(data, res);
|
||||
const message = await getErrorMessage(res, `failed to get user (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import type { UserRecord, UserUpdateRequest } from "@sprint/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function update(request: UserUpdateRequest & ServerQueryInput<UserRecord>) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
export async function update(request: UserUpdateRequest): Promise<UserRecord> {
|
||||
const csrfToken = getCsrfToken();
|
||||
|
||||
const res = await fetch(`${getServerURL()}/user/update`, {
|
||||
@@ -13,23 +11,18 @@ export async function update(request: UserUpdateRequest & ServerQueryInput<UserR
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
body: JSON.stringify(request),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `failed to update user (${res.status})`;
|
||||
toast.error(message);
|
||||
onError?.(error);
|
||||
} else {
|
||||
const data = await res.json();
|
||||
const message = await getErrorMessage(res, `failed to update user (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
const data = (await res.json()) as UserRecord;
|
||||
if (!data.id) {
|
||||
toast.error(`failed to update user (${res.status})`);
|
||||
onError?.(`failed to update user (${res.status})`);
|
||||
return;
|
||||
}
|
||||
onSuccess?.(data, res);
|
||||
throw new Error(`failed to update user (${res.status})`);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -1,24 +1,16 @@
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function uploadAvatar({
|
||||
file,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
file: File;
|
||||
} & ServerQueryInput<string>) {
|
||||
export async function uploadAvatar(file: File): Promise<string> {
|
||||
const MAX_FILE_SIZE = 5 * 1024 * 1024;
|
||||
const ALLOWED_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif"];
|
||||
|
||||
if (file.size > MAX_FILE_SIZE) {
|
||||
onError?.("File size exceeds 5MB limit");
|
||||
return;
|
||||
throw new Error("File size exceeds 5MB limit");
|
||||
}
|
||||
|
||||
if (!ALLOWED_TYPES.includes(file.type)) {
|
||||
onError?.("Invalid file type. Allowed types: png, jpg, jpeg, webp, gif");
|
||||
return;
|
||||
throw new Error("Invalid file type. Allowed types: png, jpg, jpeg, webp, gif");
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
@@ -36,17 +28,14 @@ export async function uploadAvatar({
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json().catch(() => res.text());
|
||||
const message =
|
||||
typeof error === "string" ? error : error.error || `Failed to upload avatar (${res.status})`;
|
||||
onError?.(message);
|
||||
return;
|
||||
const message = await getErrorMessage(res, `Failed to upload avatar (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
if (data.avatarURL) {
|
||||
onSuccess?.(data.avatarURL, res);
|
||||
} else {
|
||||
onError?.("Failed to upload avatar");
|
||||
return data.avatarURL;
|
||||
}
|
||||
|
||||
throw new Error("Failed to upload avatar");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user