mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-19 05:32:16 +01:00
Compare commits
5 Commits
db4af2bcac
...
b660486eb7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b660486eb7 | ||
|
|
61db23fc01 | ||
|
|
ab761d97f9 | ||
|
|
50d60393bc | ||
|
|
b5639e4bb2 |
@@ -907,7 +907,7 @@ test.describe('Employee Sidebar Navigation', () => {
|
|||||||
|
|
||||||
// Visible links
|
// Visible links
|
||||||
await expect(employee.page.getByRole('link', { name: 'Dashboard' })).toBeVisible();
|
await expect(employee.page.getByRole('link', { name: 'Dashboard' })).toBeVisible();
|
||||||
await expect(employee.page.getByRole('link', { name: 'Time' })).toBeVisible();
|
await expect(employee.page.getByRole('link', { name: 'Time', exact: true })).toBeVisible();
|
||||||
await expect(employee.page.getByRole('link', { name: 'Calendar' })).toBeVisible();
|
await expect(employee.page.getByRole('link', { name: 'Calendar' })).toBeVisible();
|
||||||
await expect(employee.page.getByRole('link', { name: 'Projects' })).toBeVisible();
|
await expect(employee.page.getByRole('link', { name: 'Projects' })).toBeVisible();
|
||||||
await expect(employee.page.getByRole('link', { name: 'Clients' })).toBeVisible();
|
await expect(employee.page.getByRole('link', { name: 'Clients' })).toBeVisible();
|
||||||
|
|||||||
@@ -640,7 +640,7 @@ test('test that creating a project with estimated time in human-readable format
|
|||||||
await page.getByLabel('Project Name').fill(newProjectName);
|
await page.getByLabel('Project Name').fill(newProjectName);
|
||||||
|
|
||||||
// Fill in estimated time using human-readable format
|
// Fill in estimated time using human-readable format
|
||||||
const estimatedTimeInput = page.getByPlaceholder('e.g. 2h 30m or 1.5');
|
const estimatedTimeInput = page.getByLabel('Time Estimated');
|
||||||
await estimatedTimeInput.fill('2h 30m');
|
await estimatedTimeInput.fill('2h 30m');
|
||||||
await estimatedTimeInput.press('Tab');
|
await estimatedTimeInput.press('Tab');
|
||||||
|
|
||||||
@@ -668,7 +668,7 @@ test('test that creating a project with estimated time using decimal notation wo
|
|||||||
await page.getByLabel('Project Name').fill(newProjectName);
|
await page.getByLabel('Project Name').fill(newProjectName);
|
||||||
|
|
||||||
// Fill in estimated time using decimal notation (1.5 hours = 1h 30m)
|
// Fill in estimated time using decimal notation (1.5 hours = 1h 30m)
|
||||||
const estimatedTimeInput = page.getByPlaceholder('e.g. 2h 30m or 1.5');
|
const estimatedTimeInput = page.getByLabel('Time Estimated');
|
||||||
await estimatedTimeInput.fill('1.5');
|
await estimatedTimeInput.fill('1.5');
|
||||||
await estimatedTimeInput.press('Tab');
|
await estimatedTimeInput.press('Tab');
|
||||||
|
|
||||||
@@ -696,7 +696,7 @@ test('test that creating a project with estimated time using comma decimal notat
|
|||||||
await page.getByLabel('Project Name').fill(newProjectName);
|
await page.getByLabel('Project Name').fill(newProjectName);
|
||||||
|
|
||||||
// Fill in estimated time using comma decimal notation (2,5 hours = 2h 30m)
|
// Fill in estimated time using comma decimal notation (2,5 hours = 2h 30m)
|
||||||
const estimatedTimeInput = page.getByPlaceholder('e.g. 2h 30m or 1.5');
|
const estimatedTimeInput = page.getByLabel('Time Estimated');
|
||||||
await estimatedTimeInput.fill('2,5');
|
await estimatedTimeInput.fill('2,5');
|
||||||
await estimatedTimeInput.press('Tab');
|
await estimatedTimeInput.press('Tab');
|
||||||
|
|
||||||
@@ -727,7 +727,7 @@ test('test that updating estimated time on existing project works', async ({ pag
|
|||||||
await page.getByRole('menuitem').getByText('Edit').first().click();
|
await page.getByRole('menuitem').getByText('Edit').first().click();
|
||||||
|
|
||||||
// Fill in estimated time
|
// Fill in estimated time
|
||||||
const estimatedTimeInput = page.getByPlaceholder('e.g. 2h 30m or 1.5');
|
const estimatedTimeInput = page.getByLabel('Time Estimated');
|
||||||
await estimatedTimeInput.fill('4h 15m');
|
await estimatedTimeInput.fill('4h 15m');
|
||||||
await estimatedTimeInput.press('Tab');
|
await estimatedTimeInput.press('Tab');
|
||||||
|
|
||||||
@@ -748,7 +748,7 @@ test('test that estimated time input displays formatted value after blur', async
|
|||||||
await goToProjectsOverview(page);
|
await goToProjectsOverview(page);
|
||||||
await page.getByRole('button', { name: 'Create Project' }).click();
|
await page.getByRole('button', { name: 'Create Project' }).click();
|
||||||
|
|
||||||
const estimatedTimeInput = page.getByPlaceholder('e.g. 2h 30m or 1.5');
|
const estimatedTimeInput = page.getByLabel('Time Estimated');
|
||||||
|
|
||||||
// Enter time in various formats and check the displayed value
|
// Enter time in various formats and check the displayed value
|
||||||
await estimatedTimeInput.fill('90');
|
await estimatedTimeInput.fill('90');
|
||||||
|
|||||||
@@ -43,9 +43,7 @@ const emit = defineEmits<{
|
|||||||
disabled:opacity-50 disabled:cursor-not-allowed" />
|
disabled:opacity-50 disabled:cursor-not-allowed" />
|
||||||
</span>
|
</span>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent> Stop the running time entry to edit the timesheet </TooltipContent>
|
||||||
Stop the running time entry to edit the timesheet
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
<DurationSecondsInput
|
<DurationSecondsInput
|
||||||
|
|||||||
@@ -26,9 +26,7 @@ defineEmits<{
|
|||||||
<Button variant="ghost" size="sm" :disabled="busy">
|
<Button variant="ghost" size="sm" :disabled="busy">
|
||||||
<LoadingSpinner v-if="busy" class="h-3.5 w-3.5 m-0" />
|
<LoadingSpinner v-if="busy" class="h-3.5 w-3.5 m-0" />
|
||||||
Copy last week
|
Copy last week
|
||||||
<ChevronDownIcon
|
<ChevronDownIcon v-if="!busy" class="h-3.5 w-3.5 ml-1 text-icon-default" />
|
||||||
v-if="!busy"
|
|
||||||
class="h-3.5 w-3.5 ml-1 text-icon-default" />
|
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="start" class="min-w-[220px]">
|
<DropdownMenuContent align="start" class="min-w-[220px]">
|
||||||
|
|||||||
@@ -58,7 +58,11 @@ const emit = defineEmits<{
|
|||||||
<div class="inline-block min-w-full align-middle">
|
<div class="inline-block min-w-full align-middle">
|
||||||
<div
|
<div
|
||||||
class="grid min-w-full w-max border-y border-default-background-separator"
|
class="grid min-w-full w-max border-y border-default-background-separator"
|
||||||
style="grid-template-columns: minmax(420px, 1fr) repeat(7, minmax(96px, 120px)) minmax(100px, auto) 40px;">
|
style="
|
||||||
|
grid-template-columns:
|
||||||
|
minmax(420px, 1fr) repeat(7, minmax(96px, 120px)) minmax(100px, auto)
|
||||||
|
40px;
|
||||||
|
">
|
||||||
<!-- Header row -->
|
<!-- Header row -->
|
||||||
<div
|
<div
|
||||||
class="bg-background dark:bg-secondary pl-7 pr-3 py-1 text-xs text-text-tertiary md:sticky md:left-0 md:z-10">
|
class="bg-background dark:bg-secondary pl-7 pr-3 py-1 text-xs text-text-tertiary md:sticky md:left-0 md:z-10">
|
||||||
|
|||||||
@@ -170,12 +170,9 @@ async function createTag(name: string): Promise<Tag | undefined> {
|
|||||||
@remove-row="handleRemoveRow"
|
@remove-row="handleRemoveRow"
|
||||||
@cell-update="handleCellUpdate"
|
@cell-update="handleCellUpdate"
|
||||||
@project-task-change="
|
@project-task-change="
|
||||||
(row, projectId, taskId) =>
|
(row, projectId, taskId) => handleRowIdentityChange(row, { projectId, taskId })
|
||||||
handleRowIdentityChange(row, { projectId, taskId })
|
|
||||||
"
|
|
||||||
@billable-change="
|
|
||||||
(row, billable) => handleRowIdentityChange(row, { billable })
|
|
||||||
"
|
"
|
||||||
|
@billable-change="(row, billable) => handleRowIdentityChange(row, { billable })"
|
||||||
@tags-change="(row, tags) => handleRowIdentityChange(row, { tags })"
|
@tags-change="(row, tags) => handleRowIdentityChange(row, { tags })"
|
||||||
@add-row="handleAddRow" />
|
@add-row="handleAddRow" />
|
||||||
|
|
||||||
|
|||||||
@@ -265,9 +265,9 @@ export function useCalendarEvents(params: {
|
|||||||
'seconds'
|
'seconds'
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
durationSeconds = params.currentTime.value.diff(
|
durationSeconds = Math.max(
|
||||||
getDayJsInstance()(entry.start),
|
0,
|
||||||
'seconds'
|
params.currentTime.value.diff(getDayJsInstance()(entry.start), 'seconds')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import duration from 'dayjs/plugin/duration';
|
|||||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||||
import isToday from 'dayjs/plugin/isToday';
|
import isToday from 'dayjs/plugin/isToday';
|
||||||
import isYesterday from 'dayjs/plugin/isYesterday';
|
import isYesterday from 'dayjs/plugin/isYesterday';
|
||||||
|
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
|
||||||
|
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
|
||||||
import utc from 'dayjs/plugin/utc';
|
import utc from 'dayjs/plugin/utc';
|
||||||
import timezone from 'dayjs/plugin/timezone';
|
import timezone from 'dayjs/plugin/timezone';
|
||||||
import weekOfYear from 'dayjs/plugin/weekOfYear';
|
import weekOfYear from 'dayjs/plugin/weekOfYear';
|
||||||
@@ -68,6 +70,8 @@ function configureParseLocale(numberFormat?: string) {
|
|||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
dayjs.extend(isToday);
|
dayjs.extend(isToday);
|
||||||
dayjs.extend(isYesterday);
|
dayjs.extend(isYesterday);
|
||||||
|
dayjs.extend(isSameOrBefore);
|
||||||
|
dayjs.extend(isSameOrAfter);
|
||||||
dayjs.extend(duration);
|
dayjs.extend(duration);
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
dayjs.extend(timezone);
|
dayjs.extend(timezone);
|
||||||
|
|||||||
@@ -88,7 +88,6 @@ export function findFreeWindowOnDay(
|
|||||||
// Sort + merge so we can walk a clean [gap, obstacle, gap, ...] sequence.
|
// Sort + merge so we can walk a clean [gap, obstacle, gap, ...] sequence.
|
||||||
obstacles.sort((a, b) => a.start.diff(b.start));
|
obstacles.sort((a, b) => a.start.diff(b.start));
|
||||||
|
|
||||||
|
|
||||||
// merge overlaps
|
// merge overlaps
|
||||||
const merged: Interval[] = [];
|
const merged: Interval[] = [];
|
||||||
for (const obs of obstacles) {
|
for (const obs of obstacles) {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import {
|
|||||||
type FreeWindow,
|
type FreeWindow,
|
||||||
} from './cellMath';
|
} from './cellMath';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cell-level edit dispatcher. Picks one of four strategies based on
|
* Cell-level edit dispatcher. Picks one of four strategies based on
|
||||||
* the diff between current and requested totals:
|
* the diff between current and requested totals:
|
||||||
@@ -317,6 +316,5 @@ export function useTimesheetCellMutations(
|
|||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return { handleCellUpdate };
|
return { handleCellUpdate };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user