feat(timesheet): add tags management and responsive time tracking flows
This commit is contained in:
122
src/api/timeEntries.ts
Normal file
122
src/api/timeEntries.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { authFetch } from "./client";
|
||||
|
||||
export interface TimeEntry {
|
||||
id: string;
|
||||
workspace: string;
|
||||
user: string;
|
||||
project: string | null;
|
||||
description: string;
|
||||
start_time: string;
|
||||
end_time: string | null;
|
||||
duration: string | null;
|
||||
tags: string[];
|
||||
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;
|
||||
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");
|
||||
return response.json();
|
||||
};
|
||||
|
||||
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");
|
||||
return response.json();
|
||||
};
|
||||
|
||||
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");
|
||||
return response.json();
|
||||
};
|
||||
|
||||
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");
|
||||
};
|
||||
Reference in New Issue
Block a user