mirror of
https://github.com/hex248/sprint.git
synced 2026-02-08 10:33:01 +00:00
use ServerQueryInput properly
This commit is contained in:
@@ -1,50 +1,35 @@
|
||||
import type { IssueCreateRequest, IssueRecord } from "@issue/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
|
||||
export async function create({
|
||||
projectId,
|
||||
title,
|
||||
description,
|
||||
sprintId,
|
||||
assigneeId,
|
||||
status,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
projectId: number;
|
||||
title: string;
|
||||
description: string;
|
||||
sprintId?: number | null;
|
||||
assigneeId?: number | null;
|
||||
status?: string;
|
||||
} & ServerQueryInput) {
|
||||
const url = new URL(`${getServerURL()}/issue/create`);
|
||||
url.searchParams.set("projectId", `${projectId}`);
|
||||
url.searchParams.set("title", title.trim());
|
||||
if (description.trim() !== "") url.searchParams.set("description", description.trim());
|
||||
if (sprintId != null) url.searchParams.set("sprintId", `${sprintId}`);
|
||||
if (assigneeId != null) url.searchParams.set("assigneeId", `${assigneeId}`);
|
||||
if (status != null && status.trim() !== "") url.searchParams.set("status", status.trim());
|
||||
|
||||
export async function create(request: IssueCreateRequest & ServerQueryInput<IssueRecord>) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
const csrfToken = getCsrfToken();
|
||||
const headers: HeadersInit = {};
|
||||
if (csrfToken) headers["X-CSRF-Token"] = csrfToken;
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
headers,
|
||||
const res = await fetch(`${getServerURL()}/issue/create`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to create issue (${res.status})`);
|
||||
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();
|
||||
if (!data.id) {
|
||||
toast.error(`failed to create issue (${res.status})`);
|
||||
onError?.(`failed to create issue (${res.status})`);
|
||||
return;
|
||||
}
|
||||
|
||||
onSuccess?.(data, res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { SuccessResponse } from "@issue/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
|
||||
@@ -7,24 +9,27 @@ export async function remove({
|
||||
onError,
|
||||
}: {
|
||||
issueId: number;
|
||||
} & ServerQueryInput) {
|
||||
const url = new URL(`${getServerURL()}/issue/delete`);
|
||||
url.searchParams.set("id", `${issueId}`);
|
||||
|
||||
} & ServerQueryInput<SuccessResponse>) {
|
||||
const csrfToken = getCsrfToken();
|
||||
const headers: HeadersInit = {};
|
||||
if (csrfToken) headers["X-CSRF-Token"] = csrfToken;
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
headers,
|
||||
const res = await fetch(`${getServerURL()}/issue/delete`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify({ id: issueId }),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to delete issue (${res.status})`);
|
||||
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.text();
|
||||
const data = await res.json();
|
||||
onSuccess?.(data, res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,30 @@
|
||||
import type { IssuesReplaceStatusRequest, ReplaceStatusResponse } from "@issue/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
|
||||
export async function replaceStatus({
|
||||
organisationId,
|
||||
oldStatus,
|
||||
newStatus,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
organisationId: number;
|
||||
oldStatus: string;
|
||||
newStatus: string;
|
||||
} & ServerQueryInput) {
|
||||
const url = new URL(`${getServerURL()}/issues/replace-status`);
|
||||
url.searchParams.set("organisationId", `${organisationId}`);
|
||||
url.searchParams.set("oldStatus", oldStatus);
|
||||
url.searchParams.set("newStatus", newStatus);
|
||||
|
||||
export async function replaceStatus(
|
||||
request: IssuesReplaceStatusRequest & ServerQueryInput<ReplaceStatusResponse>,
|
||||
) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
const csrfToken = getCsrfToken();
|
||||
const headers: HeadersInit = {};
|
||||
if (csrfToken) headers["X-CSRF-Token"] = csrfToken;
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
headers,
|
||||
const res = await fetch(`${getServerURL()}/issues/replace-status`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to replace status (${res.status})`);
|
||||
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);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { IssueRecord } from "@issue/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
|
||||
@@ -17,31 +19,32 @@ export async function update({
|
||||
sprintId?: number | null;
|
||||
assigneeId?: number | null;
|
||||
status?: string;
|
||||
} & ServerQueryInput) {
|
||||
const url = new URL(`${getServerURL()}/issue/update`);
|
||||
url.searchParams.set("id", `${issueId}`);
|
||||
if (title !== undefined) url.searchParams.set("title", title);
|
||||
if (description !== undefined) url.searchParams.set("description", description);
|
||||
if (sprintId !== undefined) {
|
||||
url.searchParams.set("sprintId", sprintId === null ? "null" : `${sprintId}`);
|
||||
}
|
||||
if (assigneeId !== undefined) {
|
||||
url.searchParams.set("assigneeId", assigneeId === null ? "null" : `${assigneeId}`);
|
||||
}
|
||||
if (status !== undefined) url.searchParams.set("status", status);
|
||||
|
||||
} & ServerQueryInput<IssueRecord>) {
|
||||
const csrfToken = getCsrfToken();
|
||||
const headers: HeadersInit = {};
|
||||
if (csrfToken) headers["X-CSRF-Token"] = csrfToken;
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
headers,
|
||||
const res = await fetch(`${getServerURL()}/issue/update`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: issueId,
|
||||
title,
|
||||
description,
|
||||
sprintId,
|
||||
assigneeId,
|
||||
status,
|
||||
}),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to update issue (${res.status})`);
|
||||
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);
|
||||
|
||||
@@ -1,35 +1,28 @@
|
||||
import type { OrgAddMemberRequest, OrganisationMemberRecord } from "@issue/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
|
||||
export async function addMember({
|
||||
organisationId,
|
||||
userId,
|
||||
role = "member",
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
organisationId: number;
|
||||
userId: number;
|
||||
role?: string;
|
||||
} & ServerQueryInput) {
|
||||
const url = new URL(`${getServerURL()}/organisation/add-member`);
|
||||
url.searchParams.set("organisationId", `${organisationId}`);
|
||||
url.searchParams.set("userId", `${userId}`);
|
||||
url.searchParams.set("role", role);
|
||||
|
||||
export async function addMember(request: OrgAddMemberRequest & ServerQueryInput<OrganisationMemberRecord>) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
const csrfToken = getCsrfToken();
|
||||
const headers: HeadersInit = {};
|
||||
if (csrfToken) headers["X-CSRF-Token"] = csrfToken;
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
const res = await fetch(`${getServerURL()}/organisation/add-member`, {
|
||||
method: "POST",
|
||||
headers,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to add member (${res.status})`);
|
||||
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);
|
||||
|
||||
@@ -1,23 +1,17 @@
|
||||
import type { OrganisationResponse } from "@issue/shared";
|
||||
import { getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
|
||||
export async function byUser({
|
||||
userId,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
userId: number;
|
||||
} & ServerQueryInput) {
|
||||
const url = new URL(`${getServerURL()}/organisations/by-user`);
|
||||
url.searchParams.set("userId", `${userId}`);
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
export async function byUser({ onSuccess, onError }: ServerQueryInput<OrganisationResponse[]>) {
|
||||
const res = await fetch(`${getServerURL()}/organisations/by-user`, {
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to get organisations (${res.status})`);
|
||||
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);
|
||||
|
||||
@@ -1,44 +1,37 @@
|
||||
import type { OrganisationRecord, OrgCreateRequest } from "@issue/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
|
||||
export async function create({
|
||||
name,
|
||||
slug,
|
||||
userId,
|
||||
description,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
name: string;
|
||||
slug: string;
|
||||
userId: number;
|
||||
description: string;
|
||||
} & ServerQueryInput) {
|
||||
const url = new URL(`${getServerURL()}/organisation/create`);
|
||||
url.searchParams.set("name", name.trim());
|
||||
url.searchParams.set("slug", slug.trim());
|
||||
url.searchParams.set("userId", `${userId}`);
|
||||
if (description.trim() !== "") url.searchParams.set("description", description.trim());
|
||||
|
||||
export async function create(request: OrgCreateRequest & ServerQueryInput<OrganisationRecord>) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
const csrfToken = getCsrfToken();
|
||||
const headers: HeadersInit = {};
|
||||
if (csrfToken) headers["X-CSRF-Token"] = csrfToken;
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
headers,
|
||||
const res = await fetch(`${getServerURL()}/organisation/create`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to create organisation (${res.status})`);
|
||||
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();
|
||||
if (!data.id) {
|
||||
toast.error(`failed to create organisation (${res.status})`);
|
||||
onError?.(`failed to create organisation (${res.status})`);
|
||||
return;
|
||||
}
|
||||
|
||||
onSuccess?.(data, res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export async function members({
|
||||
onError,
|
||||
}: {
|
||||
organisationId: number;
|
||||
} & ServerQueryInput) {
|
||||
} & ServerQueryInput<OrganisationMemberResponse[]>) {
|
||||
const url = new URL(`${getServerURL()}/organisation/members`);
|
||||
url.searchParams.set("organisationId", `${organisationId}`);
|
||||
|
||||
|
||||
@@ -1,32 +1,28 @@
|
||||
import type { OrgRemoveMemberRequest, SuccessResponse } from "@issue/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
|
||||
export async function removeMember({
|
||||
organisationId,
|
||||
userId,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
organisationId: number;
|
||||
userId: number;
|
||||
} & ServerQueryInput) {
|
||||
const url = new URL(`${getServerURL()}/organisation/remove-member`);
|
||||
url.searchParams.set("organisationId", `${organisationId}`);
|
||||
url.searchParams.set("userId", `${userId}`);
|
||||
|
||||
export async function removeMember(request: OrgRemoveMemberRequest & ServerQueryInput<SuccessResponse>) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
const csrfToken = getCsrfToken();
|
||||
const headers: HeadersInit = {};
|
||||
if (csrfToken) headers["X-CSRF-Token"] = csrfToken;
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
const res = await fetch(`${getServerURL()}/organisation/remove-member`, {
|
||||
method: "POST",
|
||||
headers,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to remove member (${res.status})`);
|
||||
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);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { OrganisationRecord } from "@issue/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
|
||||
@@ -15,28 +17,33 @@ export async function update({
|
||||
description?: string;
|
||||
slug?: string;
|
||||
statuses?: Record<string, string>;
|
||||
} & ServerQueryInput) {
|
||||
const url = new URL(`${getServerURL()}/organisation/update`);
|
||||
url.searchParams.set("id", `${organisationId}`);
|
||||
if (name !== undefined) url.searchParams.set("name", name);
|
||||
if (description !== undefined) url.searchParams.set("description", description);
|
||||
if (slug !== undefined) url.searchParams.set("slug", slug);
|
||||
if (statuses !== undefined) {
|
||||
url.searchParams.set("statuses", JSON.stringify(statuses));
|
||||
}
|
||||
|
||||
} & ServerQueryInput<OrganisationRecord>) {
|
||||
const csrfToken = getCsrfToken();
|
||||
const headers: HeadersInit = {};
|
||||
if (csrfToken) headers["X-CSRF-Token"] = csrfToken;
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
headers,
|
||||
const res = await fetch(`${getServerURL()}/organisation/update`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: organisationId,
|
||||
name,
|
||||
description,
|
||||
slug,
|
||||
statuses,
|
||||
}),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to update organisation (${res.status})`);
|
||||
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);
|
||||
|
||||
@@ -1,35 +1,30 @@
|
||||
import type { OrganisationMemberRecord, OrgUpdateMemberRoleRequest } from "@issue/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
|
||||
export async function updateMemberRole({
|
||||
organisationId,
|
||||
userId,
|
||||
role,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
organisationId: number;
|
||||
userId: number;
|
||||
role: string;
|
||||
} & ServerQueryInput) {
|
||||
const url = new URL(`${getServerURL()}/organisation/update-member-role`);
|
||||
url.searchParams.set("organisationId", `${organisationId}`);
|
||||
url.searchParams.set("userId", `${userId}`);
|
||||
url.searchParams.set("role", role);
|
||||
|
||||
export async function updateMemberRole(
|
||||
request: OrgUpdateMemberRoleRequest & ServerQueryInput<OrganisationMemberRecord>,
|
||||
) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
const csrfToken = getCsrfToken();
|
||||
const headers: HeadersInit = {};
|
||||
if (csrfToken) headers["X-CSRF-Token"] = csrfToken;
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
const res = await fetch(`${getServerURL()}/organisation/update-member-role`, {
|
||||
method: "POST",
|
||||
headers,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to update member role (${res.status})`);
|
||||
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);
|
||||
|
||||
@@ -1,44 +1,35 @@
|
||||
import type { ProjectCreateRequest, ProjectRecord } from "@issue/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
|
||||
export async function create({
|
||||
key,
|
||||
name,
|
||||
creatorId,
|
||||
organisationId,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
key: string;
|
||||
name: string;
|
||||
creatorId: number;
|
||||
organisationId: number;
|
||||
} & ServerQueryInput) {
|
||||
const url = new URL(`${getServerURL()}/project/create`);
|
||||
url.searchParams.set("key", key.trim());
|
||||
url.searchParams.set("name", name.trim());
|
||||
url.searchParams.set("creatorId", `${creatorId}`);
|
||||
url.searchParams.set("organisationId", `${organisationId}`);
|
||||
|
||||
export async function create(request: ProjectCreateRequest & ServerQueryInput<ProjectRecord>) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
const csrfToken = getCsrfToken();
|
||||
const headers: HeadersInit = {};
|
||||
if (csrfToken) headers["X-CSRF-Token"] = csrfToken;
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
headers,
|
||||
const res = await fetch(`${getServerURL()}/project/create`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to create project (${res.status})`);
|
||||
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();
|
||||
if (!data.id) {
|
||||
toast.error(`failed to create project (${res.status})`);
|
||||
onError?.(`failed to create project (${res.status})`);
|
||||
return;
|
||||
}
|
||||
|
||||
onSuccess?.(data, res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { SprintRecord } from "@issue/shared";
|
||||
import { getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
|
||||
@@ -7,7 +8,7 @@ export async function byProject({
|
||||
onError,
|
||||
}: {
|
||||
projectId: number;
|
||||
} & ServerQueryInput) {
|
||||
} & ServerQueryInput<SprintRecord[]>) {
|
||||
const url = new URL(`${getServerURL()}/sprints/by-project`);
|
||||
url.searchParams.set("projectId", `${projectId}`);
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { SprintRecord } from "@issue/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
|
||||
@@ -12,36 +14,41 @@ export async function create({
|
||||
}: {
|
||||
projectId: number;
|
||||
name: string;
|
||||
color: string;
|
||||
color?: string;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
} & ServerQueryInput) {
|
||||
const url = new URL(`${getServerURL()}/sprint/create`);
|
||||
url.searchParams.set("projectId", `${projectId}`);
|
||||
url.searchParams.set("name", name.trim());
|
||||
url.searchParams.set("color", color);
|
||||
url.searchParams.set("startDate", startDate.toISOString());
|
||||
url.searchParams.set("endDate", endDate.toISOString());
|
||||
|
||||
} & ServerQueryInput<SprintRecord>) {
|
||||
const csrfToken = getCsrfToken();
|
||||
const headers: HeadersInit = {};
|
||||
if (csrfToken) headers["X-CSRF-Token"] = csrfToken;
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
headers,
|
||||
const res = await fetch(`${getServerURL()}/sprint/create`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
projectId,
|
||||
name: name.trim(),
|
||||
color,
|
||||
startDate: startDate.toISOString(),
|
||||
endDate: endDate.toISOString(),
|
||||
}),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to create sprint (${res.status})`);
|
||||
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();
|
||||
if (!data.id) {
|
||||
toast.error(`failed to create sprint (${res.status})`);
|
||||
onError?.(`failed to create sprint (${res.status})`);
|
||||
return;
|
||||
}
|
||||
|
||||
onSuccess?.(data, res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,28 @@
|
||||
import type { TimerEndRequest, TimerState } from "@issue/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
|
||||
export async function end({
|
||||
issueId,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
issueId: number;
|
||||
} & ServerQueryInput) {
|
||||
const url = new URL(`${getServerURL()}/timer/end`);
|
||||
url.searchParams.set("issueId", `${issueId}`);
|
||||
|
||||
export async function end(request: TimerEndRequest & ServerQueryInput<TimerState>) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
const csrfToken = getCsrfToken();
|
||||
const headers: HeadersInit = {};
|
||||
if (csrfToken) headers["X-CSRF-Token"] = csrfToken;
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
const res = await fetch(`${getServerURL()}/timer/end`, {
|
||||
method: "POST",
|
||||
headers,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to end timer (${res.status})`);
|
||||
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);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { TimerState } from "@issue/shared";
|
||||
import { getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
|
||||
export async function get({
|
||||
@@ -7,22 +8,19 @@ export async function get({
|
||||
onError,
|
||||
}: {
|
||||
issueId: number;
|
||||
} & ServerQueryInput) {
|
||||
} & ServerQueryInput<TimerState>) {
|
||||
const url = new URL(`${getServerURL()}/timer/get`);
|
||||
url.searchParams.set("issueId", `${issueId}`);
|
||||
|
||||
const csrfToken = getCsrfToken();
|
||||
const headers: HeadersInit = {};
|
||||
if (csrfToken) headers["X-CSRF-Token"] = csrfToken;
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
headers,
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to get timer (${res.status})`);
|
||||
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);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { TimerState } from "@issue/shared";
|
||||
import { getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
|
||||
export async function getInactive({
|
||||
@@ -7,24 +8,21 @@ export async function getInactive({
|
||||
onError,
|
||||
}: {
|
||||
issueId: number;
|
||||
} & ServerQueryInput) {
|
||||
} & ServerQueryInput<TimerState[]>) {
|
||||
const url = new URL(`${getServerURL()}/timer/get-inactive`);
|
||||
url.searchParams.set("issueId", `${issueId}`);
|
||||
|
||||
const csrfToken = getCsrfToken();
|
||||
const headers: HeadersInit = {};
|
||||
if (csrfToken) headers["X-CSRF-Token"] = csrfToken;
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
headers,
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to get timers (${res.status})`);
|
||||
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);
|
||||
onSuccess?.(data || [], res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,28 @@
|
||||
import type { TimerState, TimerToggleRequest } from "@issue/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
|
||||
export async function toggle({
|
||||
issueId,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
issueId: number;
|
||||
} & ServerQueryInput) {
|
||||
const url = new URL(`${getServerURL()}/timer/toggle`);
|
||||
url.searchParams.set("issueId", `${issueId}`);
|
||||
|
||||
export async function toggle(request: TimerToggleRequest & ServerQueryInput<TimerState>) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
const csrfToken = getCsrfToken();
|
||||
const headers: HeadersInit = {};
|
||||
if (csrfToken) headers["X-CSRF-Token"] = csrfToken;
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
const res = await fetch(`${getServerURL()}/timer/toggle`, {
|
||||
method: "POST",
|
||||
headers,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to toggle timer (${res.status})`);
|
||||
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);
|
||||
|
||||
@@ -8,7 +8,7 @@ export async function byUsername({
|
||||
onError,
|
||||
}: {
|
||||
username: string;
|
||||
} & ServerQueryInput) {
|
||||
} & ServerQueryInput<UserRecord>) {
|
||||
const url = new URL(`${getServerURL()}/user/by-username`);
|
||||
url.searchParams.set("username", username);
|
||||
|
||||
@@ -17,8 +17,10 @@ export async function byUsername({
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to get user (${res.status})`);
|
||||
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);
|
||||
|
||||
@@ -1,44 +1,35 @@
|
||||
import type { UserRecord, UserUpdateRequest } from "@issue/shared";
|
||||
import { toast } from "sonner";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import type { ServerQueryInput } from "..";
|
||||
|
||||
export async function update({
|
||||
id,
|
||||
name,
|
||||
password,
|
||||
avatarURL,
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
id: number;
|
||||
name: string;
|
||||
password: string;
|
||||
avatarURL: string | null;
|
||||
} & ServerQueryInput) {
|
||||
const url = new URL(`${getServerURL()}/user/update`);
|
||||
url.searchParams.set("id", `${id}`);
|
||||
url.searchParams.set("name", name.trim());
|
||||
url.searchParams.set("password", password.trim());
|
||||
url.searchParams.set("avatarURL", avatarURL || "null");
|
||||
|
||||
export async function update(request: UserUpdateRequest & ServerQueryInput<UserRecord>) {
|
||||
const { onSuccess, onError, ...body } = request;
|
||||
const csrfToken = getCsrfToken();
|
||||
const headers: HeadersInit = {};
|
||||
if (csrfToken) headers["X-CSRF-Token"] = csrfToken;
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
headers,
|
||||
const res = await fetch(`${getServerURL()}/user/update`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(csrfToken ? { "X-CSRF-Token": csrfToken } : {}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `failed to update user (${res.status})`);
|
||||
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();
|
||||
if (!data.id) {
|
||||
toast.error(`failed to update user (${res.status})`);
|
||||
onError?.(`failed to update user (${res.status})`);
|
||||
return;
|
||||
}
|
||||
|
||||
onSuccess?.(data, res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export async function uploadAvatar({
|
||||
onError,
|
||||
}: {
|
||||
file: File;
|
||||
} & ServerQueryInput) {
|
||||
} & ServerQueryInput<string>) {
|
||||
const MAX_FILE_SIZE = 5 * 1024 * 1024;
|
||||
const ALLOWED_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif"];
|
||||
|
||||
@@ -36,8 +36,10 @@ export async function uploadAvatar({
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.text();
|
||||
onError?.(error || `Failed to upload avatar (${res.status})`);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user