Files
qlockify-frontend-deployment/src/api/timeEntries.ts
Amirhossein Khalili ef3eaf1206
Some checks failed
Frontend CI/CD / build (push) Has been cancelled
Frontend CI/CD / deploy (push) Has been cancelled
fix(timezone): fix timer clock-skew
2026-05-26 13:00:35 +03:30

152 lines
4.1 KiB
TypeScript

import { authFetch } from "./client";
import { invalidateApiCache } from "./cache";
export interface TimeEntryProjectDetails {
id: string;
name: string;
is_deleted: boolean;
client_name: string | null;
}
export interface TimeEntryTagDetails {
id: string;
name: string;
color: string;
is_deleted: boolean;
}
export interface TimeEntry {
id: string;
workspace: string;
user: string;
project: string | null;
project_details: TimeEntryProjectDetails | null;
description: string;
start_time: string;
start_time_ms: number;
end_time: string | null;
end_time_ms: number | null;
server_now_ms: number;
duration: string | null;
tags: string[];
tag_details: TimeEntryTagDetails[];
is_billable: boolean;
hourly_rate: string | null;
currency: string;
created_at: string;
updated_at: string;
}
export interface TimeEntryGroupDay {
key: string;
date: string;
total_ms: number;
entries: TimeEntry[];
}
export interface TimeEntryGroupWeek {
key: string;
week_start: string;
week_end: string;
total_ms: number;
days: TimeEntryGroupDay[];
}
interface GroupedTimeEntryResponse {
items_per_page: number;
current_page_items_count: number;
total_items: number;
offset: number;
next_offset: number | null;
has_more: boolean;
server_now_ms: number;
server_now: string;
groups: TimeEntryGroupWeek[];
}
export interface TimeEntryPayload {
workspace_id?: string;
project_id?: string | null;
description?: string;
start_time?: string;
end_time?: string | null;
tags?: string[];
is_billable?: boolean;
}
export interface TimeEntryListParams {
limit?: number;
offset?: number;
search?: string;
status?: "running" | "ended" | "all";
project?: string;
client?: string;
tags?: string[];
started_after?: string;
started_before?: string;
}
export const getTimeEntries = async (
workspaceId: string,
params: TimeEntryListParams = {},
): Promise<GroupedTimeEntryResponse> => {
const query = new URLSearchParams({ workspace: workspaceId });
if (params.limit !== undefined) query.append("limit", String(params.limit));
if (params.offset !== undefined) query.append("offset", String(params.offset));
if (params.search) query.append("search", params.search);
if (params.status) query.append("status", params.status);
if (params.project) query.append("project", params.project);
if (params.client) query.append("client", params.client);
if (params.started_after) query.append("started_after", params.started_after);
if (params.started_before) query.append("started_before", params.started_before);
if (params.tags?.length) {
params.tags.forEach((tagId) => query.append("tags", tagId));
}
const response = await authFetch(`/api/time-entries/?${query.toString()}`);
if (!response.ok) throw new Error("Failed to fetch time entries");
return response.json();
};
export const createTimeEntry = async (payload: TimeEntryPayload) => {
const response = await authFetch("/api/time-entries/", {
method: "POST",
body: JSON.stringify(payload),
});
if (!response.ok) throw new Error("Failed to create time entry");
const data = await response.json();
invalidateApiCache(["reports"]);
return data;
};
export const updateTimeEntry = async (id: string, payload: TimeEntryPayload) => {
const response = await authFetch(`/api/time-entries/${id}/`, {
method: "PATCH",
body: JSON.stringify(payload),
});
if (!response.ok) throw new Error("Failed to update time entry");
const data = await response.json();
invalidateApiCache(["reports"]);
return data;
};
export const stopTimeEntry = async (id: string, endTime?: string) => {
const response = await authFetch(`/api/time-entries/${id}/stop/`, {
method: "POST",
body: JSON.stringify(endTime ? { end_time: endTime } : {}),
});
if (!response.ok) throw new Error("Failed to stop time entry");
const data = await response.json();
invalidateApiCache(["reports"]);
return data;
};
export const deleteTimeEntry = async (id: string) => {
const response = await authFetch(`/api/time-entries/${id}/`, {
method: "DELETE",
});
if (!response.ok) throw new Error("Failed to delete time entry");
invalidateApiCache(["reports"]);
};