mirror of
https://github.com/hex248/sprint.git
synced 2026-02-08 18:33:01 +00:00
improved timer system and overlay
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type {
|
||||
IssueByIdQuery,
|
||||
IssueCreateRequest,
|
||||
IssueRecord,
|
||||
IssueResponse,
|
||||
@@ -21,6 +22,14 @@ export function useIssues(projectId?: number | null) {
|
||||
});
|
||||
}
|
||||
|
||||
export function useIssueById(issueId?: IssueByIdQuery["issueId"] | null) {
|
||||
return useQuery<IssueResponse>({
|
||||
queryKey: queryKeys.issues.byId(issueId ?? 0),
|
||||
queryFn: () => issue.byId(issueId ?? 0),
|
||||
enabled: Boolean(issueId),
|
||||
});
|
||||
}
|
||||
|
||||
export function useIssueStatusCount(organisationId?: number | null, status?: string | null) {
|
||||
return useQuery<StatusCountResponse>({
|
||||
queryKey: queryKeys.issues.statusCount(organisationId ?? 0, status ?? ""),
|
||||
|
||||
@@ -1,15 +1,28 @@
|
||||
import type { TimerEndRequest, TimerState, TimerToggleRequest } from "@sprint/shared";
|
||||
import type { TimerEndRequest, TimerListItem, TimerState, TimerToggleRequest } from "@sprint/shared";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { queryKeys } from "@/lib/query/keys";
|
||||
import { timer } from "@/lib/server";
|
||||
|
||||
const activeTimersQueryFn = () => timer.list({ activeOnly: true });
|
||||
|
||||
export function useActiveTimers(options?: { refetchInterval?: number; enabled?: boolean }) {
|
||||
return useQuery<TimerListItem[]>({
|
||||
queryKey: queryKeys.timers.list(),
|
||||
queryFn: activeTimersQueryFn,
|
||||
enabled: options?.enabled ?? true,
|
||||
refetchInterval: options?.refetchInterval,
|
||||
refetchIntervalInBackground: false,
|
||||
});
|
||||
}
|
||||
|
||||
export function useTimerState(issueId?: number | null, options?: { refetchInterval?: number }) {
|
||||
return useQuery<TimerState>({
|
||||
queryKey: queryKeys.timers.active(issueId ?? 0),
|
||||
queryFn: () => timer.get(issueId ?? 0),
|
||||
return useQuery<TimerListItem[], Error, TimerState>({
|
||||
queryKey: queryKeys.timers.list(),
|
||||
queryFn: activeTimersQueryFn,
|
||||
enabled: Boolean(issueId),
|
||||
refetchInterval: options?.refetchInterval,
|
||||
refetchIntervalInBackground: false,
|
||||
select: (timers) => timers.find((timer) => timer.issueId === issueId) ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -29,9 +42,9 @@ export function useToggleTimer() {
|
||||
return useMutation<TimerState, Error, TimerToggleRequest>({
|
||||
mutationKey: ["timers", "toggle"],
|
||||
mutationFn: timer.toggle,
|
||||
onSuccess: (data, variables) => {
|
||||
queryClient.setQueryData(queryKeys.timers.active(variables.issueId), data);
|
||||
onSuccess: (_data, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.timers.inactive(variables.issueId) });
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.timers.list() });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -42,9 +55,9 @@ export function useEndTimer() {
|
||||
return useMutation<TimerState, Error, TimerEndRequest>({
|
||||
mutationKey: ["timers", "end"],
|
||||
mutationFn: timer.end,
|
||||
onSuccess: (data, variables) => {
|
||||
queryClient.setQueryData(queryKeys.timers.active(variables.issueId), data);
|
||||
onSuccess: (_data, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.timers.inactive(variables.issueId) });
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.timers.list() });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ export const queryKeys = {
|
||||
issues: {
|
||||
all: ["issues"] as const,
|
||||
byProject: (projectId: number) => [...queryKeys.issues.all, "by-project", projectId] as const,
|
||||
byId: (issueId: number) => [...queryKeys.issues.all, "by-id", issueId] as const,
|
||||
statusCount: (organisationId: number, status: string) =>
|
||||
[...queryKeys.issues.all, "status-count", organisationId, status] as const,
|
||||
typeCount: (organisationId: number, type: string) =>
|
||||
@@ -30,7 +31,7 @@ export const queryKeys = {
|
||||
all: ["timers"] as const,
|
||||
active: (issueId: number) => [...queryKeys.timers.all, "active", issueId] as const,
|
||||
inactive: (issueId: number) => [...queryKeys.timers.all, "inactive", issueId] as const,
|
||||
list: (issueId: number) => [...queryKeys.timers.all, "list", issueId] as const,
|
||||
list: () => [...queryKeys.timers.all, "list"] as const,
|
||||
},
|
||||
users: {
|
||||
all: ["users"] as const,
|
||||
|
||||
19
packages/frontend/src/lib/server/issue/byId.ts
Normal file
19
packages/frontend/src/lib/server/issue/byId.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { IssueByIdQuery, IssueResponse } from "@sprint/shared";
|
||||
import { getServerURL } from "@/lib/utils";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
export async function byId(issueId: IssueByIdQuery["issueId"]): Promise<IssueResponse> {
|
||||
const url = new URL(`${getServerURL()}/issue/by-id`);
|
||||
url.searchParams.set("issueId", `${issueId}`);
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const message = await getErrorMessage(res, `failed to get issue (${res.status})`);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
return res.json();
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export { byId } from "@/lib/server/issue/byId";
|
||||
export { byProject } from "@/lib/server/issue/byProject";
|
||||
export { create } from "@/lib/server/issue/create";
|
||||
export { remove as delete } from "@/lib/server/issue/delete";
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import type { TimerListItem } from "@sprint/shared";
|
||||
import { getCsrfToken, getServerURL } from "@/lib/utils";
|
||||
import { getErrorMessage } from "..";
|
||||
|
||||
type TimerListInput = {
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
activeOnly?: boolean;
|
||||
};
|
||||
|
||||
export async function list(input: TimerListInput = {}): Promise<unknown> {
|
||||
export async function list(input: TimerListInput = {}): Promise<TimerListItem[]> {
|
||||
const url = new URL(`${getServerURL()}/timers`);
|
||||
if (input.limit != null) url.searchParams.set("limit", `${input.limit}`);
|
||||
if (input.offset != null) url.searchParams.set("offset", `${input.offset}`);
|
||||
if (input.activeOnly != null) url.searchParams.set("activeOnly", input.activeOnly ? "true" : "false");
|
||||
|
||||
const csrfToken = getCsrfToken();
|
||||
const headers: HeadersInit = {};
|
||||
|
||||
Reference in New Issue
Block a user