From bae4265f7029dba47864f7c12a3f6bbe4ffd67c4 Mon Sep 17 00:00:00 2001 From: Gregor Vostrak Date: Sat, 1 Jun 2024 00:12:03 +0200 Subject: [PATCH] improve error handling for 401 requests and fetch --- .../Common/Member/MemberTableRow.vue | 19 +++-- .../js/Pages/Teams/Partials/ImportData.vue | 30 ++++--- resources/js/utils/notification.ts | 25 +++++- resources/js/utils/useClients.ts | 41 ++++----- resources/js/utils/useCurrentTimeEntry.ts | 85 ++++++++++--------- resources/js/utils/useInvitations.ts | 22 ++--- resources/js/utils/useMembers.ts | 30 ++++--- resources/js/utils/useProjectMembers.ts | 45 +++++----- resources/js/utils/useProjects.ts | 54 ++++++------ resources/js/utils/useReporting.ts | 26 +++--- resources/js/utils/useTags.ts | 49 ++++++----- resources/js/utils/useTasks.ts | 45 +++++----- resources/js/utils/useTimeEntries.ts | 83 +++++++++--------- 13 files changed, 305 insertions(+), 249 deletions(-) diff --git a/resources/js/Components/Common/Member/MemberTableRow.vue b/resources/js/Components/Common/Member/MemberTableRow.vue index dc4d64a4..1adf00c9 100644 --- a/resources/js/Components/Common/Member/MemberTableRow.vue +++ b/resources/js/Components/Common/Member/MemberTableRow.vue @@ -24,15 +24,16 @@ async function invitePlaceholder(id: string) { const organizationId = getCurrentOrganizationId(); if (organizationId) { await handleApiRequestNotifications( - api.invitePlaceholder( - {}, - { - params: { - organization: organizationId, - member: id, - }, - } - ), + () => + api.invitePlaceholder( + {}, + { + params: { + organization: organizationId, + member: id, + }, + } + ), 'Member invited successfully', 'Error inviting member' ); diff --git a/resources/js/Pages/Teams/Partials/ImportData.vue b/resources/js/Pages/Teams/Partials/ImportData.vue index 165ac4fa..ea3b00cf 100644 --- a/resources/js/Pages/Teams/Partials/ImportData.vue +++ b/resources/js/Pages/Teams/Partials/ImportData.vue @@ -10,6 +10,7 @@ import { getCurrentOrganizationId } from '@/utils/useUser'; import type { ImportReport, ImportType } from '@/utils/api'; import DialogModal from '@/Components/DialogModal.vue'; import SecondaryButton from '@/Components/SecondaryButton.vue'; +import { initializeStores } from '@/utils/init'; const importTypeOptions = ref([]); const { addNotification } = useNotificationsStore(); @@ -50,19 +51,24 @@ async function importData() { if (organizationId !== null) { const { handleApiRequestNotifications } = useNotificationsStore(); - reportResult.value = await handleApiRequestNotifications( - api.importData( - { - type: importType.value.key, - data: base64String, - }, - { - params: { - organization: organizationId, + reportResult.value = await handleApiRequestNotifications(() => { + if (importType.value) { + return api.importData( + { + type: importType.value.key, + data: base64String, }, - } - ) - ); + { + params: { + organization: organizationId, + }, + } + ); + } + return new Promise((resolve, reject) => { + reject('Import type is null'); + }); + }); if (reportResult.value) { showResultModal.value = true; } diff --git a/resources/js/utils/notification.ts b/resources/js/utils/notification.ts index 38bf49d4..fd3009ab 100644 --- a/resources/js/utils/notification.ts +++ b/resources/js/utils/notification.ts @@ -37,13 +37,23 @@ export const useNotificationsStore = defineStore('notifications', () => { } } + async function fetchToken() { + return new Promise((resolve) => { + router.reload({ + onFinish: () => { + resolve(null); + }, + }); + }); + } + async function handleApiRequestNotifications( - apiRequest: Promise, + apiRequest: () => Promise, successMessage?: string, errorMessage?: string ) { try { - const response = await apiRequest; + const response = await apiRequest(); if (successMessage) { addNotification('success', successMessage); } @@ -65,7 +75,16 @@ export const useNotificationsStore = defineStore('notifications', () => { const message = error.response.data.message; addNotification('error', message); } else if (error?.response?.status === 401) { - router.get(route('login')); + await fetchToken(); + try { + const response = await apiRequest(); + if (successMessage) { + addNotification('success', successMessage); + } + return response; + } catch (error) { + router.get(route('login')); + } } else { addNotification( 'error', diff --git a/resources/js/utils/useClients.ts b/resources/js/utils/useClients.ts index c05ae2d4..faa28c4d 100644 --- a/resources/js/utils/useClients.ts +++ b/resources/js/utils/useClients.ts @@ -17,11 +17,12 @@ export const useClientsStore = defineStore('clients', () => { const organization = getCurrentOrganizationId(); if (organization) { clientResponse.value = await handleApiRequestNotifications( - api.getClients({ - params: { - organization: organization, - }, - }), + () => + api.getClients({ + params: { + organization: organization, + }, + }), undefined, 'Failed to fetch clients' ); @@ -34,11 +35,12 @@ export const useClientsStore = defineStore('clients', () => { const organization = getCurrentOrganizationId(); if (organization) { const response = await handleApiRequestNotifications( - api.createClient(clientBody, { - params: { - organization: organization, - }, - }), + () => + api.createClient(clientBody, { + params: { + organization: organization, + }, + }), 'Client created successfully', 'Failed to create client' ); @@ -51,15 +53,16 @@ export const useClientsStore = defineStore('clients', () => { const organization = getCurrentOrganizationId(); if (organization) { await handleApiRequestNotifications( - api.deleteClient( - {}, - { - params: { - organization: organization, - client: clientId, - }, - } - ), + () => + api.deleteClient( + {}, + { + params: { + organization: organization, + client: clientId, + }, + } + ), 'Client deleted successfully', 'Failed to delete client' ); diff --git a/resources/js/utils/useCurrentTimeEntry.ts b/resources/js/utils/useCurrentTimeEntry.ts index da769c4e..1ce59c6d 100644 --- a/resources/js/utils/useCurrentTimeEntry.ts +++ b/resources/js/utils/useCurrentTimeEntry.ts @@ -89,18 +89,19 @@ export const useCurrentTimeEntryStore = defineStore('currentTimeEntry', () => { ? currentTimeEntry.value.start : dayjs().utc().format(); const response = await handleApiRequestNotifications( - api.createTimeEntry( - { - member_id: membership, - start: startTime, - description: currentTimeEntry.value?.description, - project_id: currentTimeEntry.value?.project_id, - task_id: currentTimeEntry.value?.task_id, - billable: currentTimeEntry.value.billable, - tags: currentTimeEntry.value?.tags, - }, - { params: { organization: organization } } - ), + () => + api.createTimeEntry( + { + member_id: membership, + start: startTime, + description: currentTimeEntry.value?.description, + project_id: currentTimeEntry.value?.project_id, + task_id: currentTimeEntry.value?.task_id, + billable: currentTimeEntry.value.billable, + tags: currentTimeEntry.value?.tags, + }, + { params: { organization: organization } } + ), 'Timer started!' ); if (response?.data) { @@ -119,19 +120,20 @@ export const useCurrentTimeEntryStore = defineStore('currentTimeEntry', () => { if (organization) { const currentDateTime = dayjs().utc().format(); await handleApiRequestNotifications( - api.updateTimeEntry( - { - user_id: user, - start: currentTimeEntry.value.start, - end: currentDateTime, - }, - { - params: { - organization: organization, - timeEntry: currentTimeEntry.value.id, + () => + api.updateTimeEntry( + { + user_id: user, + start: currentTimeEntry.value.start, + end: currentDateTime, }, - } - ), + { + params: { + organization: organization, + timeEntry: currentTimeEntry.value.id, + }, + } + ), 'Timer stopped!' ); $reset(); @@ -147,24 +149,25 @@ export const useCurrentTimeEntryStore = defineStore('currentTimeEntry', () => { const organization = getCurrentOrganizationId(); if (organization) { const response = await handleApiRequestNotifications( - api.updateTimeEntry( - { - description: currentTimeEntry.value.description, - user_id: user, - project_id: currentTimeEntry.value.project_id, - task_id: currentTimeEntry.value.task_id, - start: currentTimeEntry.value.start, - billable: currentTimeEntry.value.billable, - end: null, - tags: currentTimeEntry.value.tags, - }, - { - params: { - organization: organization, - timeEntry: currentTimeEntry.value.id, + () => + api.updateTimeEntry( + { + description: currentTimeEntry.value.description, + user_id: user, + project_id: currentTimeEntry.value.project_id, + task_id: currentTimeEntry.value.task_id, + start: currentTimeEntry.value.start, + billable: currentTimeEntry.value.billable, + end: null, + tags: currentTimeEntry.value.tags, }, - } - ), + { + params: { + organization: organization, + timeEntry: currentTimeEntry.value.id, + }, + } + ), 'Time entry updated!' ); if (response?.data) { diff --git a/resources/js/utils/useInvitations.ts b/resources/js/utils/useInvitations.ts index 9e951f37..09cc4057 100644 --- a/resources/js/utils/useInvitations.ts +++ b/resources/js/utils/useInvitations.ts @@ -17,11 +17,12 @@ export const useInvitationsStore = defineStore('invitations', () => { const organization = getCurrentOrganizationId(); if (organization) { invitationsResponse.value = await handleApiRequestNotifications( - api.getInvitations({ - params: { - organization: organization, - }, - }), + () => + api.getInvitations({ + params: { + organization: organization, + }, + }), undefined, 'Failed to fetch invitations' ); @@ -34,11 +35,12 @@ export const useInvitationsStore = defineStore('invitations', () => { const organization = getCurrentOrganizationId(); if (organization) { await handleApiRequestNotifications( - api.invite(inviteBody, { - params: { - organization: organization, - }, - }), + () => + api.invite(inviteBody, { + params: { + organization: organization, + }, + }), 'User successfully invited', 'Failed to invite user' ); diff --git a/resources/js/utils/useMembers.ts b/resources/js/utils/useMembers.ts index 1ae787ea..1c5cd0f3 100644 --- a/resources/js/utils/useMembers.ts +++ b/resources/js/utils/useMembers.ts @@ -13,11 +13,12 @@ export const useMembersStore = defineStore('members', () => { const organization = getCurrentOrganizationId(); if (organization) { membersResponse.value = await handleApiRequestNotifications( - api.getMembers({ - params: { - organization: organization, - }, - }), + () => + api.getMembers({ + params: { + organization: organization, + }, + }), undefined, 'Failed to fetch members' ); @@ -28,15 +29,16 @@ export const useMembersStore = defineStore('members', () => { const organization = getCurrentOrganizationId(); if (organization) { await handleApiRequestNotifications( - api.removeMember( - {}, - { - params: { - organization: organization, - member: membershipId, - }, - } - ), + () => + api.removeMember( + {}, + { + params: { + organization: organization, + member: membershipId, + }, + } + ), 'Member deleted successfully', 'Failed to delete member' ); diff --git a/resources/js/utils/useProjectMembers.ts b/resources/js/utils/useProjectMembers.ts index 90a38efe..18b5f6e4 100644 --- a/resources/js/utils/useProjectMembers.ts +++ b/resources/js/utils/useProjectMembers.ts @@ -17,12 +17,13 @@ export const useProjectMembersStore = defineStore('project-members', () => { const organization = getCurrentOrganizationId(); if (organization) { projectMemberResponse.value = await handleApiRequestNotifications( - api.getProjectMembers({ - params: { - organization: organization, - project: projectId, - }, - }), + () => + api.getProjectMembers({ + params: { + organization: organization, + project: projectId, + }, + }), undefined, 'Failed to fetch project members' ); @@ -36,12 +37,13 @@ export const useProjectMembersStore = defineStore('project-members', () => { const organization = getCurrentOrganizationId(); if (organization) { await handleApiRequestNotifications( - api.createProjectMember(projectMemberBody, { - params: { - organization: organization, - project: projectId, - }, - }), + () => + api.createProjectMember(projectMemberBody, { + params: { + organization: organization, + project: projectId, + }, + }), 'Project member added successfully', 'Failed to add project member' ); @@ -56,15 +58,16 @@ export const useProjectMembersStore = defineStore('project-members', () => { const organizationId = getCurrentOrganizationId(); if (organizationId) { await handleApiRequestNotifications( - api.deleteProjectMember( - {}, - { - params: { - organization: organizationId, - projectMember: projectMemberId, - }, - } - ), + () => + api.deleteProjectMember( + {}, + { + params: { + organization: organizationId, + projectMember: projectMemberId, + }, + } + ), 'Project member removed successfully', 'Failed to remove project member' ); diff --git a/resources/js/utils/useProjects.ts b/resources/js/utils/useProjects.ts index 5c70a1af..8435d936 100644 --- a/resources/js/utils/useProjects.ts +++ b/resources/js/utils/useProjects.ts @@ -17,11 +17,12 @@ export const useProjectsStore = defineStore('projects', () => { const organization = getCurrentOrganizationId(); if (organization) { projectResponse.value = await handleApiRequestNotifications( - api.getProjects({ - params: { - organization: organization, - }, - }), + () => + api.getProjects({ + params: { + organization: organization, + }, + }), undefined, 'Failed to fetch projects' ); @@ -32,11 +33,12 @@ export const useProjectsStore = defineStore('projects', () => { const organization = getCurrentOrganizationId(); if (organization) { await handleApiRequestNotifications( - api.createProject(projectBody, { - params: { - organization: organization, - }, - }), + () => + api.createProject(projectBody, { + params: { + organization: organization, + }, + }), 'Project created successfully', 'Failed to create project' ); @@ -49,15 +51,16 @@ export const useProjectsStore = defineStore('projects', () => { const organizationId = getCurrentOrganizationId(); if (organizationId) { await handleApiRequestNotifications( - api.deleteProject( - {}, - { - params: { - organization: organizationId, - project: projectId, - }, - } - ), + () => + api.deleteProject( + {}, + { + params: { + organization: organizationId, + project: projectId, + }, + } + ), 'Project deleted successfully', 'Failed to delete project' ); @@ -72,12 +75,13 @@ export const useProjectsStore = defineStore('projects', () => { const organizationId = getCurrentOrganizationId(); if (organizationId) { await handleApiRequestNotifications( - api.updateProject(updateProjectBody, { - params: { - organization: organizationId, - project: projectId, - }, - }), + () => + api.updateProject(updateProjectBody, { + params: { + organization: organizationId, + project: projectId, + }, + }), 'Project updated successfully', 'Failed to update project' ); diff --git a/resources/js/utils/useReporting.ts b/resources/js/utils/useReporting.ts index bb7d39c3..6ac1101d 100644 --- a/resources/js/utils/useReporting.ts +++ b/resources/js/utils/useReporting.ts @@ -39,12 +39,13 @@ export const useReportingStore = defineStore('reporting', () => { const organization = getCurrentOrganizationId(); if (organization) { reportingGraphResponse.value = await handleApiRequestNotifications( - api.getAggregatedTimeEntries({ - params: { - organization: organization, - }, - queries: params, - }), + () => + api.getAggregatedTimeEntries({ + params: { + organization: organization, + }, + queries: params, + }), undefined, 'Failed to fetch reporting data' ); @@ -57,12 +58,13 @@ export const useReportingStore = defineStore('reporting', () => { const organization = getCurrentOrganizationId(); if (organization) { reportingTableResponse.value = await handleApiRequestNotifications( - api.getAggregatedTimeEntries({ - params: { - organization: organization, - }, - queries: params, - }), + () => + api.getAggregatedTimeEntries({ + params: { + organization: organization, + }, + queries: params, + }), undefined, 'Failed to fetch reporting data' ); diff --git a/resources/js/utils/useTags.ts b/resources/js/utils/useTags.ts index d703cbe9..ca2b5c0a 100644 --- a/resources/js/utils/useTags.ts +++ b/resources/js/utils/useTags.ts @@ -12,11 +12,12 @@ export const useTagsStore = defineStore('tags', () => { const organizationId = getCurrentOrganizationId(); if (organizationId) { const response = await handleApiRequestNotifications( - api.getTags({ - params: { - organization: organizationId, - }, - }), + () => + api.getTags({ + params: { + organization: organizationId, + }, + }), undefined, 'Failed to fetch tags' ); @@ -34,15 +35,16 @@ export const useTagsStore = defineStore('tags', () => { const organizationId = getCurrentOrganizationId(); if (organizationId) { await handleApiRequestNotifications( - api.deleteTag( - {}, - { - params: { - organization: organizationId, - tag: tagId, - }, - } - ), + () => + api.deleteTag( + {}, + { + params: { + organization: organizationId, + tag: tagId, + }, + } + ), 'Tag deleted successfully', 'Failed to delete tag' ); @@ -54,16 +56,17 @@ export const useTagsStore = defineStore('tags', () => { const organizationId = getCurrentOrganizationId(); if (organizationId) { const response = await handleApiRequestNotifications( - api.createTag( - { - name: name, - }, - { - params: { - organization: organizationId, + () => + api.createTag( + { + name: name, }, - } - ), + { + params: { + organization: organizationId, + }, + } + ), 'Tag created successfully', 'Failed to create tag' ); diff --git a/resources/js/utils/useTasks.ts b/resources/js/utils/useTasks.ts index e0f14c16..ab10fd4d 100644 --- a/resources/js/utils/useTasks.ts +++ b/resources/js/utils/useTasks.ts @@ -12,7 +12,7 @@ export const useTasksStore = defineStore('tasks', () => { async function fetchTasks() { const organizationId = getCurrentOrganizationId(); if (organizationId) { - const tasksResponse = await handleApiRequestNotifications( + const tasksResponse = await handleApiRequestNotifications(() => api.getTasks({ params: { organization: organizationId, @@ -29,12 +29,13 @@ export const useTasksStore = defineStore('tasks', () => { const organizationId = getCurrentOrganizationId(); if (organizationId) { await handleApiRequestNotifications( - api.updateTask(task, { - params: { - organization: organizationId, - task: task.id, - }, - }), + () => + api.updateTask(task, { + params: { + organization: organizationId, + task: task.id, + }, + }), 'Task updated successfully', 'Failed to update task' ); @@ -45,11 +46,12 @@ export const useTasksStore = defineStore('tasks', () => { const organizationId = getCurrentOrganizationId(); if (organizationId) { await handleApiRequestNotifications( - api.createTask(task, { - params: { - organization: organizationId, - }, - }), + () => + api.createTask(task, { + params: { + organization: organizationId, + }, + }), 'Task created successfully', 'Failed to create task' ); @@ -61,15 +63,16 @@ export const useTasksStore = defineStore('tasks', () => { const organizationId = getCurrentOrganizationId(); if (organizationId) { await handleApiRequestNotifications( - api.deleteTask( - {}, - { - params: { - organization: organizationId, - task: taskId, - }, - } - ), + () => + api.deleteTask( + {}, + { + params: { + organization: organizationId, + task: taskId, + }, + } + ), 'Task deleted successfully', 'Failed to delete task' ); diff --git a/resources/js/utils/useTimeEntries.ts b/resources/js/utils/useTimeEntries.ts index 0e3d8479..1faa6759 100644 --- a/resources/js/utils/useTimeEntries.ts +++ b/resources/js/utils/useTimeEntries.ts @@ -21,15 +21,16 @@ export const useTimeEntriesStore = defineStore('timeEntries', () => { const organizationId = getCurrentOrganizationId(); if (organizationId) { const timeEntriesResponse = await handleApiRequestNotifications( - api.getTimeEntries({ - params: { - organization: organizationId, - }, - queries: { - only_full_dates: 'true', - member_id: getCurrentMembershipId(), - }, - }), + () => + api.getTimeEntries({ + params: { + organization: organizationId, + }, + queries: { + only_full_dates: 'true', + member_id: getCurrentMembershipId(), + }, + }), undefined, 'Failed to fetch time entries' ); @@ -47,16 +48,17 @@ export const useTimeEntriesStore = defineStore('timeEntries', () => { dayjs(latestTimeEntry.start).utc().format('YYYY-MM-DD'); const timeEntriesResponse = await handleApiRequestNotifications( - api.getTimeEntries({ - params: { - organization: organizationId, - }, - queries: { - only_full_dates: 'true', - member_id: getCurrentMembershipId(), - end: dayjs(latestTimeEntry.start).utc().format(), - }, - }), + () => + api.getTimeEntries({ + params: { + organization: organizationId, + }, + queries: { + only_full_dates: 'true', + member_id: getCurrentMembershipId(), + end: dayjs(latestTimeEntry.start).utc().format(), + }, + }), undefined, 'Failed to fetch time entries' ); @@ -77,12 +79,13 @@ export const useTimeEntriesStore = defineStore('timeEntries', () => { const organizationId = getCurrentOrganizationId(); if (organizationId) { await handleApiRequestNotifications( - api.updateTimeEntry(timeEntry, { - params: { - organization: organizationId, - timeEntry: timeEntry.id, - }, - }), + () => + api.updateTimeEntry(timeEntry, { + params: { + organization: organizationId, + timeEntry: timeEntry.id, + }, + }), 'Time entry updated successfully', 'Failed to update time entry' ); @@ -100,11 +103,12 @@ export const useTimeEntriesStore = defineStore('timeEntries', () => { member_id: memberId, } as CreateTimeEntryBody; await handleApiRequestNotifications( - api.createTimeEntry(newTimeEntry, { - params: { - organization: organizationId, - }, - }), + () => + api.createTimeEntry(newTimeEntry, { + params: { + organization: organizationId, + }, + }), 'Time entry created successfully', 'Failed to create time entry' ); @@ -116,15 +120,16 @@ export const useTimeEntriesStore = defineStore('timeEntries', () => { const organizationId = getCurrentOrganizationId(); if (organizationId) { await handleApiRequestNotifications( - api.deleteTimeEntry( - {}, - { - params: { - organization: organizationId, - timeEntry: timeEntryId, - }, - } - ), + () => + api.deleteTimeEntry( + {}, + { + params: { + organization: organizationId, + timeEntry: timeEntryId, + }, + } + ), 'Time entry deleted successfully', 'Failed to delete time entry' );