diff --git a/src/modules/chatbot/components/chatbot-drawer.vue b/src/modules/chatbot/components/chatbot-drawer.vue
index 380c991..ca1ce8f 100644
--- a/src/modules/chatbot/components/chatbot-drawer.vue
+++ b/src/modules/chatbot/components/chatbot-drawer.vue
@@ -45,7 +45,7 @@
@@ -92,6 +92,7 @@
color="accent"
size="2em"
class="shadow-5"
+ style="pointer-events: auto;"
@click="chatbot_store.is_showing_chatbot = true"
/>
@@ -103,5 +104,6 @@
:deep(.q-drawer) {
background: rgba(0, 0, 0, 0);
overflow: hidden;
+ pointer-events: none;
}
\ No newline at end of file
diff --git a/src/modules/timesheet-approval/components/details-dialog.vue b/src/modules/timesheet-approval/components/details-dialog.vue
index 5cbc813..07e3afe 100644
--- a/src/modules/timesheet-approval/components/details-dialog.vue
+++ b/src/modules/timesheet-approval/components/details-dialog.vue
@@ -2,17 +2,39 @@
setup
lang="ts"
>
- /* eslint-disable */
- import { ref } from 'vue';
+ import { useI18n } from 'vue-i18n';
+ import { computed, ref } from 'vue';
import { useTimesheetStore } from 'src/stores/timesheet-store';
import DetailsDialogChartHoursWorked from 'src/modules/timesheet-approval/components/details-dialog-chart-hours-worked.vue';
import DetailsDialogChartShiftTypes from 'src/modules/timesheet-approval/components/details-dialog-chart-shift-types.vue';
import DetailsDialogChartExpenses from 'src/modules/timesheet-approval/components/details-dialog-chart-expenses.vue';
import TimesheetWrapper from 'src/modules/timesheets/components/timesheet-wrapper.vue';
import ExpenseDialogList from 'src/modules/timesheets/components/expense-dialog-list.vue';
+ import { useTimesheetApprovalApi } from '../composables/use-timesheet-approval-api';
+ const { t } = useI18n();
const timesheet_store = useTimesheetStore();
+ const timesheetApprovalApi = useTimesheetApprovalApi();
const is_dialog_open = ref(false);
+
+ const isApproved = computed(() => timesheet_store.timesheets.every(timesheet => timesheet.is_approved));
+ const approveButtonLabel = computed(() => isApproved.value ?
+ t('timesheet_approvals.table.verified') :
+ t('timesheet_approvals.table.unverified')
+ );
+ const approveButtonIcon = computed(() => isApproved.value ? 'lock' : 'lock_open');
+
+ const onClickApproveAll = async () => {
+ const employeeEmail = timesheet_store.current_pay_period_overview?.email;
+ const isApproved = timesheet_store.timesheets.every(timesheet => timesheet.is_approved);
+
+ if (employeeEmail !== undefined && isApproved !== undefined) {
+ await timesheetApprovalApi.toggleTimesheetsApprovalByEmployeeEmail(
+ employeeEmail,
+ !isApproved
+ );
+ }
+ }
@@ -28,12 +50,34 @@
@before-hide="timesheet_store.getTimesheetOverviews"
>
-
\ No newline at end of file
+
+
+
\ No newline at end of file
diff --git a/src/modules/timesheet-approval/components/overview-list-filters.vue b/src/modules/timesheet-approval/components/overview-list-filters.vue
index 588dab1..fb82c72 100644
--- a/src/modules/timesheet-approval/components/overview-list-filters.vue
+++ b/src/modules/timesheet-approval/components/overview-list-filters.vue
@@ -18,8 +18,8 @@
-
-
+
+
-
+
{{ $t('timesheet_approvals.table.filter_columns') }}
diff --git a/src/modules/timesheet-approval/composables/use-timesheet-approval-api.ts b/src/modules/timesheet-approval/composables/use-timesheet-approval-api.ts
index eec21e6..3189d34 100644
--- a/src/modules/timesheet-approval/composables/use-timesheet-approval-api.ts
+++ b/src/modules/timesheet-approval/composables/use-timesheet-approval-api.ts
@@ -34,7 +34,10 @@ export const useTimesheetApprovalApi = () => {
const approval_success = await timesheet_store.toggleTimesheetsApprovalByEmployeeEmail(email, approval_status);
const overview = timesheet_store.pay_period_overviews.find(overview => overview.email === email);
- if (overview && approval_success) overview.is_approved = approval_status;
+ if (overview && approval_success) {
+ overview.is_approved = approval_status;
+ await timesheet_store.getTimesheetsByOptionalEmployeeEmail(email);
+ }
}
timesheet_store.is_loading = false;
diff --git a/src/modules/timesheets/components/shift-list-day-row.vue b/src/modules/timesheets/components/shift-list-day-row.vue
index da1e176..f2d2aac 100644
--- a/src/modules/timesheets/components/shift-list-day-row.vue
+++ b/src/modules/timesheets/components/shift-list-day-row.vue
@@ -3,18 +3,18 @@
lang="ts"
>
import { useI18n } from 'vue-i18n';
- import { computed, onMounted, ref } from 'vue';
- import { QSelect, QInput, useQuasar, type QSelectProps } from 'quasar';
+ import { computed, inject, onMounted, ref } from 'vue';
+ import { QSelect, QInput, useQuasar, type QSelectProps, QPopupProxy } from 'quasar';
import { useUiStore } from 'src/stores/ui-store';
import { SHIFT_OPTIONS } from 'src/modules/timesheets/utils/shift.util';
import type { Shift } from 'src/modules/timesheets/models/shift.models';
+ import { useAuthStore } from 'src/stores/auth-store';
// ================== State ==================
- const COMMENT_LENGTH_MAX = 280;
+ const shift = defineModel('shift', { required: true });
const { errorMessage = undefined, isTimesheetApproved = false, holiday = false } = defineProps<{
- dense?: boolean;
isTimesheetApproved?: boolean;
errorMessage?: string | undefined;
holiday?: boolean | undefined;
@@ -25,19 +25,30 @@
'onTimeFieldBlur': [void];
}>();
- const shift = defineModel('shift', { required: true });
+
+ const COMMENT_LENGTH_MAX = 280;
const q = useQuasar();
const { t } = useI18n();
const ui_store = useUiStore();
+ const authStore = useAuthStore();
+
+ const mode = inject<'normal' | 'approval'>('mode');
const shiftTypeSelected = ref(SHIFT_OPTIONS.find(option => option.value == shift.value.type));
const selectRef = ref(null);
const shiftErrorMessage = ref();
const is_showing_delete_confirm = ref(false);
+ const popupProxyRef = ref(null);
// ================== Computed ==================
+ const rightClickMenuIcon = computed(() => shift.value.is_approved ? 'lock_open' : 'lock');
+
+ const rightClickMenuLabel = computed(() => shift.value.is_approved ?
+ t('timesheet_approvals.tooltip.unapprove') :
+ t('timesheet_approvals.tooltip.approve'));
+
const timeInputProps = computed(() => ({
dense: true,
borderless: shift.value.is_approved && isTimesheetApproved,
@@ -109,6 +120,14 @@
is_showing_delete_confirm.value = state;
}
+ const onRightClickApprove = () => {
+ if (authStore.user?.user_module_access.includes('timesheets_approval'))
+ shift.value.is_approved = !shift.value.is_approved;
+
+ if (popupProxyRef.value)
+ popupProxyRef.value.hide();
+ }
+
onMounted(() => {
if (ui_store.focus_next_component) {
selectRef.value?.focus();
@@ -121,6 +140,31 @@
+
+
+
+
+
+
+
+ {{ rightClickMenuLabel }}
+
+
+
+
{
if (mode === 'normal') {
diff --git a/src/stores/timesheet-store.ts b/src/stores/timesheet-store.ts
index 985908b..f6f1841 100644
--- a/src/stores/timesheet-store.ts
+++ b/src/stores/timesheet-store.ts
@@ -98,7 +98,6 @@ export const useTimesheetStore = defineStore('timesheet', () => {
} catch (error) {
console.error('There was an error retrieving Employee Pay Period overviews: ', error);
pay_period_overviews.value = [];
- // TODO: More in-depth error-handling here
is_loading.value = false;
return false;
@@ -107,7 +106,6 @@ export const useTimesheetStore = defineStore('timesheet', () => {
const getTimesheetsByOptionalEmployeeEmail = async (employee_email?: string): Promise => {
if (pay_period.value === undefined) return false;
- is_loading.value = true;
let response;
try {
@@ -128,13 +126,11 @@ export const useTimesheetStore = defineStore('timesheet', () => {
initial_timesheets.value = [];
}
- is_loading.value = false;
return response.success;
} catch (error) {
console.error('There was an error retrieving timesheet details for this employee: ', error);
- // TODO: More in-depth error-handling here
timesheets.value = [];
- is_loading.value = false;
+
return false;
}
};