Skip to content

Commit

Permalink
add time entry mass update
Browse files Browse the repository at this point in the history
  • Loading branch information
Onatcer committed Oct 29, 2024
1 parent a74c297 commit 66a5a1a
Show file tree
Hide file tree
Showing 6 changed files with 1,172 additions and 1,710 deletions.
2,758 changes: 1,063 additions & 1,695 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
"@electron-toolkit/utils": "^3.0.0",
"@sentry/electron": "^5.3.0",
"@sentry/vite-plugin": "^2.22.2",
"@solidtime/api": "^0.0.3",
"@solidtime/ui": "^0.0.8",
"@solidtime/api": "^0.0.4",
"@solidtime/ui": "^0.0.9",
"electron-updater": "^6.1.7"
},
"devDependencies": {
Expand Down Expand Up @@ -69,7 +69,7 @@
"postcss": "^8.4.39",
"prettier": "^3.3.2",
"tailwindcss": "^3.4.4",
"typescript": "^5.5.2",
"typescript": "~5.5.2",
"vite": "^5.3.1",
"vue": "^3.4.30",
"vue-tsc": "^2.0.22",
Expand Down
2 changes: 0 additions & 2 deletions src/renderer/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ declare global {
interface Window {
getTimezoneSetting: () => string
getWeekStartSetting: () => string
__TAURI__: Record<string, unknown>
}
}
Expand Down Expand Up @@ -60,7 +59,6 @@ import { useQueryClient } from '@tanstack/vue-query'
<template>
<VueQueryDevtools></VueQueryDevtools>
<AutoUpdaterOverlay></AutoUpdaterOverlay>

<div class="h-10 w-full border-b border-border-primary flex justify-end items-center pr-3">
<div class="flex-1 h-full" style="-webkit-app-region: drag"></div>
<div v-if="isLoggedIn" class="flex items-center space-x-2">
Expand Down
72 changes: 66 additions & 6 deletions src/renderer/src/components/MainTimeEntryTable.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<script setup lang="ts">
import { useQuery } from '@tanstack/vue-query'
import { type Component, computed, nextTick, onMounted, watch, watchEffect } from 'vue'
import { type Component, computed, nextTick, onMounted, ref, watch, watchEffect } from 'vue'
import {
TimeEntryGroupedTable,
TimeTrackerControls,
TimeTrackerRunningInDifferentOrganizationOverlay,
TimeEntryMassActionRow,
} from '@solidtime/ui'
import {
emptyTimeEntry,
Expand All @@ -17,6 +18,7 @@ import {
useCurrentTimeEntryUpdateMutation,
useTimeEntriesUpdateMutation,
useTimeEntryUpdateMutation,
useTimeEntriesDeleteMutation,
} from '../utils/timeEntries.ts'
import { getAllProjects, useProjectCreateMutation } from '../utils/projects.ts'
import { getAllTasks } from '../utils/tasks.ts'
Expand All @@ -27,6 +29,7 @@ import type {
Project,
Tag,
TimeEntry,
UpdateMultipleTimeEntriesChangeset,
} from '@solidtime/api'
import { getAllTags, useTagCreateMutation } from '../utils/tags.ts'
import { LoadingSpinner } from '@solidtime/ui'
Expand All @@ -44,11 +47,15 @@ import { apiClient } from '../utils/api'
import { updateTrayState } from '../utils/tray'
import { getMe } from '../utils/me'
const { currentOrganizationId } = useMyMemberships()
const currentOrganizationLoaded = computed(() => !!currentOrganizationId)
const { currentOrganizationId, currentMembership } = useMyMemberships()
const currentOrganizationLoaded = computed(() => !!currentOrganizationId.value)
const { liveTimer, startLiveTimer, stopLiveTimer } = useLiveTimer()
watch(currentOrganizationId, () => {
selectedTimeEntries.value = []
})
const { data: timeEntriesResponse } = useQuery({
queryKey: ['timeEntries', currentOrganizationId],
queryFn: () => getAllTimeEntries(currentOrganizationId.value, currentMembershipId.value),
Expand All @@ -61,8 +68,8 @@ const { data: currentTimeEntryResponse, isError: currentTimeEntryResponseIsError
queryFn: () => getCurrentTimeEntry(),
})
const currentTimeEntry = useStorage('currentTimeEntry', { ...emptyTimeEntry })
const lastTimeEntry = useStorage('lastTimeEntry', { ...emptyTimeEntry })
const currentTimeEntry = useStorage<TimeEntry>('currentTimeEntry', { ...emptyTimeEntry })
const lastTimeEntry = useStorage<TimeEntry>('lastTimeEntry', { ...emptyTimeEntry })
watch(timeEntries, () => {
if (timeEntries.value?.[0]) {
Expand Down Expand Up @@ -116,6 +123,7 @@ const tags = computed(() => tagsResponse.value?.data)
const currentTimeEntryUpdateMutation = useCurrentTimeEntryUpdateMutation()
const timeEntriesUpdate = useTimeEntriesUpdateMutation()
const timeEntriesDelete = useTimeEntriesDeleteMutation()
const timeEntryUpdate = useTimeEntryUpdateMutation()
const timeEntryDelete = useTimeEntryDeleteMutation()
const timeEntryCreate = useTimeEntryCreateMutation()
Expand Down Expand Up @@ -264,8 +272,30 @@ watch(meResponse, () => {
}
})
const selectedTimeEntries = ref([] as TimeEntry[])
function deleteSelected() {
timeEntriesDelete.mutate(selectedTimeEntries.value)
selectedTimeEntries.value = []
}
async function clearSelectionAndState() {
selectedTimeEntries.value = []
}
// TODO: Fix me
const currency = 'EUR'
const canCreateProjects = computed(() => {
if (currentMembership.value) {
return (
currentMembership.value?.role === 'admin' ||
currentMembership.value?.role === 'owner' ||
currentMembership.value?.role === 'manager'
)
}
return false
})
</script>

<template>
Expand All @@ -289,6 +319,8 @@ const currency = 'EUR'
v-model:currentTimeEntry="currentTimeEntry"
v-model:liveTimer="liveTimer"
:tags
:enableEstimatedTime="false"
:canCreateProject="canCreateProjects"
:createProject
:createClient
:tasks
Expand All @@ -305,8 +337,33 @@ const currency = 'EUR'
</div>
</div>
<div class="overflow-y-scroll w-full flex-1">
<TimeEntryMassActionRow
:selectedTimeEntries
:deleteSelected
:allSelected="selectedTimeEntries.length === timeEntries.length"
:tasks
:tags
:currency
:enableEstimatedTime="false"
:canCreateProject="canCreateProjects"
:projects
:clients
:updateTimeEntries="
(args: UpdateMultipleTimeEntriesChangeset) =>
timeEntriesUpdate.mutate({
ids: selectedTimeEntries.map((timeEntry) => timeEntry.id),
changes: args,
})
"
:createProject
:createClient
:createTag
@submit="clearSelectionAndState"
@select-all="selectedTimeEntries = [...timeEntries]"
@unselect-all="selectedTimeEntries = []"></TimeEntryMassActionRow>
<TimeEntryGroupedTable
v-if="timeEntries && projects && tasks && tags && clients"
v-model:selected="selectedTimeEntries"
:projects
:tasks
:tags
Expand All @@ -324,7 +381,10 @@ const currency = 'EUR'
timeEntriesUpdate.mutate({ ids: ids, changes: changes })
"
:deleteTimeEntries="
(args) => args.forEach((timeEntry) => timeEntryDelete.mutate(timeEntry))
(args: TimeEntry[]) =>
args.forEach((timeEntry: TimeEntry) =>
timeEntryDelete.mutate(timeEntry)
)
"
:createTimeEntry="createTimeEntry"
:createTag
Expand Down
1 change: 0 additions & 1 deletion src/renderer/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ Sentry.init({

window.addEventListener('keypress', (event) => {
if (event.key === 'Escape') {
// your code
event.preventDefault()
}
})
Expand Down
43 changes: 40 additions & 3 deletions src/renderer/src/utils/timeEntries.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { apiClient } from './api.ts'
import type { CreateTimeEntryBody, TimeEntry, TimeEntryResponse } from '@solidtime/api'
import type {
CreateTimeEntryBody,
TimeEntry,
TimeEntryResponse,
UpdateMultipleTimeEntriesChangeset,
} from '@solidtime/api'
import { useMutation, useQueryClient } from '@tanstack/vue-query'
import { useMyMemberships } from './myMemberships.ts'

Expand Down Expand Up @@ -143,6 +148,39 @@ export function useTimeEntryUpdateMutation() {
})
}

export function useTimeEntriesDeleteMutation() {
const queryClient = useQueryClient()
const { currentOrganizationId } = useMyMemberships()

return useMutation({
scope: {
id: 'timeEntry',
},
mutationFn: (timeEntries: TimeEntry[]) => {
if (currentOrganizationId.value === null) {
throw new Error('No current organization id - create time entry')
}
const timeEntryIds = timeEntries.map((timeEntry) => {
if (offlineUuidStore[timeEntry.id]) {
return offlineUuidStore[timeEntry.id]
}
return timeEntry.id
})
return apiClient.value.deleteTimeEntries(undefined, {
queries: {
ids: timeEntryIds,
},
params: {
organization: currentOrganizationId.value,
},
})
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['timeEntries', currentOrganizationId] })
},
})
}

export function useTimeEntriesUpdateMutation() {
const queryClient = useQueryClient()
const { currentOrganizationId } = useMyMemberships()
Expand All @@ -151,8 +189,7 @@ export function useTimeEntriesUpdateMutation() {
scope: {
id: 'timeEntry',
},
mutationFn: (changes: { ids: string[]; changes: Partial<TimeEntry> }) => {
console.log(changes)
mutationFn: (changes: { ids: string[]; changes: UpdateMultipleTimeEntriesChangeset }) => {
if (currentOrganizationId.value === null) {
throw new Error('No current organization id - update time entry')
}
Expand Down

0 comments on commit 66a5a1a

Please sign in to comment.