-
-
-
+
+
+
+
+
+
+
+
+
+
+ {{ $t(scope.opt.label) }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t(scope.label) }}
+
+
+
+
+
+
+
+
+ {{ shift.is_remote ? $t('timesheet.shift.types.REMOTE') :
+ $t('timesheet.shift.types.OFFICE') }}
+
+
+
+
+
+ {{ shift.is_remote ? $t('timesheet.shift.types.REMOTE') :
+ $t('timesheet.shift.types.OFFICE') }}
+
+
+
+
{{ $t('shared.misc.in') }}
+ class="text-weight-medium text-uppercase q-px-sm no-pointer-events"
+ :class="$q.dark.isActive ? 'bg-secondary' : 'bg-blue-grey-7'"
+ >
+ {{ $t('timesheet.shift.types.label') }}
+
-
-
+
-
-
-
+
+
+
+ {{ getHoursMinutesStringFromHoursFloat(expectedDailyHours) }}
+
+
+
+
+
+
+
-
- {{ $t('shared.misc.out') }}
-
-
+ :readonly="isApproved"
+ :background-color="isApproved ? 'white' : undefined"
+ :input-text-color="isApproved ? 'accent' : ''"
+ :label="$t('timesheet.expense.employee_comment')"
+ :append-content="isApproved ? '' : `${commentLength ?? 0}/280`"
+ />
-
-
@@ -390,4 +337,13 @@
padding-top: 0;
align-items: center;
}
+
+:deep(.q-field--float .q-field__label) {
+ transform: translate(-17px, -60%) scale(0.75) !important;
+ border-radius: 10px 10px 10px 0px;
+}
+
+:deep(.q-field--auto-height.q-field--labeled .q-field__control-container) {
+ padding: 0;
+}
\ No newline at end of file
diff --git a/src/modules/timesheets/components/mobile/shift-list-mobile.vue b/src/modules/timesheets/components/mobile/shift-list-mobile.vue
new file mode 100644
index 0000000..40e6ffe
--- /dev/null
+++ b/src/modules/timesheets/components/mobile/shift-list-mobile.vue
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/timesheets/components/new-shift-list.vue b/src/modules/timesheets/components/new-shift-list.vue
deleted file mode 100644
index 4b87399..0000000
--- a/src/modules/timesheets/components/new-shift-list.vue
+++ /dev/null
@@ -1,287 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ getHolidayName(day.date) }}
-
-
-
-
-
-
- {{ $d(extractDate(day.date, 'YYYY-MM-DD'), {
- weekday: 'long', day: 'numeric', month:
- 'long'
- }) }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/modules/timesheets/components/shift-list-date-widget.vue b/src/modules/timesheets/components/shift-list-date-widget.vue
index 4ce59c2..1d1405a 100644
--- a/src/modules/timesheets/components/shift-list-date-widget.vue
+++ b/src/modules/timesheets/components/shift-list-date-widget.vue
@@ -4,14 +4,15 @@
>
import { computed } from 'vue';
import { date, useQuasar } from 'quasar';
-
+
const q = useQuasar();
const { extractDate } = date;
- const { displayDate, dense = false, approved = false} = defineProps<{
+ const { displayDate, dense = false, approved = false } = defineProps<{
displayDate: string;
dense?: boolean;
approved?: boolean;
+ today?: boolean;
}>();
const date_font_size = computed(() => dense ? '1.5em' : '2.5em');
@@ -23,24 +24,35 @@
+
+
- {{ $d(display_date, { weekday: $q.platform.is.mobile ? 'short' : 'long'}) }}
+ {{ $d(display_date, { weekday: $q.platform.is.mobile ? 'short' : 'long' }) }}
+
{{ display_date.getDate() }}
+
-
\ No newline at end of file
+
+
+
\ No newline at end of file
diff --git a/src/modules/timesheets/components/shift-list-day-row.vue b/src/modules/timesheets/components/shift-list-day-row.vue
index 3cbfa62..87cd998 100644
--- a/src/modules/timesheets/components/shift-list-day-row.vue
+++ b/src/modules/timesheets/components/shift-list-day-row.vue
@@ -3,10 +3,10 @@
lang="ts"
>
import DetailsDialogShiftMenu from 'src/modules/timesheet-approval/components/details-dialog-shift-menu.vue';
+ import TargoInput from 'src/modules/shared/components/targo-input.vue';
- import { useI18n } from 'vue-i18n';
import { computed, inject, onMounted, ref } from 'vue';
- import { QSelect, QInput, useQuasar, type QSelectProps } from 'quasar';
+ import { QSelect, useQuasar, colors, getCssVar } from 'quasar';
import { useUiStore } from 'src/stores/ui-store';
import { useAuthStore } from 'src/stores/auth-store';
import { getCurrentDailyMinutesWorked, getShiftOptions, getTimeStringFromMinutes, SHIFT_OPTIONS } from 'src/modules/timesheets/utils/shift.util';
@@ -23,7 +23,6 @@
const shift = defineModel
('shift', { required: true });
const {
- errorMessage = undefined,
isTimesheetApproved = false,
currentShifts,
holiday = false,
@@ -32,7 +31,7 @@
currentShifts: Shift[];
expectedDailyHours?: number;
isTimesheetApproved?: boolean;
- errorMessage?: string | undefined;
+ errorTimesheet?: boolean | undefined;
holiday?: boolean | undefined;
}>();
@@ -42,7 +41,6 @@
}>();
const q = useQuasar();
- const { t } = useI18n();
const uiStore = useUiStore();
const authStore = useAuthStore();
@@ -59,41 +57,7 @@
// ================== Computed ==================
const hasPTO = computed(() => currentShifts.some(shift => SHIFT_TYPES_WITH_PREDEFINED_TIMES.includes(shift.type)));
-
- const timeInputProps = computed(() => ({
- dense: true,
- borderless: shift.value.is_approved && isTimesheetApproved,
- readonly: shift.value.is_approved && isTimesheetApproved,
- standout: q.dark.isActive ? 'bg-blue-grey-3' : 'bg-blue-grey-9',
- labelSlot: true,
- lazyRules: true,
- noErrorIcon: true,
- hideBottomSpace: true,
- error: shift.value.has_error,
- errorMessage: errorMessage ? t(errorMessage) : (shiftErrorMessage.value ? t(shiftErrorMessage.value) : undefined),
- labelColor: shift.value.is_approved ? 'white' : (holiday ? 'purple-5' : 'accent'),
- class: `col rounded-5 bg-dark q-mx-xs ${shift.value.id === -2 ? 'bg-negative' : ''} ${shift.value.is_approved || isTimesheetApproved ? 'cursor-not-allowed inset-shadow' : ''}`,
- inputClass: `text-weight-medium ${shift.value.id === -2 ? 'text-white ' : ' '} ${shift.value.is_approved ? 'text-white cursor-not-allowed q-px-sm' : ''}`,
- style: shift.value.is_approved ? (holiday ? 'background-color: #7b1fa2 !important' : 'background-color: #0a7d32 !important;') : '',
- inputStyle: "font-size: 1.2em;"
- }));
-
- const shiftTypeSelectProps = computed>(() => ({
- standout: q.dark.isActive ? 'bg-blue-grey-3' : 'bg-blue-grey-9',
- dense: true,
- borderless: shift.value.is_approved && isTimesheetApproved,
- readonly: shift.value.is_approved && isTimesheetApproved,
- optionsDense: !q.platform.is.mobile,
- hideDropdownIcon: true,
- menuOffset: [0, 10],
- menuAnchor: "bottom middle",
- menuSelf: "top middle",
- options: getShiftOptions(hasPTO.value, currentShifts.length > 1),
- class: `col rounded-5 q-mx-xs bg-dark ${!shift.value.is_approved && !isTimesheetApproved ? '' : 'inset-shadow'}`,
- popupContentClass: "text-uppercase text-weight-bold text-center rounded-5",
- style: shift.value.is_approved ? (holiday ? 'background-color: #7b1fa2 !important' : 'background-color: #0a7d32 !important;') : '',
- popupContentStyle: "border: 2px solid var(--q-accent)",
- }));
+ const isApproved = computed(() => isTimesheetApproved || shift.value.is_approved);
// ================== Methods ==================
@@ -170,7 +134,7 @@
-
+
+
{{ $t(scope.opt.label) }}
@@ -278,7 +261,7 @@
:disable="shift.is_approved"
dense
keep-color
- size="3em"
+ size="2.75em"
:color="holiday ? 'purple-5' : 'accent'"
icon="las la-building"
checked-icon="las la-laptop"
@@ -294,6 +277,15 @@
+
+
+
+ {{ $t('timesheet.shift.types.label') }}
+
+
@@ -319,38 +311,32 @@
v-else
class="col row items-start text-uppercase rounded-5 q-pa-xs"
>
-
-
- {{ $t('shared.misc.in') }}
-
-
+ />
-
-
- {{ $t('shared.misc.out') }}
-
-
+ />
padding-top: 0;
align-items: center;
}
+
+:deep(.q-field--dense.q-field--float .q-field__label) {
+ transform: translate(-17px, -60%) scale(0.75) !important;
+ border-radius: 10px 10px 10px 0px;
+}
\ No newline at end of file
diff --git a/src/modules/timesheets/components/shift-list-day.vue b/src/modules/timesheets/components/shift-list-day.vue
index c1ecda1..70aed74 100644
--- a/src/modules/timesheets/components/shift-list-day.vue
+++ b/src/modules/timesheets/components/shift-list-day.vue
@@ -3,127 +3,207 @@
lang="ts"
>
import ShiftListDayRow from 'src/modules/timesheets/components/shift-list-day-row.vue';
- import ShiftListDayRowMobile from 'src/modules/timesheets/components/mobile/shift-list-day-row-mobile.vue';
+ import ShiftListDateWidget from 'src/modules/timesheets/components/shift-list-date-widget.vue';
- import { inject, ref } from 'vue';
+ import { useI18n } from 'vue-i18n';
+ import { computed, inject, ref } from 'vue';
+ import { useUiStore } from 'src/stores/ui-store';
import { useTimesheetStore } from 'src/stores/timesheet-store';
import { useShiftApi } from 'src/modules/timesheets/composables/use-shift-api';
import { useTimesheetApi } from 'src/modules/timesheets/composables/use-timesheet-api';
import { isShiftOverlap } from 'src/modules/timesheets/utils/shift.util';
- import type { Shift } from 'src/modules/timesheets/models/shift.models';
+ import { Shift } from 'src/modules/timesheets/models/shift.models';
import type { TimesheetDay } from 'src/modules/timesheets/models/timesheet.models';
- // ================== State ==================
+ // ========== Constants ========================================
- const { timesheetId, weekDayIndex, day, dense = false, approved = false, holiday = false } = defineProps<{
+ const CURRENT_DATE_STRING = new Date().toISOString().slice(0, 10);
+
+ // ========== State ========================================
+
+ const day = defineModel({ required: true });
+
+ const { timesheetId, weekDayIndex, timesheetApproved = false } = defineProps<{
timesheetId: number;
weekDayIndex: number;
- day: TimesheetDay;
dense?: boolean;
- approved?: boolean;
- holiday?: boolean;
+ timesheetApproved?: boolean;
}>();
- const emit = defineEmits<{
- 'deleteUnsavedShift': [void];
- }>();
-
- const shift_api = useShiftApi();
- const timesheet_api = useTimesheetApi();
- const timesheet_store = useTimesheetStore();
- const preset_mouseover = ref(false);
- const shift_error_message = ref();
+ const { locale } = useI18n();
+ const uiStore = useUiStore();
+ const shiftApi = useShiftApi();
+ const timesheetApi = useTimesheetApi();
+ const timesheetStore = useTimesheetStore();
+ const presetMouseover = ref(false);
+ const shiftErrorMessage = ref();
const employeeEmail = inject('employeeEmail');
+ // ========== Computed ========================================
+
+ const isDayApproved = computed(() => day.value.shifts.length > 0 && day.value.shifts.every(
+ shift => shift.is_approved === true));
+
+ const isHoliday = computed(() => timesheetStore.federal_holidays.some(
+ holiday => holiday.date === day.value.date));
+
+ const isToday = computed(() => CURRENT_DATE_STRING === day.value.date);
+
// ================== Methods ==================
- const deleteCurrentShift = async (shift: Shift) => {
- if (shift.id <= 0) {
- shift.id = 0;
- emit('deleteUnsavedShift');
- } else {
- await shift_api.deleteShiftById(shift.id, employeeEmail);
- }
+ const addNewShift = () => {
+ uiStore.focusNextComponent = true;
+ const newShift = new Shift(day.value.date);
+ newShift.timesheet_id = timesheetId;
+ day.value.shifts.push(newShift);
+ };
- if (day.shifts.length < 2 && shift_error_message.value !== undefined) {
+ const deleteCurrentShift = async (shiftId: number, index: number) => {
+ if (shiftId <= 0)
+ day.value.shifts.splice(index, 1);
+ else
+ await shiftApi.deleteShiftById(shiftId, employeeEmail);
+
+ if (day.value.shifts.length < 2 && shiftErrorMessage.value !== undefined) {
onTimeFieldBlur();
}
};
const onTimeFieldBlur = () => {
- const is_error = isShiftOverlap(day.shifts);
- day.shifts.map(shift => shift.has_error = is_error);
+ const is_error = isShiftOverlap(day.value.shifts);
+ day.value.shifts.map(shift => shift.has_error = is_error);
if (is_error)
- shift_error_message.value = 'timesheet.errors.SHIFT_OVERLAP_SHORT';
+ shiftErrorMessage.value = 'timesheet.errors.SHIFT_OVERLAP_SHORT';
else
- shift_error_message.value = undefined;
+ shiftErrorMessage.value = undefined;
}
const onClickApplyDailyPreset = async () => {
- await timesheet_api.applyPreset(timesheetId, weekDayIndex, day.date, employeeEmail);
+ await timesheetApi.applyPreset(timesheetId, weekDayIndex, day.value.date, employeeEmail);
}
+
+ const getHolidayName = (date: string) => {
+ const holiday = timesheetStore.federal_holidays.find(holiday => holiday.date === date);
+ if (!holiday) return;
+
+ if (locale.value === 'fr-FR')
+ return holiday.nameFr;
+
+ else if (locale.value === 'en-CA')
+ return holiday.nameEn;
+ };
-
-
-
+
+
-
-
-
-
+ {{ getHolidayName(day.date) }}
+
-
\ No newline at end of file
+
+
+
\ No newline at end of file
diff --git a/src/modules/timesheets/components/shift-list-scrollable.vue b/src/modules/timesheets/components/shift-list-scrollable.vue
index cc33003..82975aa 100644
--- a/src/modules/timesheets/components/shift-list-scrollable.vue
+++ b/src/modules/timesheets/components/shift-list-scrollable.vue
@@ -1,8 +1,9 @@