mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-08 00:02:15 +01:00
add break time entries and simplified time tracker ui
This commit is contained in:
@@ -406,6 +406,7 @@ export async function createTimeEntryViaApi(
|
||||
taskId?: string | null;
|
||||
tags?: string[];
|
||||
billable?: boolean;
|
||||
type?: 'work' | 'break';
|
||||
}
|
||||
) {
|
||||
const { start, end } = createTimestamps(data.duration);
|
||||
@@ -421,6 +422,7 @@ export async function createTimeEntryViaApi(
|
||||
task_id: data.taskId ?? null,
|
||||
tags: data.tags ?? [],
|
||||
billable: data.billable ?? false,
|
||||
type: data.type ?? 'work',
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -754,6 +756,7 @@ export async function getTimeEntriesViaApi(
|
||||
project_id: string | null;
|
||||
task_id: string | null;
|
||||
description: string;
|
||||
type: 'work' | 'break';
|
||||
}>
|
||||
> {
|
||||
const params = new URLSearchParams();
|
||||
@@ -779,6 +782,7 @@ export async function createTimeEntryWithTimestampsViaApi(
|
||||
taskId?: string | null;
|
||||
tags?: string[];
|
||||
billable?: boolean;
|
||||
type?: 'work' | 'break';
|
||||
}
|
||||
) {
|
||||
const response = await ctx.request.post(
|
||||
@@ -793,12 +797,19 @@ export async function createTimeEntryWithTimestampsViaApi(
|
||||
task_id: data.taskId ?? null,
|
||||
tags: data.tags ?? [],
|
||||
billable: data.billable ?? false,
|
||||
type: data.type ?? 'work',
|
||||
},
|
||||
}
|
||||
);
|
||||
expect(response.status()).toBe(201);
|
||||
const body = await response.json();
|
||||
return body.data as { id: string; start: string; end: string; description: string };
|
||||
return body.data as {
|
||||
id: string;
|
||||
start: string;
|
||||
end: string;
|
||||
description: string;
|
||||
type: 'work' | 'break';
|
||||
};
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────
|
||||
@@ -903,3 +914,71 @@ export async function createReportViaApi(
|
||||
public_until: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────────
|
||||
// Invoices
|
||||
// ──────────────────────────────────────────────────
|
||||
|
||||
export async function createInvoiceViaApi(
|
||||
ctx: TestContext,
|
||||
data: {
|
||||
reference: string;
|
||||
buyer_name?: string;
|
||||
seller_name?: string;
|
||||
currency?: string;
|
||||
date?: string;
|
||||
tax_rate?: number;
|
||||
}
|
||||
) {
|
||||
const response = await ctx.request.post(
|
||||
`${PLAYWRIGHT_BASE_URL}/api/v1/organizations/${ctx.orgId}/invoices`,
|
||||
{
|
||||
data: {
|
||||
seller_name: data.seller_name ?? 'Test Seller',
|
||||
buyer_name: data.buyer_name ?? 'Test Buyer',
|
||||
reference: data.reference,
|
||||
currency: data.currency ?? 'EUR',
|
||||
date: data.date ?? new Date().toISOString().split('T')[0],
|
||||
// Mirror the UI create form, which always sends a tax rate (default 0).
|
||||
// Invoices with a null tax_rate currently crash PDF rendering.
|
||||
tax_rate: data.tax_rate ?? 0,
|
||||
},
|
||||
}
|
||||
);
|
||||
expect(response.status()).toBe(201);
|
||||
const body = await response.json();
|
||||
return body.data as { id: string; reference: string; buyer_name: string };
|
||||
}
|
||||
|
||||
export async function updateInvoiceSettingsViaApi(ctx: TestContext, data: Record<string, unknown>) {
|
||||
const response = await ctx.request.put(
|
||||
`${PLAYWRIGHT_BASE_URL}/api/v1/organizations/${ctx.orgId}/invoice-settings`,
|
||||
{ data }
|
||||
);
|
||||
expect(response.status()).toBe(200);
|
||||
const body = await response.json();
|
||||
return body.data as Record<string, unknown>;
|
||||
}
|
||||
|
||||
export async function getInvoiceSettingsViaApi(ctx: TestContext) {
|
||||
const response = await ctx.request.get(
|
||||
`${PLAYWRIGHT_BASE_URL}/api/v1/organizations/${ctx.orgId}/invoice-settings`
|
||||
);
|
||||
expect(response.status()).toBe(200);
|
||||
const body = await response.json();
|
||||
return body.data as Record<string, unknown>;
|
||||
}
|
||||
|
||||
export async function getInvoicesViaApi(ctx: TestContext) {
|
||||
const response = await ctx.request.get(
|
||||
`${PLAYWRIGHT_BASE_URL}/api/v1/organizations/${ctx.orgId}/invoices`
|
||||
);
|
||||
expect(response.status()).toBe(200);
|
||||
const body = await response.json();
|
||||
return body.data as Array<{
|
||||
id: string;
|
||||
reference: string;
|
||||
buyer_name: string;
|
||||
paid_date: string | null;
|
||||
}>;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,17 @@ export async function assertThatTimerHasStarted(page: Page) {
|
||||
|
||||
export function newTimeEntryResponse(
|
||||
page: Page,
|
||||
{ description = '', status = 201, tags = [] } = {}
|
||||
{
|
||||
description = '',
|
||||
status = 201,
|
||||
tags = [],
|
||||
type,
|
||||
}: {
|
||||
description?: string;
|
||||
status?: number;
|
||||
tags?: string[];
|
||||
type?: 'work' | 'break';
|
||||
} = {}
|
||||
) {
|
||||
return page.waitForResponse(async (response) => {
|
||||
return (
|
||||
@@ -34,6 +44,7 @@ export function newTimeEntryResponse(
|
||||
(await response.json()).data.description === description &&
|
||||
(await response.json()).data.task_id === null &&
|
||||
(await response.json()).data.user_id !== null &&
|
||||
(type === undefined || (await response.json()).data.type === type) &&
|
||||
JSON.stringify((await response.json()).data.tags) === JSON.stringify(tags)
|
||||
);
|
||||
});
|
||||
@@ -48,7 +59,18 @@ export async function assertThatTimerIsStopped(page: Page) {
|
||||
).toHaveClass(/bg-accent-300\/70/);
|
||||
}
|
||||
|
||||
export async function stoppedTimeEntryResponse(page: Page, { description = '', tags = [] } = {}) {
|
||||
export async function stoppedTimeEntryResponse(
|
||||
page: Page,
|
||||
{
|
||||
description = '',
|
||||
tags = [],
|
||||
type,
|
||||
}: {
|
||||
description?: string;
|
||||
tags?: string[];
|
||||
type?: 'work' | 'break';
|
||||
} = {}
|
||||
) {
|
||||
return page.waitForResponse(async (response) => {
|
||||
return (
|
||||
response.status() === 200 &&
|
||||
@@ -62,6 +84,7 @@ export async function stoppedTimeEntryResponse(page: Page, { description = '', t
|
||||
(await response.json()).data.task_id === null &&
|
||||
(await response.json()).data.duration !== null &&
|
||||
(await response.json()).data.user_id !== null &&
|
||||
(type === undefined || (await response.json()).data.type === type) &&
|
||||
JSON.stringify((await response.json()).data.tags) === JSON.stringify(tags)
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user