fix(timesheets): add autofill functionality to sick, vacation, holiday shifts.
This commit is contained in:
parent
6368beb24d
commit
2b1b0dbcbd
|
|
@ -5,20 +5,31 @@
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
import { QSelect, QInput } from 'quasar';
|
import { QSelect, QInput } from 'quasar';
|
||||||
import { useUiStore } from 'src/stores/ui-store';
|
import { useUiStore } from 'src/stores/ui-store';
|
||||||
import { SHIFT_OPTIONS } from 'src/modules/timesheets/utils/shift.util';
|
import { getCurrentDailyMinutesWorked, getTimeStringFromMinutes, SHIFT_OPTIONS } from 'src/modules/timesheets/utils/shift.util';
|
||||||
import type { Shift } from 'src/modules/timesheets/models/shift.models';
|
import type { Shift, ShiftOption, ShiftType } from 'src/modules/timesheets/models/shift.models';
|
||||||
|
import { getHoursMinutesStringFromHoursFloat } from 'src/utils/date-and-time-utils';
|
||||||
|
|
||||||
// ========== state ========================================
|
// ========== state ========================================
|
||||||
|
|
||||||
|
const SHIFT_TYPES_WITH_PREDEFINED_TIMES: ShiftType[] = ['HOLIDAY', 'SICK', 'VACATION'];
|
||||||
const COMMENT_LENGTH_MAX = 280;
|
const COMMENT_LENGTH_MAX = 280;
|
||||||
|
|
||||||
const shift = defineModel<Shift>('shift', { required: true });
|
const shift = defineModel<Shift>('shift', { required: true });
|
||||||
|
|
||||||
const { errorMessage = undefined, dense = false, hasShiftAfter = false, isTimesheetApproved = false } = defineProps<{
|
const {
|
||||||
|
dense = false,
|
||||||
|
hasShiftAfter = false,
|
||||||
|
isTimesheetApproved = false,
|
||||||
|
errorMessage = undefined,
|
||||||
|
expectedDailyHours = 8,
|
||||||
|
currentShifts,
|
||||||
|
} = defineProps<{
|
||||||
dense?: boolean;
|
dense?: boolean;
|
||||||
hasShiftAfter?: boolean;
|
hasShiftAfter?: boolean;
|
||||||
isTimesheetApproved?: boolean;
|
isTimesheetApproved?: boolean;
|
||||||
errorMessage?: string | undefined;
|
errorMessage?: string | undefined;
|
||||||
|
expectedDailyHours?: number;
|
||||||
|
currentShifts: Shift[];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
|
@ -27,10 +38,13 @@
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const ui_store = useUiStore();
|
const ui_store = useUiStore();
|
||||||
const shift_type_selected = ref(SHIFT_OPTIONS.find(option => option.value == shift.value.type));
|
const shiftTypeSelected = ref(SHIFT_OPTIONS.find(option => option.value == shift.value.type));
|
||||||
const select_ref = ref<QSelect | null>(null);
|
const select_ref = ref<QSelect | null>(null);
|
||||||
const is_showing_comment_popup = ref(false);
|
const is_showing_comment_popup = ref(false);
|
||||||
const error_message = ref('');
|
const error_message = ref('');
|
||||||
|
const isShowingPredefinedTime = ref(shift.value.type === 'HOLIDAY');
|
||||||
|
const predefinedHoursString = ref('');
|
||||||
|
const predefinedHoursBgColor = ref(`bg-${shiftTypeSelected.value?.icon_color ?? ''}`);
|
||||||
|
|
||||||
// ========== computed ========================================
|
// ========== computed ========================================
|
||||||
|
|
||||||
|
|
@ -39,7 +53,7 @@
|
||||||
// ========== methods =========================================
|
// ========== methods =========================================
|
||||||
|
|
||||||
const onBlurShiftTypeSelect = () => {
|
const onBlurShiftTypeSelect = () => {
|
||||||
if (shift_type_selected.value === undefined) {
|
if (shiftTypeSelected.value === undefined) {
|
||||||
shift.value.type = 'REGULAR';
|
shift.value.type = 'REGULAR';
|
||||||
shift.value.id = 0;
|
shift.value.id = 0;
|
||||||
emit('requestDelete');
|
emit('requestDelete');
|
||||||
|
|
@ -63,11 +77,35 @@
|
||||||
return 'negative';
|
return 'negative';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onShiftTypeChange = (option: ShiftOption) => {
|
||||||
|
shift.value.type = option.value;
|
||||||
|
|
||||||
|
if (SHIFT_TYPES_WITH_PREDEFINED_TIMES.includes(option.value)) {
|
||||||
|
predefinedHoursBgColor.value = `bg-${option.icon_color}`;
|
||||||
|
shift.value.start_time = '00:00';
|
||||||
|
|
||||||
|
if (option.value === 'SICK' || option.value === 'VACATION') {
|
||||||
|
const workedMinutes = getCurrentDailyMinutesWorked(currentShifts);
|
||||||
|
console.log('worked minutes: ', workedMinutes);
|
||||||
|
const expectedWorkedMinutes = expectedDailyHours * 60;
|
||||||
|
const leftOverMinutes = expectedWorkedMinutes - workedMinutes;
|
||||||
|
|
||||||
|
shift.value.end_time = getTimeStringFromMinutes(leftOverMinutes);
|
||||||
|
isShowingPredefinedTime.value = false;
|
||||||
|
} else {
|
||||||
|
isShowingPredefinedTime.value = true;
|
||||||
|
predefinedHoursString.value = getHoursMinutesStringFromHoursFloat(expectedDailyHours);
|
||||||
|
shift.value.end_time = getTimeStringFromMinutes(expectedDailyHours * 60);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
isShowingPredefinedTime.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (ui_store.focus_next_component) {
|
if (ui_store.focus_next_component) {
|
||||||
select_ref.value?.focus();
|
select_ref.value?.focus();
|
||||||
select_ref.value?.showPopup();
|
select_ref.value?.showPopup();
|
||||||
shift_type_selected.value = undefined;
|
shiftTypeSelected.value = undefined;
|
||||||
ui_store.focus_next_component = false;
|
ui_store.focus_next_component = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,7 +169,7 @@
|
||||||
<!-- shift type -->
|
<!-- shift type -->
|
||||||
<q-select
|
<q-select
|
||||||
ref="select"
|
ref="select"
|
||||||
v-model="shift_type_selected"
|
v-model="shiftTypeSelected"
|
||||||
:standout="$q.dark.isActive ? 'bg-blue-grey-3' : 'bg-blue-grey-9'"
|
:standout="$q.dark.isActive ? 'bg-blue-grey-3' : 'bg-blue-grey-9'"
|
||||||
dense
|
dense
|
||||||
:borderless="(shift.is_approved && isTimesheetApproved)"
|
:borderless="(shift.is_approved && isTimesheetApproved)"
|
||||||
|
|
@ -148,7 +186,7 @@
|
||||||
:style="shift.is_approved ? 'background-color: #0a7d32 !important;' : ''"
|
:style="shift.is_approved ? 'background-color: #0a7d32 !important;' : ''"
|
||||||
popup-content-style="border: 2px solid var(--q-accent)"
|
popup-content-style="border: 2px solid var(--q-accent)"
|
||||||
@blur="onBlurShiftTypeSelect"
|
@blur="onBlurShiftTypeSelect"
|
||||||
@update:model-value="option => shift.type = option.value"
|
@update:model-value="onShiftTypeChange"
|
||||||
>
|
>
|
||||||
<template #selected-item="scope">
|
<template #selected-item="scope">
|
||||||
<div
|
<div
|
||||||
|
|
@ -234,7 +272,25 @@
|
||||||
</q-select>
|
</q-select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col row items-start text-uppercase rounded-5 q-pa-xs">
|
<div
|
||||||
|
v-if="isShowingPredefinedTime"
|
||||||
|
class="col row items-start text-uppercase rounded-5 q-pa-xs relative-position"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="absolute-full rounded-5 q-mx-sm q-my-xs"
|
||||||
|
:class="predefinedHoursBgColor"
|
||||||
|
style="opacity: 0.3;"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<span class="col text-center text-uppercase text-h6 text-bold q-py-xs">
|
||||||
|
{{ getHoursMinutesStringFromHoursFloat(expectedDailyHours) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="col row items-start text-uppercase rounded-5 q-pa-xs"
|
||||||
|
>
|
||||||
<!-- punch in field -->
|
<!-- punch in field -->
|
||||||
<div class="col q-pr-xs">
|
<div class="col q-pr-xs">
|
||||||
<q-input
|
<q-input
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,14 @@
|
||||||
import { QSelect, QInput, useQuasar, type QSelectProps, QPopupProxy } from 'quasar';
|
import { QSelect, QInput, useQuasar, type QSelectProps, QPopupProxy } from 'quasar';
|
||||||
import { useUiStore } from 'src/stores/ui-store';
|
import { useUiStore } from 'src/stores/ui-store';
|
||||||
import { useAuthStore } from 'src/stores/auth-store';
|
import { useAuthStore } from 'src/stores/auth-store';
|
||||||
import { SHIFT_OPTIONS } from 'src/modules/timesheets/utils/shift.util';
|
import { getCurrentDailyMinutesWorked, getShiftOptions, getTimeStringFromMinutes, SHIFT_OPTIONS } from 'src/modules/timesheets/utils/shift.util';
|
||||||
import type { TotalHours } from 'src/modules/timesheets/models/timesheet.models';
|
|
||||||
import type { Shift, ShiftOption, ShiftType } from 'src/modules/timesheets/models/shift.models';
|
import type { Shift, ShiftOption, ShiftType } from 'src/modules/timesheets/models/shift.models';
|
||||||
import { getHoursMinutesStringFromHoursFloat } from 'src/utils/date-and-time-utils';
|
import { getHoursMinutesStringFromHoursFloat } from 'src/utils/date-and-time-utils';
|
||||||
|
|
||||||
// ========== Constants ========================================
|
// ========== Constants ========================================
|
||||||
|
|
||||||
const SHIFT_TYPES_WITH_PREDEFINED_TIMES: ShiftType[] = ['HOLIDAY', 'SICK', 'VACATION'];
|
const SHIFT_TYPES_WITH_PREDEFINED_TIMES: ShiftType[] = ['HOLIDAY', 'SICK', 'VACATION'];
|
||||||
|
const COMMENT_LENGTH_MAX = 280;
|
||||||
|
|
||||||
// ========== State ========================================
|
// ========== State ========================================
|
||||||
|
|
||||||
|
|
@ -23,11 +23,11 @@ import { getHoursMinutesStringFromHoursFloat } from 'src/utils/date-and-time-uti
|
||||||
const {
|
const {
|
||||||
errorMessage = undefined,
|
errorMessage = undefined,
|
||||||
isTimesheetApproved = false,
|
isTimesheetApproved = false,
|
||||||
|
currentShifts,
|
||||||
holiday = false,
|
holiday = false,
|
||||||
expectedDailyHours = 8,
|
expectedDailyHours = 8,
|
||||||
dailyHours,
|
|
||||||
} = defineProps<{
|
} = defineProps<{
|
||||||
dailyHours: TotalHours;
|
currentShifts: Shift[];
|
||||||
expectedDailyHours?: number;
|
expectedDailyHours?: number;
|
||||||
isTimesheetApproved?: boolean;
|
isTimesheetApproved?: boolean;
|
||||||
errorMessage?: string | undefined;
|
errorMessage?: string | undefined;
|
||||||
|
|
@ -39,9 +39,6 @@ import { getHoursMinutesStringFromHoursFloat } from 'src/utils/date-and-time-uti
|
||||||
'onTimeFieldBlur': [void];
|
'onTimeFieldBlur': [void];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
|
||||||
const COMMENT_LENGTH_MAX = 280;
|
|
||||||
|
|
||||||
const q = useQuasar();
|
const q = useQuasar();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const ui_store = useUiStore();
|
const ui_store = useUiStore();
|
||||||
|
|
@ -53,13 +50,15 @@ import { getHoursMinutesStringFromHoursFloat } from 'src/utils/date-and-time-uti
|
||||||
const selectRef = ref<QSelect | null>(null);
|
const selectRef = ref<QSelect | null>(null);
|
||||||
const shiftErrorMessage = ref<string | undefined>();
|
const shiftErrorMessage = ref<string | undefined>();
|
||||||
const is_showing_delete_confirm = ref(false);
|
const is_showing_delete_confirm = ref(false);
|
||||||
const isShowingPredefinedTime = ref(false);
|
const isShowingPredefinedTime = ref(shift.value.type === 'HOLIDAY');
|
||||||
const popupProxyRef = ref<QPopupProxy | null>(null);
|
const popupProxyRef = ref<QPopupProxy | null>(null);
|
||||||
const predefinedHours = ref(0);
|
const predefinedHoursString = ref('');
|
||||||
const predefinedHoursBgColor = ref('');
|
const predefinedHoursBgColor = ref(`bg-${shiftTypeSelected.value?.icon_color ?? ''}`);
|
||||||
|
|
||||||
// ================== Computed ==================
|
// ================== Computed ==================
|
||||||
|
|
||||||
|
const hasPTO = computed(() => currentShifts.some(shift => SHIFT_TYPES_WITH_PREDEFINED_TIMES.includes(shift.type)));
|
||||||
|
|
||||||
const rightClickMenuIcon = computed(() => shift.value.is_approved ? 'lock_open' : 'lock');
|
const rightClickMenuIcon = computed(() => shift.value.is_approved ? 'lock_open' : 'lock');
|
||||||
|
|
||||||
const rightClickMenuLabel = computed(() => shift.value.is_approved ?
|
const rightClickMenuLabel = computed(() => shift.value.is_approved ?
|
||||||
|
|
@ -94,7 +93,7 @@ import { getHoursMinutesStringFromHoursFloat } from 'src/utils/date-and-time-uti
|
||||||
menuOffset: [0, 10],
|
menuOffset: [0, 10],
|
||||||
menuAnchor: "bottom middle",
|
menuAnchor: "bottom middle",
|
||||||
menuSelf: "top middle",
|
menuSelf: "top middle",
|
||||||
options: SHIFT_OPTIONS,
|
options: getShiftOptions(hasPTO.value, currentShifts.length > 1),
|
||||||
class: `col rounded-5 q-mx-xs bg-dark ${!shift.value.is_approved && !isTimesheetApproved ? '' : 'inset-shadow'}`,
|
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",
|
popupContentClass: "text-uppercase text-weight-bold text-center rounded-5",
|
||||||
style: shift.value.is_approved ? (holiday ? 'background-color: #7b1fa2 !important' : 'background-color: #0a7d32 !important;') : '',
|
style: shift.value.is_approved ? (holiday ? 'background-color: #7b1fa2 !important' : 'background-color: #0a7d32 !important;') : '',
|
||||||
|
|
@ -149,21 +148,23 @@ import { getHoursMinutesStringFromHoursFloat } from 'src/utils/date-and-time-uti
|
||||||
shift.value.type = option.value;
|
shift.value.type = option.value;
|
||||||
|
|
||||||
if (SHIFT_TYPES_WITH_PREDEFINED_TIMES.includes(option.value)) {
|
if (SHIFT_TYPES_WITH_PREDEFINED_TIMES.includes(option.value)) {
|
||||||
isShowingPredefinedTime.value = true;
|
|
||||||
predefinedHoursBgColor.value = `bg-${option.icon_color}`;
|
predefinedHoursBgColor.value = `bg-${option.icon_color}`;
|
||||||
|
shift.value.start_time = '00:00';
|
||||||
|
|
||||||
if (option.value === 'SICK') {
|
if (option.value === 'SICK' || option.value === 'VACATION') {
|
||||||
const workedHours =
|
const workedMinutes = getCurrentDailyMinutesWorked(currentShifts);
|
||||||
dailyHours.regular +
|
console.log('worked minutes: ', workedMinutes);
|
||||||
dailyHours.emergency +
|
const expectedWorkedMinutes = expectedDailyHours * 60;
|
||||||
dailyHours.evening +
|
const leftOverMinutes = expectedWorkedMinutes - workedMinutes;
|
||||||
dailyHours.holiday +
|
|
||||||
dailyHours.sick +
|
shift.value.end_time = getTimeStringFromMinutes(leftOverMinutes);
|
||||||
dailyHours.vacation;
|
} else {
|
||||||
predefinedHours.value = Math.max(expectedDailyHours - workedHours, 0);
|
isShowingPredefinedTime.value = true;
|
||||||
|
predefinedHoursString.value = getHoursMinutesStringFromHoursFloat(expectedDailyHours);
|
||||||
|
shift.value.start_time = '00:00';
|
||||||
|
shift.value.end_time = `${expectedDailyHours}:00`;
|
||||||
}
|
}
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
isShowingPredefinedTime.value = false;
|
isShowingPredefinedTime.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -334,21 +335,21 @@ import { getHoursMinutesStringFromHoursFloat } from 'src/utils/date-and-time-uti
|
||||||
<!-- If shift type has predefined timestamps -->
|
<!-- If shift type has predefined timestamps -->
|
||||||
<div
|
<div
|
||||||
v-if="isShowingPredefinedTime"
|
v-if="isShowingPredefinedTime"
|
||||||
class="col row q-px-sm relative-position flex-center"
|
class="col row q-pa-xs relative-position flex-center"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="absolute-full rounded-5 q-mx-sm"
|
class="absolute-full rounded-5 q-mx-sm q-my-xs"
|
||||||
:class="predefinedHoursBgColor"
|
:class="predefinedHoursBgColor"
|
||||||
style="opacity: 0.3;"
|
style="opacity: 0.3;"
|
||||||
></div>
|
></div>
|
||||||
<span class="col text-center text-uppercase text-h6 text-bold">{{ getHoursMinutesStringFromHoursFloat(predefinedHours) }}</span>
|
|
||||||
|
<span class="col text-center text-uppercase text-h6 text-bold q-py-xs">
|
||||||
|
{{ getHoursMinutesStringFromHoursFloat(expectedDailyHours) }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Else show input fields for in-out timestamps -->
|
<!-- Else show input fields for in-out timestamps -->
|
||||||
<div
|
<div v-else class="col row items-start text-uppercase rounded-5 q-pa-xs">
|
||||||
v-else
|
|
||||||
class="col row items-start text-uppercase rounded-5 q-pa-xs"
|
|
||||||
>
|
|
||||||
<q-input
|
<q-input
|
||||||
ref="start_time"
|
ref="start_time"
|
||||||
v-model="shift.start_time"
|
v-model="shift.start_time"
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,7 @@
|
||||||
:is-timesheet-approved="approved"
|
:is-timesheet-approved="approved"
|
||||||
:error-message="shift_error_message"
|
:error-message="shift_error_message"
|
||||||
:dense="dense"
|
:dense="dense"
|
||||||
|
:current-shifts="day.shifts"
|
||||||
:has-shift-after="shift_index < day.shifts.length - 1"
|
:has-shift-after="shift_index < day.shifts.length - 1"
|
||||||
@request-delete="deleteCurrentShift(shift)"
|
@request-delete="deleteCurrentShift(shift)"
|
||||||
@on-time-field-blur="onTimeFieldBlur()"
|
@on-time-field-blur="onTimeFieldBlur()"
|
||||||
|
|
@ -116,7 +117,7 @@
|
||||||
v-else
|
v-else
|
||||||
v-model:shift="day.shifts[shift_index]!"
|
v-model:shift="day.shifts[shift_index]!"
|
||||||
:holiday="holiday"
|
:holiday="holiday"
|
||||||
:daily-hours="day.daily_hours"
|
:current-shifts="day.shifts"
|
||||||
:is-timesheet-approved="approved"
|
:is-timesheet-approved="approved"
|
||||||
:error-message="shift_error_message"
|
:error-message="shift_error_message"
|
||||||
@request-delete="deleteCurrentShift(shift)"
|
@request-delete="deleteCurrentShift(shift)"
|
||||||
|
|
|
||||||
|
|
@ -46,4 +46,5 @@ export interface ShiftOption extends QSelectOption {
|
||||||
value: ShiftType;
|
value: ShiftType;
|
||||||
icon: string;
|
icon: string;
|
||||||
icon_color: string;
|
icon_color: string;
|
||||||
|
disable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ export const DATE_FORMAT_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
|
||||||
export interface TimesheetResponse {
|
export interface TimesheetResponse {
|
||||||
has_preset_schedule: boolean;
|
has_preset_schedule: boolean;
|
||||||
employee_fullname: string;
|
employee_fullname: string;
|
||||||
|
daily_expected_hours: number;
|
||||||
timesheets: Timesheet[];
|
timesheets: Timesheet[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,82 @@
|
||||||
import { date } from "quasar";
|
import { date } from "quasar";
|
||||||
import type { SchedulePresetShift } from "src/modules/employee-list/models/schedule-presets.models";
|
import type { SchedulePresetShift } from "src/modules/employee-list/models/schedule-presets.models";
|
||||||
import type { Shift, ShiftOption } from "src/modules/timesheets/models/shift.models";
|
import type { Shift, ShiftOption, ShiftType } from "src/modules/timesheets/models/shift.models";
|
||||||
|
|
||||||
export const isShiftOverlap = (shifts: Shift[] | SchedulePresetShift[]): boolean => {
|
export const isShiftOverlap = (shifts: Shift[] | SchedulePresetShift[]): boolean => {
|
||||||
if (shifts.length < 2) return false;
|
if (shifts.length < 2) return false;
|
||||||
|
|
||||||
const parsed_shifts = shifts.map(shift => ({
|
const parsed_shifts = shifts.map(shift => ({
|
||||||
start: date.extractDate(`2000-01-01 ${shift.start_time}`, 'YYYY-MM-DD HH:mm').getTime(),
|
start: date.extractDate(`2000-01-01 ${shift.start_time}`, 'YYYY-MM-DD HH:mm').getTime(),
|
||||||
end: date.extractDate(`2000-01-01 ${shift.end_time}`, 'YYYY-MM-DD HH:mm').getTime(),
|
end: date.extractDate(`2000-01-01 ${shift.end_time}`, 'YYYY-MM-DD HH:mm').getTime(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
for (let i = 0; i < parsed_shifts.length; i++) {
|
for (let i = 0; i < parsed_shifts.length; i++) {
|
||||||
for (let j = i + 1; j < parsed_shifts.length; j++) {
|
for (let j = i + 1; j < parsed_shifts.length; j++) {
|
||||||
const parsed_shift_a = parsed_shifts[i];
|
const parsed_shift_a = parsed_shifts[i];
|
||||||
const parsed_shift_b = parsed_shifts[j];
|
const parsed_shift_b = parsed_shifts[j];
|
||||||
|
|
||||||
if (parsed_shift_a === undefined || parsed_shift_b === undefined) continue;
|
if (parsed_shift_a === undefined || parsed_shift_b === undefined) continue;
|
||||||
|
|
||||||
if (Math.max(parsed_shift_a.start, parsed_shift_b.start) < Math.min(parsed_shift_a.end, parsed_shift_b.end)) {
|
if (Math.max(parsed_shift_a.start, parsed_shift_b.start) < Math.min(parsed_shift_a.end, parsed_shift_b.end)) {
|
||||||
return true; // overlap found
|
return true; // overlap found
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getCurrentDailyMinutesWorked = (shifts: Shift[]): number => {
|
||||||
|
const shiftTypesToIgnore: ShiftType[] = ['HOLIDAY', 'SICK', 'VACATION'];
|
||||||
|
let minutes = 0;
|
||||||
|
|
||||||
|
shifts.forEach(shift => {
|
||||||
|
if (shiftTypesToIgnore.includes(shift.type)) return;
|
||||||
|
|
||||||
|
const startTime = new Date(`1970-01-01T${shift.start_time}:00`);
|
||||||
|
const endTime = new Date(`1970-01-01T${shift.end_time}:00`);
|
||||||
|
|
||||||
|
const diff = date.getDateDiff(endTime, startTime, 'minutes');
|
||||||
|
minutes += diff;
|
||||||
|
});
|
||||||
|
|
||||||
|
return minutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getTimeStringFromMinutes = (minutes: number): string => {
|
||||||
|
const h = Math.floor(minutes / 60);
|
||||||
|
const m = minutes % 60;
|
||||||
|
|
||||||
|
if (h < 10 ) {
|
||||||
|
if (m < 10)
|
||||||
|
return `0${h}:0${m}`;
|
||||||
|
|
||||||
|
return `0${h}:${m}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${h}:${m}`;
|
||||||
|
}
|
||||||
|
|
||||||
export const SHIFT_OPTIONS: ShiftOption[] = [
|
export const SHIFT_OPTIONS: ShiftOption[] = [
|
||||||
{ label: 'timesheet.shift.types.REGULAR', value: 'REGULAR', icon: 'wb_sunny', icon_color: 'accent' },
|
{ label: 'timesheet.shift.types.REGULAR', value: 'REGULAR', icon: 'wb_sunny', icon_color: 'accent' },
|
||||||
{ label: 'timesheet.shift.types.EVENING', value: 'EVENING', icon: 'bedtime', icon_color: 'orange-5' },
|
{ label: 'timesheet.shift.types.EVENING', value: 'EVENING', icon: 'bedtime', icon_color: 'orange-5' },
|
||||||
{ label: 'timesheet.shift.types.EMERGENCY', value: 'EMERGENCY', icon: 'ring_volume', icon_color: 'red-5' },
|
{ label: 'timesheet.shift.types.EMERGENCY', value: 'EMERGENCY', icon: 'ring_volume', icon_color: 'red-5' },
|
||||||
{ label: 'timesheet.shift.types.VACATION', value: 'VACATION', icon: 'beach_access', icon_color: 'deep-orange-5' },
|
{ label: 'timesheet.shift.types.VACATION', value: 'VACATION', icon: 'beach_access', icon_color: 'deep-orange-5' },
|
||||||
{ label: 'timesheet.shift.types.HOLIDAY', value: 'HOLIDAY', icon: 'event', icon_color: 'purple-5' },
|
{ label: 'timesheet.shift.types.HOLIDAY', value: 'HOLIDAY', icon: 'event', icon_color: 'purple-5' },
|
||||||
{ label: 'timesheet.shift.types.SICK', value: 'SICK', icon: 'medication_liquid', icon_color: 'light-blue-6' },
|
{ label: 'timesheet.shift.types.SICK', value: 'SICK', icon: 'medication_liquid', icon_color: 'light-blue-6' },
|
||||||
// { label: 'timesheet.shift.types.BANKING', value: 'BANKING', icon: 'savings', icon_color: 'pink-3' },
|
// { label: 'timesheet.shift.types.BANKING', value: 'BANKING', icon: 'savings', icon_color: 'pink-3' },
|
||||||
{ label: 'timesheet.shift.types.WITHDRAW_BANKED', value: 'WITHDRAW_BANKED', icon: 'attach_money', icon_color: 'yellow-4' },
|
{ label: 'timesheet.shift.types.WITHDRAW_BANKED', value: 'WITHDRAW_BANKED', icon: 'attach_money', icon_color: 'yellow-4' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const getShiftOptions = (disablePTO: boolean, isNotUnique: boolean): ShiftOption[] => {
|
||||||
|
return [
|
||||||
|
{ label: 'timesheet.shift.types.REGULAR', value: 'REGULAR', icon: 'wb_sunny', icon_color: 'accent' },
|
||||||
|
{ label: 'timesheet.shift.types.EVENING', value: 'EVENING', icon: 'bedtime', icon_color: 'orange-5' },
|
||||||
|
{ label: 'timesheet.shift.types.EMERGENCY', value: 'EMERGENCY', icon: 'ring_volume', icon_color: 'red-5' },
|
||||||
|
{ label: 'timesheet.shift.types.VACATION', value: 'VACATION', icon: 'beach_access', icon_color: 'deep-orange-5', disable: disablePTO },
|
||||||
|
{ label: 'timesheet.shift.types.HOLIDAY', value: 'HOLIDAY', icon: 'event', icon_color: 'purple-5', disable: isNotUnique || disablePTO },
|
||||||
|
{ label: 'timesheet.shift.types.SICK', value: 'SICK', icon: 'medication_liquid', icon_color: 'light-blue-6', disable: disablePTO },
|
||||||
|
// { label: 'timesheet.shift.types.BANKING', value: 'BANKING', icon: 'savings', icon_color: 'pink-3' },
|
||||||
|
// { label: 'timesheet.shift.types.WITHDRAW_BANKED', value: 'WITHDRAW_BANKED', icon: 'attach_money', icon_color: 'yellow-4' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
@ -20,14 +20,27 @@ export const getMinutes = (hours: number) => {
|
||||||
return minutes > 1 ? minutes.toString() : '0';
|
return minutes > 1 ? minutes.toString() : '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getHoursMinutesStringFromHoursFloat = (hours: number): string => {
|
export const getHoursMinutesStringFromHoursFloat = (hours: number, minutes?: number): string => {
|
||||||
let flat_hours = Math.floor(hours);
|
let flatHours = Math.floor(hours);
|
||||||
let minutes = Math.round((hours - flat_hours) * 60);
|
let flatMinutes = minutes ?? Math.round((hours - flatHours) * 60);
|
||||||
|
|
||||||
if (minutes === 60) {
|
if (flatMinutes === 60) {
|
||||||
flat_hours += 1;
|
flatHours += 1;
|
||||||
minutes = 0;
|
flatMinutes = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${flat_hours}h${minutes > 1 ? ' ' + minutes : ''}`
|
return `${flatHours}h${flatMinutes > 1 ? ' ' + flatMinutes : ''}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getHoursMinutesBetweenTwoHHmm = (startTime: string, endTime: string): {
|
||||||
|
hours: number,
|
||||||
|
minutes: number,
|
||||||
|
} => {
|
||||||
|
const [startHours, startMinutes] = startTime.split(':');
|
||||||
|
const [endHours, endMinutes] = endTime.split(':');
|
||||||
|
|
||||||
|
return {
|
||||||
|
hours: Number(endHours) - Number(startHours),
|
||||||
|
minutes: Number(endMinutes) - Number(startMinutes),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user