mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-08 08:12:17 +01:00
Enable npm workspaces and update dependencies
This commit is contained in:
@@ -187,10 +187,10 @@ test('test that entering a random value in the time range does not start the tim
|
||||
}) => {
|
||||
await goToDashboard(page);
|
||||
await page.getByTestId('time_entry_time').fill('asdasdasd');
|
||||
await page.getByTestId('time_entry_time').press('Tab'),
|
||||
(await page.getByTestId('time_entry_time').press('Tab'),
|
||||
await page.locator(
|
||||
'[data-testid="dashboard_timer"] [data-testid="timer_button"].bg-accent-300/70'
|
||||
);
|
||||
));
|
||||
});
|
||||
|
||||
test('test that entering a time starts the timer on enter', async ({ page }) => {
|
||||
|
||||
4134
package-lock.json
generated
4134
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
33
package.json
33
package.json
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"workspaces": [
|
||||
"resources/js/packages/ui",
|
||||
"resources/js/packages/api"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
@@ -19,27 +23,29 @@
|
||||
"@playwright/test": "^1.41.1",
|
||||
"@tailwindcss/forms": "^0.5.9",
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
"@types/chroma-js": "2.4.5",
|
||||
"@types/chroma-js": "^3.1.0",
|
||||
"@types/node": "^22.10.10",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"@vue/tsconfig": "^0.5.1",
|
||||
"@vitejs/plugin-vue": "^6.0.3",
|
||||
"@vue/tsconfig": "^0.8.0",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"axios": "^1.6.4",
|
||||
"eslint-plugin-unused-imports": "^4.1.4",
|
||||
"laravel-vite-plugin": "^1.0.0",
|
||||
"laravel-vite-plugin": "^2.1.0",
|
||||
"openapi-zod-client": "^1.16.2",
|
||||
"postcss": "^8.4.47",
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-nesting": "^12.1.5",
|
||||
"tailwindcss": "^3.4.13",
|
||||
"typescript": "^5.7.3",
|
||||
"vite": "^6.0.11",
|
||||
"vite-plugin-checker": "^0.8.0",
|
||||
"vite": "^7.0.0",
|
||||
"vite-plugin-checker": "^0.12.0",
|
||||
"vue": "^3.5.0",
|
||||
"vue-tsc": "^2.2.0"
|
||||
"vue-tsc": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.6.0",
|
||||
"@floating-ui/vue": "^1.0.6",
|
||||
"@zodios/core": "^10.9.6",
|
||||
"@fullcalendar/core": "^6.1.18",
|
||||
"@fullcalendar/daygrid": "^6.1.18",
|
||||
"@fullcalendar/interaction": "^6.1.18",
|
||||
@@ -54,22 +60,23 @@
|
||||
"@tanstack/vue-table": "^8.21.2",
|
||||
"@vue/eslint-config-prettier": "^10.2.0",
|
||||
"@vue/eslint-config-typescript": "^14.3.0",
|
||||
"@vueuse/core": "^12.8.2",
|
||||
"@vueuse/integrations": "^12.5.0",
|
||||
"@vueuse/core": "^14.0.0",
|
||||
"@vueuse/integrations": "^14.0.0",
|
||||
"chroma-js": "3.1.2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"dayjs": "^1.11.11",
|
||||
"echarts": "^5.5.0",
|
||||
"focus-trap": "^7.6.0",
|
||||
"echarts": "^6.0.0",
|
||||
"focus-trap": "^8.0.0",
|
||||
"lucide-vue-next": "^0.487.0",
|
||||
"parse-duration": "^2.0.1",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia": "^3.0.0",
|
||||
"radix-vue": "^1.9.6",
|
||||
"reka-ui": "^2.7.0",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"vue-echarts": "^7.0.3"
|
||||
"vue-echarts": "^8.0.0",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"overrides": {
|
||||
"vite-plugin-checker": {
|
||||
|
||||
@@ -125,7 +125,7 @@ const gridTemplate = computed(() => {
|
||||
:create-client
|
||||
:currency="getOrganizationCurrencyString()"
|
||||
:clients="clients"
|
||||
:enable-estimated-time="isAllowedToPerformPremiumAction"></ProjectCreateModal>
|
||||
:enable-estimated-time="isAllowedToPerformPremiumAction()"></ProjectCreateModal>
|
||||
<div class="flow-root max-w-[100vw] overflow-x-auto">
|
||||
<div class="inline-block min-w-full align-middle">
|
||||
<div data-testid="project_table" class="grid min-w-full" :style="gridTemplate">
|
||||
|
||||
@@ -158,7 +158,7 @@ const option = computed(() => {
|
||||
if (dailyHoursTracked?.value) {
|
||||
return (
|
||||
formatDate(
|
||||
dailyHoursTracked?.value[dataIndex].date,
|
||||
dailyHoursTracked?.value[dataIndex]?.date ?? '',
|
||||
organization?.value?.date_format
|
||||
) +
|
||||
': ' +
|
||||
|
||||
@@ -23,9 +23,9 @@ const { data: last7Days, isLoading } = useQuery({
|
||||
},
|
||||
enabled: computed(() => !!organizationId.value),
|
||||
placeholderData: Array.from({ length: 7 }, (_, i) => ({
|
||||
date: new Date(Date.now() - i * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
|
||||
date: new Date(Date.now() - i * 24 * 60 * 60 * 1000).toISOString().split('T')[0]!,
|
||||
duration: 0,
|
||||
history: Array(8).fill(0),
|
||||
history: Array(8).fill(0) as number[],
|
||||
})),
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -42,10 +42,10 @@ const weekdays = computed(() => {
|
||||
};
|
||||
if (dayMapping[getWeekStart()]) {
|
||||
const customOrder = [];
|
||||
const startIndex = daysOrder.indexOf(dayMapping[getWeekStart()]);
|
||||
const startIndex = daysOrder.indexOf(dayMapping[getWeekStart()]!);
|
||||
|
||||
for (let i = startIndex; i < 7 + startIndex; i++) {
|
||||
customOrder.push(daysOrder[i % daysOrder.length]);
|
||||
customOrder.push(daysOrder[i % daysOrder.length]!);
|
||||
}
|
||||
|
||||
return customOrder;
|
||||
|
||||
@@ -29,7 +29,7 @@ const photoInput = ref<HTMLInputElement | null>(null);
|
||||
|
||||
const updateProfileInformation = () => {
|
||||
if (photoInput.value && photoInput.value.files && photoInput.value.files?.length > 0) {
|
||||
form.photo = photoInput.value?.files[0];
|
||||
form.photo = photoInput.value?.files[0] ?? null;
|
||||
}
|
||||
|
||||
form.post(route('user-profile-information.update'), {
|
||||
|
||||
@@ -137,7 +137,7 @@ const showBillableRate = computed(() => {
|
||||
<ProjectCreateModal
|
||||
v-model:show="showCreateProjectModal"
|
||||
:create-project
|
||||
:enable-estimated-time="isAllowedToPerformPremiumAction"
|
||||
:enable-estimated-time="isAllowedToPerformPremiumAction()"
|
||||
:create-client
|
||||
:currency="getOrganizationCurrencyString()"
|
||||
:clients="clients"
|
||||
|
||||
@@ -37,7 +37,7 @@ onMounted(() => {
|
||||
const currentUrl = window.location.href;
|
||||
// check if # exists exactly once in the URL
|
||||
if (currentUrl.split('#').length === 2) {
|
||||
sharedSecret.value = currentUrl.split('#')[1];
|
||||
sharedSecret.value = currentUrl.split('#')[1] ?? null;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -124,9 +124,10 @@ const groupedPieChartData = computed(() => {
|
||||
if (entry.description === null) {
|
||||
return {
|
||||
value: entry.seconds,
|
||||
name: emptyPlaceholder[
|
||||
aggregatedTableTimeEntries.value?.grouped_type ?? 'project'
|
||||
],
|
||||
name:
|
||||
emptyPlaceholder[
|
||||
aggregatedTableTimeEntries.value?.grouped_type ?? 'project'
|
||||
] ?? '',
|
||||
color: '#CCCCCC',
|
||||
};
|
||||
}
|
||||
@@ -146,14 +147,17 @@ const tableData = computed(() => {
|
||||
cost: entry.cost,
|
||||
description:
|
||||
entry.description ??
|
||||
emptyPlaceholder[aggregatedTableTimeEntries.value?.grouped_type ?? 'project'],
|
||||
emptyPlaceholder[aggregatedTableTimeEntries.value?.grouped_type ?? 'project'] ??
|
||||
'',
|
||||
grouped_data:
|
||||
entry.grouped_data?.map((el) => {
|
||||
return {
|
||||
seconds: el.seconds,
|
||||
cost: el.cost,
|
||||
description:
|
||||
el.description ?? emptyPlaceholder[entry.grouped_type ?? 'project'],
|
||||
el.description ??
|
||||
emptyPlaceholder[entry.grouped_type ?? 'project'] ??
|
||||
'',
|
||||
};
|
||||
}) ?? [],
|
||||
};
|
||||
|
||||
@@ -46,8 +46,8 @@ async function importData() {
|
||||
addNotification('error', 'Please select the CSV or ZIP file that you want to import');
|
||||
return;
|
||||
}
|
||||
const rawBase64String = await toBase64(files.value[0]);
|
||||
const base64String = rawBase64String.split(';')[1].replace('base64,', '') as string;
|
||||
const rawBase64String = await toBase64(files.value[0]!);
|
||||
const base64String = rawBase64String.split(';')[1]!.replace('base64,', '') as string;
|
||||
const organizationId = getCurrentOrganizationId();
|
||||
if (organizationId !== null) {
|
||||
const { handleApiRequestNotifications } = useNotificationsStore();
|
||||
|
||||
2164
resources/js/packages/api/package-lock.json
generated
2164
resources/js/packages/api/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@
|
||||
"directory": "resources/js/packages/api"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npx vite build",
|
||||
"build": "vite build",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"files": [
|
||||
@@ -27,14 +27,14 @@
|
||||
"type": "module",
|
||||
"author": "solidtime",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@zodios/core": "^10.9.6",
|
||||
"axios": "^1.13.2",
|
||||
"typescript": "^5.5.4",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^5.4.2",
|
||||
"vite-plugin-dts": "^4.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@zodios/core": "^10.9.6",
|
||||
"axios": "^1.6.4",
|
||||
"typescript": "^5.5.4",
|
||||
"vite": "^5.4.1 || ^6.0.0 || ^7.0.0",
|
||||
"zod": "^3.23.8"
|
||||
}
|
||||
}
|
||||
|
||||
3925
resources/js/packages/ui/package-lock.json
generated
3925
resources/js/packages/ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -48,22 +48,13 @@
|
||||
"author": "solidtime",
|
||||
"license": "AGPL-3.0",
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.4.1",
|
||||
"@vitejs/plugin-vue": "^5.1.2",
|
||||
"@vue/tsconfig": "^0.5.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.4.14",
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-nesting": "^12.1.0",
|
||||
"typescript": "^5.5.4",
|
||||
"vite": "^5.4.1",
|
||||
"vite-plugin-dts": "^4.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@floating-ui/vue": "^1.1.4",
|
||||
"@heroicons/vue": "^2.1.5",
|
||||
"@vueuse/core": "^12.5.0",
|
||||
"@zodios/core": "^10.9.6",
|
||||
"@vitejs/plugin-vue": "^5.1.2 || ^6.0.0",
|
||||
"@vueuse/core": "^12.5.0 || ^14.0.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"dayjs": "^1.11.13",
|
||||
@@ -71,7 +62,9 @@
|
||||
"reka-ui": "^2.2.0",
|
||||
"tailwind-merge": "^2.5.2",
|
||||
"tailwindcss": "^3.1.0",
|
||||
"typescript": "^5.5.4",
|
||||
"vite": "^5.4.1 || ^6.0.0 || ^7.0.0",
|
||||
"vue": "^3.5.0",
|
||||
"vue-tsc": "^2.2.0"
|
||||
"vue-tsc": "^2.2.0 || ^3.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,8 +320,8 @@ function getSlotDuration(calendarEl: HTMLElement): number {
|
||||
const secondTime = secondSlot.getAttribute('data-time');
|
||||
|
||||
if (firstTime && secondTime) {
|
||||
const [h1, m1] = firstTime.split(':').map(Number);
|
||||
const [h2, m2] = secondTime.split(':').map(Number);
|
||||
const [h1 = 0, m1 = 0] = firstTime.split(':').map(Number);
|
||||
const [h2 = 0, m2 = 0] = secondTime.split(':').map(Number);
|
||||
const diff = h2 * 60 + m2 - (h1 * 60 + m1);
|
||||
if (diff > 0) return diff;
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ function updateDuration() {
|
||||
const results = parseHHMM(temporaryCustomTimerEntry.value);
|
||||
if (results) {
|
||||
const newStartDate = getDayJsInstance()(end.value)
|
||||
.subtract(parseInt(results[1]), 'h')
|
||||
.subtract(parseInt(results[2]), 'm');
|
||||
.subtract(parseInt(results[1]!), 'h')
|
||||
.subtract(parseInt(results[2]!), 'm');
|
||||
start.value = newStartDate.utc().format();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ function updateTime(event: Event) {
|
||||
const target = event.target as HTMLInputElement;
|
||||
const newValue = target.value.trim();
|
||||
if (newValue.split(':').length === 2) {
|
||||
const [hours, minutes] = newValue.split(':');
|
||||
const [hours, minutes] = newValue.split(':') as [string, string];
|
||||
if (!isNaN(parseInt(hours)) && !isNaN(parseInt(minutes))) {
|
||||
const currentTime = getLocalizedDayJs(model.value);
|
||||
const newHours = Math.min(parseInt(hours), 23);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { defineProps, nextTick, ref, watch } from 'vue';
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
import DatePicker from '@/packages/ui/src/Input/DatePicker.vue';
|
||||
import { getDayJsInstance, getLocalizedDayJs } from '@/packages/ui/src/utils/time';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
@@ -53,7 +53,7 @@ const groupedTimeEntries = computed(() => {
|
||||
}
|
||||
const groupedEntriesByDayAndType: Record<string, TimeEntriesGroupedByType[]> = {};
|
||||
for (const dailyEntriesKey in groupedEntriesByDay) {
|
||||
const dailyEntries = groupedEntriesByDay[dailyEntriesKey];
|
||||
const dailyEntries = groupedEntriesByDay[dailyEntriesKey]!;
|
||||
const newDailyEntries: TimeEntriesGroupedByType[] = [];
|
||||
|
||||
for (const entry of dailyEntries) {
|
||||
@@ -66,26 +66,22 @@ const groupedTimeEntries = computed(() => {
|
||||
e.description === entry.description
|
||||
);
|
||||
if (oldEntriesIndex !== -1 && newDailyEntries[oldEntriesIndex]) {
|
||||
newDailyEntries[oldEntriesIndex].timeEntries.push(entry);
|
||||
const existingEntry = newDailyEntries[oldEntriesIndex]!;
|
||||
existingEntry.timeEntries.push(entry);
|
||||
|
||||
// Add up durations for time entries of the same type
|
||||
newDailyEntries[oldEntriesIndex].duration =
|
||||
(newDailyEntries[oldEntriesIndex].duration ?? 0) + (entry?.duration ?? 0);
|
||||
existingEntry.duration = (existingEntry.duration ?? 0) + (entry?.duration ?? 0);
|
||||
|
||||
// adapt start end times so they show the earliest start and latest end time
|
||||
if (
|
||||
getDayJsInstance()(entry.start).isBefore(
|
||||
getDayJsInstance()(newDailyEntries[oldEntriesIndex].start)
|
||||
getDayJsInstance()(existingEntry.start)
|
||||
)
|
||||
) {
|
||||
newDailyEntries[oldEntriesIndex].start = entry.start;
|
||||
existingEntry.start = entry.start;
|
||||
}
|
||||
if (
|
||||
getDayJsInstance()(entry.end).isAfter(
|
||||
getDayJsInstance()(newDailyEntries[oldEntriesIndex].end)
|
||||
)
|
||||
) {
|
||||
newDailyEntries[oldEntriesIndex].end = entry.end;
|
||||
if (getDayJsInstance()(entry.end).isAfter(getDayJsInstance()(existingEntry.end))) {
|
||||
existingEntry.end = entry.end;
|
||||
}
|
||||
} else {
|
||||
newDailyEntries.push({ ...entry, timeEntries: [entry] });
|
||||
@@ -204,7 +200,7 @@ function unselectAllTimeEntries(value: TimeEntriesGroupedByType[]) {
|
||||
:delete-time-entry="() => deleteTimeEntries([entry])"
|
||||
:duplicate-time-entry="() => createTimeEntry(entry)"
|
||||
:currency="currency"
|
||||
:time-entry="entry.timeEntries[0]"
|
||||
:time-entry="entry.timeEntries[0]!"
|
||||
@selected="selectedTimeEntries.push(entry)"
|
||||
@unselected="
|
||||
selectedTimeEntries = selectedTimeEntries.filter(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import Dropdown from '@/packages/ui/src/Input/Dropdown.vue';
|
||||
import { defineProps, ref, inject, type ComputedRef } from 'vue';
|
||||
import { ref, inject, type ComputedRef } from 'vue';
|
||||
import { formatDateLocalized, formatStartEnd } from '@/packages/ui/src/utils/time';
|
||||
import TimeRangeSelector from '@/packages/ui/src/Input/TimeRangeSelector.vue';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
formatHumanReadableDuration,
|
||||
parseTimeInput,
|
||||
} from '@/packages/ui/src/utils/time';
|
||||
import { computed, defineProps, ref, inject, type ComputedRef } from 'vue';
|
||||
import { computed, ref, inject, type ComputedRef } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import type { Organization } from '@/packages/api/src';
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ function addProjectToFilterObject(
|
||||
|
||||
if (projectClientIndex !== -1) {
|
||||
// client already exists in filter array
|
||||
tempFilteredClients[projectClientIndex].projects.push({
|
||||
tempFilteredClients[projectClientIndex]!.projects.push({
|
||||
...project,
|
||||
expanded: expanded,
|
||||
tasks: filteredTasks,
|
||||
@@ -119,7 +119,7 @@ function addProjectToFilterObject(
|
||||
|
||||
if (noClientIndex !== -1) {
|
||||
// no client group already exists in filter array
|
||||
tempFilteredClients[noClientIndex].projects.push({
|
||||
tempFilteredClients[noClientIndex]!.projects.push({
|
||||
...project,
|
||||
expanded: expanded,
|
||||
tasks: filteredTasks,
|
||||
@@ -253,7 +253,7 @@ function isProjectSelected(project: Project) {
|
||||
|
||||
function initializeHighlightedItem() {
|
||||
if (filteredProjects.value.length > 0) {
|
||||
highlightedItemId.value = filteredProjects.value[0].id;
|
||||
highlightedItemId.value = filteredProjects.value[0]!.id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,27 +312,27 @@ function moveHighlightUp() {
|
||||
highlightedItemId.value = currentProjectWithTasks.id;
|
||||
return;
|
||||
}
|
||||
highlightedItemId.value = currentProjectWithTasks.tasks[taskIndex - 1].id;
|
||||
highlightedItemId.value = currentProjectWithTasks.tasks[taskIndex - 1]!.id;
|
||||
}
|
||||
}
|
||||
if (currentHighlightedIndex === 0) {
|
||||
// selected project is the first project in the list
|
||||
// highlight the last project or the last task of the last project
|
||||
const lastProject = filteredProjects.value[filteredProjects.value.length - 1];
|
||||
const lastProject = filteredProjects.value[filteredProjects.value.length - 1]!;
|
||||
if (lastProject.tasks.length > 0 && lastProject.expanded) {
|
||||
// highlight last task of last project
|
||||
highlightedItemId.value = lastProject.tasks[lastProject.tasks.length - 1].id;
|
||||
highlightedItemId.value = lastProject.tasks[lastProject.tasks.length - 1]!.id;
|
||||
} else {
|
||||
highlightedItemId.value = filteredProjects.value[filteredProjects.value.length - 1].id;
|
||||
highlightedItemId.value = filteredProjects.value[filteredProjects.value.length - 1]!.id;
|
||||
}
|
||||
} else {
|
||||
// selected item is a project that is not the first project in the list
|
||||
const previousProject = filteredProjects.value[currentHighlightedIndex - 1];
|
||||
const previousProject = filteredProjects.value[currentHighlightedIndex - 1]!;
|
||||
if (previousProject.tasks.length > 0 && previousProject.expanded) {
|
||||
// highlight last task of previous project
|
||||
highlightedItemId.value = previousProject.tasks[previousProject.tasks.length - 1].id;
|
||||
highlightedItemId.value = previousProject.tasks[previousProject.tasks.length - 1]!.id;
|
||||
} else {
|
||||
highlightedItemId.value = filteredProjects.value[currentHighlightedIndex - 1].id;
|
||||
highlightedItemId.value = filteredProjects.value[currentHighlightedIndex - 1]!.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -361,33 +361,33 @@ function moveHighlightDown() {
|
||||
const projectIndex = filteredProjects.value.indexOf(currentProjectWithTasks);
|
||||
if (projectIndex === filteredProjects.value.length - 1) {
|
||||
// highlight the first project if it was the last project
|
||||
highlightedItemId.value = filteredProjects.value[0].id;
|
||||
highlightedItemId.value = filteredProjects.value[0]!.id;
|
||||
} else {
|
||||
highlightedItemId.value = filteredProjects.value[projectIndex + 1].id;
|
||||
highlightedItemId.value = filteredProjects.value[projectIndex + 1]!.id;
|
||||
}
|
||||
return;
|
||||
}
|
||||
highlightedItemId.value = currentProjectWithTasks.tasks[taskIndex + 1].id;
|
||||
highlightedItemId.value = currentProjectWithTasks.tasks[taskIndex + 1]!.id;
|
||||
}
|
||||
}
|
||||
if (currentHighlightedIndex === filteredProjects.value.length - 1) {
|
||||
// selected project is the last project in the list
|
||||
// highlight the first project or the last project of the last project
|
||||
const lastProject = filteredProjects.value[filteredProjects.value.length - 1];
|
||||
const lastProject = filteredProjects.value[filteredProjects.value.length - 1]!;
|
||||
if (lastProject.tasks.length > 0 && lastProject.expanded) {
|
||||
// highlight last task of last project
|
||||
highlightedItemId.value = lastProject.tasks[0].id;
|
||||
highlightedItemId.value = lastProject.tasks[0]!.id;
|
||||
} else {
|
||||
highlightedItemId.value = filteredProjects.value[0].id;
|
||||
highlightedItemId.value = filteredProjects.value[0]!.id;
|
||||
}
|
||||
} else {
|
||||
// selected item is a project that is not the last project in the list
|
||||
const currentProjectWithTasks = filteredProjects.value[currentHighlightedIndex];
|
||||
const currentProjectWithTasks = filteredProjects.value[currentHighlightedIndex]!;
|
||||
if (currentProjectWithTasks.tasks.length > 0 && currentProjectWithTasks.expanded) {
|
||||
// highlight last task of previous project
|
||||
highlightedItemId.value = currentProjectWithTasks.tasks[0].id;
|
||||
highlightedItemId.value = currentProjectWithTasks.tasks[0]!.id;
|
||||
} else {
|
||||
highlightedItemId.value = filteredProjects.value[currentHighlightedIndex + 1].id;
|
||||
highlightedItemId.value = filteredProjects.value[currentHighlightedIndex + 1]!.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -423,7 +423,7 @@ function expandProject() {
|
||||
if (currentHighlightedIndex === -1) {
|
||||
return;
|
||||
}
|
||||
const currentProject = filteredProjects.value[currentHighlightedIndex];
|
||||
const currentProject = filteredProjects.value[currentHighlightedIndex]!;
|
||||
currentProject.expanded = true;
|
||||
}
|
||||
|
||||
@@ -434,7 +434,7 @@ function collapseProject() {
|
||||
if (currentHighlightedIndex === -1) {
|
||||
return;
|
||||
}
|
||||
const currentProject = filteredProjects.value[currentHighlightedIndex];
|
||||
const currentProject = filteredProjects.value[currentHighlightedIndex]!;
|
||||
currentProject.expanded = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,11 +23,11 @@ export const colors = [
|
||||
];
|
||||
|
||||
export function getRandomColor() {
|
||||
return colors[Math.floor(Math.random() * colors.length)];
|
||||
return colors[Math.floor(Math.random() * colors.length)]!;
|
||||
}
|
||||
|
||||
export function getRandomColorWithSeed(seed: string) {
|
||||
const pseudoRandom = new Prando(seed);
|
||||
const index = pseudoRandom.nextInt(0, colors.length - 1);
|
||||
return colors[index];
|
||||
return colors[index]!;
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ export type NumberFormat =
|
||||
export function formatNumber(value: number, format?: string): string {
|
||||
// Convert to fixed 2 decimal places first
|
||||
const parts = value.toFixed(2).split('.');
|
||||
const wholePart = parts[0];
|
||||
const decimalPart = parts[1];
|
||||
const wholePart = parts[0] ?? '0';
|
||||
const decimalPart = parts[1] ?? '00';
|
||||
|
||||
// Format the whole number part based on the format
|
||||
let formattedWhole: string;
|
||||
|
||||
@@ -107,7 +107,7 @@ export default class Prando {
|
||||
* @return An item from the array.
|
||||
*/
|
||||
public nextArrayItem<T>(array: T[]): T {
|
||||
return array[this.nextInt(0, array.length - 1)];
|
||||
return array[this.nextInt(0, array.length - 1)] as T;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,16 +12,16 @@ export function useSelectEvents<Type>(
|
||||
const currentHightlightedIndex = filteredItems.value.indexOf(highlightedItem.value);
|
||||
if (currentHightlightedIndex === 0) {
|
||||
highlightedItemId.value = getKeyFromItem(
|
||||
filteredItems.value[filteredItems.value.length - 1]
|
||||
filteredItems.value[filteredItems.value.length - 1]!
|
||||
);
|
||||
} else {
|
||||
highlightedItemId.value = getKeyFromItem(
|
||||
filteredItems.value[currentHightlightedIndex - 1]
|
||||
filteredItems.value[currentHightlightedIndex - 1]!
|
||||
);
|
||||
}
|
||||
} else {
|
||||
highlightedItemId.value = getKeyFromItem(
|
||||
filteredItems.value[filteredItems.value.length - 1]
|
||||
filteredItems.value[filteredItems.value.length - 1]!
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -30,14 +30,14 @@ export function useSelectEvents<Type>(
|
||||
if (highlightedItem.value) {
|
||||
const currentHightlightedIndex = filteredItems.value.indexOf(highlightedItem.value);
|
||||
if (currentHightlightedIndex === filteredItems.value.length - 1) {
|
||||
highlightedItemId.value = getKeyFromItem(filteredItems.value[0]);
|
||||
highlightedItemId.value = getKeyFromItem(filteredItems.value[0]!);
|
||||
} else {
|
||||
highlightedItemId.value = getKeyFromItem(
|
||||
filteredItems.value[currentHightlightedIndex + 1]
|
||||
filteredItems.value[currentHightlightedIndex + 1]!
|
||||
);
|
||||
}
|
||||
} else {
|
||||
highlightedItemId.value = getKeyFromItem(filteredItems.value[0]);
|
||||
highlightedItemId.value = getKeyFromItem(filteredItems.value[0]!);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -227,9 +227,9 @@ export function parseTimeInput(
|
||||
if (HHMMSStimeRegex.test(input)) {
|
||||
const match = input.match(HHMMSStimeRegex);
|
||||
if (match) {
|
||||
const hours = parseInt(match[1]);
|
||||
const minutes = parseInt(match[2]);
|
||||
const seconds = parseInt(match[3]);
|
||||
const hours = parseInt(match[1]!);
|
||||
const minutes = parseInt(match[2]!);
|
||||
const seconds = parseInt(match[3]!);
|
||||
return hours * 3600 + minutes * 60 + seconds;
|
||||
}
|
||||
}
|
||||
@@ -239,8 +239,8 @@ export function parseTimeInput(
|
||||
if (HHMMtimeRegex.test(input)) {
|
||||
const match = input.match(HHMMtimeRegex);
|
||||
if (match) {
|
||||
const hours = parseInt(match[1]);
|
||||
const minutes = parseInt(match[2]);
|
||||
const hours = parseInt(match[1]!);
|
||||
const minutes = parseInt(match[2]!);
|
||||
return (hours * 60 + minutes) * 60;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,17 +264,17 @@ function prefetchProjectMembers(queryClient: QueryClient, projectId: string) {
|
||||
*/
|
||||
function findPrefetcher(url: string): ((queryClient: QueryClient) => void) | undefined {
|
||||
// Extract pathname from URL
|
||||
const pathname = url.startsWith('http') ? new URL(url).pathname : url.split('?')[0];
|
||||
const pathname = url.startsWith('http') ? new URL(url).pathname : url.split('?')[0]!;
|
||||
|
||||
// Try exact match first
|
||||
if (routePrefetchers[pathname]) {
|
||||
if (pathname && routePrefetchers[pathname]) {
|
||||
return routePrefetchers[pathname];
|
||||
}
|
||||
|
||||
// Try pattern matching for dynamic routes like /projects/{id}
|
||||
const projectMatch = pathname.match(/^\/projects\/([^/]+)$/);
|
||||
const projectMatch = pathname?.match(/^\/projects\/([^/]+)$/);
|
||||
if (projectMatch) {
|
||||
const projectId = projectMatch[1];
|
||||
const projectId = projectMatch[1]!;
|
||||
return (queryClient) => {
|
||||
prefetchProjects(queryClient);
|
||||
prefetchTasks(queryClient);
|
||||
|
||||
@@ -349,8 +349,8 @@ export function useCommandPalette() {
|
||||
if (cmd.permission && !cmd.permission()) continue;
|
||||
if (cmd.condition && !cmd.condition()) continue;
|
||||
|
||||
if (grouped[cmd.group]) {
|
||||
grouped[cmd.group].push(cmd);
|
||||
if (grouped[cmd.group] !== undefined) {
|
||||
grouped[cmd.group]!.push(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ export function useTimeEntriesInfiniteQuery() {
|
||||
if (!lastPage?.data || lastPage.data.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const latestTimeEntry = lastPage.data[lastPage.data.length - 1];
|
||||
const latestTimeEntry = lastPage.data[lastPage.data.length - 1]!;
|
||||
return dayjs(latestTimeEntry.start).utc().format();
|
||||
},
|
||||
enabled: computed(() => !!organizationId.value),
|
||||
|
||||
Reference in New Issue
Block a user