152 lines
4.1 KiB
TypeScript
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"]);
|
|
};
|