feat(timesheet): added remote_work, add_shift, add_expense btns,

This commit is contained in:
Matthieu Haineault 2025-09-08 16:14:57 -04:00
parent d0b0f2df6c
commit e7dfe6db00
10 changed files with 273 additions and 170 deletions

View File

@ -259,19 +259,23 @@ export default {
previous_week:'Previous week', previous_week:'Previous week',
}, },
save_button:'Save', save_button:'Save',
shift_types: 'Shift`s Type', cancel_button:'Cancel',
shiftTypes: { remote_button: 'Remote work',
add_shift:'Add Shift',
shift_types_label: 'Shift`s Type',
shift_types: {
EMERGENCY: 'Emergency', EMERGENCY: 'Emergency',
EVENING: 'Evening', EVENING: 'Evening',
HOLIDAY: 'Holiday', HOLIDAY: 'Holiday',
REGULAR: 'Regular', REGULAR: 'Regular',
SICK: 'Sick Leave', SICK: 'Sick Leave',
VACATION: 'Vacation', VACATION: 'Vacation',
WORK_FROM_HOME: 'Work from home',
}, },
fields: { fields: {
start:'Start', start:'Start',
end:'End', end:'End',
header_comment:'Shift`s comment',
textarea_comment: 'Leave a comment here',
}, },
//rest //rest
timeSheetTab_1: 'Shifts', timeSheetTab_1: 'Shifts',

View File

@ -309,19 +309,24 @@ export default {
previous_week:'Semaine précédente', previous_week:'Semaine précédente',
}, },
save_button:'Enregistrer', save_button:'Enregistrer',
shift_types: 'Type de quart', cancel_button:'Annuler',
shiftTypes: { remote_button: 'Télétravail',
add_shift:'Ajouter une quart',
shift_types_label: 'Type de quart',
shift_types: {
EMERGENCY: 'Urgence', EMERGENCY: 'Urgence',
EVENING: 'Soir', EVENING: 'Soir',
HOLIDAY: 'Férier', HOLIDAY: 'Férier',
SICK: 'Absence', SICK: 'Absence',
REGULAR: 'Régulier', REGULAR: 'Régulier',
VACATION: 'Vacance', VACATION: 'Vacance',
WORK_FROM_HOME: 'Télétravail',
}, },
fields: { fields: {
start:'Entrée', start:'Entrée',
end:'Sortie', end:'Sortie',
header_comment:'Commentaire du Quart',
textarea_comment:'Laissez votre commentaire',
}, },
//rest //rest
timeSheetTab_1: 'Quarts de travail', timeSheetTab_1: 'Quarts de travail',

View File

@ -26,7 +26,7 @@ const save = ()=> {
<template> <template>
<q-card class="full-width"> <q-card class="full-width">
<div class="row items-center justify-between q-pa-md"> <div class="row items-center justify-between q-pa-md">
<div class="text-h6">Commentaire du quart</div> {{ $t('timesheet.fields.header_comment') }}
</div> </div>
<q-separator/> <q-separator/>
<div class="q-pa-md"> <div class="q-pa-md">
@ -35,7 +35,7 @@ const save = ()=> {
type="textarea" type="textarea"
autogrow autogrow
filled filled
label="Votre commentaire" :label= "$t('timesheet.fields.textarea_comment')"
:counter=true :counter=true
maxlength="512" maxlength="512"
color="primary" color="primary"
@ -45,11 +45,11 @@ const save = ()=> {
<q-btn <q-btn
color="secondary" color="secondary"
text-color="grey-8" text-color="grey-8"
label="close" :label="$t('timesheet.cancel_button')"
@click="close" @click="close"
/> />
<q-btn <q-btn
label="Sauvegarder" :label="$t('timesheet.save_button')"
color="primary" color="primary"
@click="save" @click="save"
/> />

View File

@ -28,6 +28,7 @@
<template> <template>
<div class="row q-mb-lg q-mt-lg" > <div class="row q-mb-lg q-mt-lg" >
<!-- navigation to previous week -->
<q-btn <q-btn
push rounded push rounded
icon="keyboard_arrow_left" icon="keyboard_arrow_left"
@ -43,6 +44,7 @@
> {{ $t( 'timesheet.nav_button.previous_week' )}} > {{ $t( 'timesheet.nav_button.previous_week' )}}
</q-tooltip> </q-tooltip>
</q-btn> </q-btn>
<!-- navigation to current week -->
<q-btn <q-btn
push rounded push rounded
icon="today" icon="today"
@ -58,6 +60,7 @@
>{{ $t('timesheet.nav_button.current_week') }} >{{ $t('timesheet.nav_button.current_week') }}
</q-tooltip> </q-tooltip>
</q-btn> </q-btn>
<!-- navigation through calendar date picker -->
<q-btn <q-btn
push rounded push rounded
icon="calendar_month" icon="calendar_month"
@ -73,6 +76,7 @@
>{{ $t('timesheet.nav_button.calendar_date_picker') }} >{{ $t('timesheet.nav_button.calendar_date_picker') }}
</q-tooltip> </q-tooltip>
</q-btn> </q-btn>
<!-- navigation to next week -->
<q-btn <q-btn
push rounded push rounded
icon="keyboard_arrow_right" icon="keyboard_arrow_right"
@ -90,6 +94,7 @@
</q-btn> </q-btn>
</div> </div>
<!-- date picker calendar -->
<q-dialog v-model="is_showing_calendar_picker" transition-show="jump-down" transition-hide="jump-up" position="top"> <q-dialog v-model="is_showing_calendar_picker" transition-show="jump-down" transition-hide="jump-up" position="top">
<q-date <q-date
v-model="calendar_date" v-model="calendar_date"

View File

@ -0,0 +1,64 @@
<script setup lang="ts">
/* eslint-disable */
import { computed } from 'vue';
import type { CreateShiftPayload, Shift } from '../../types/timesheet-shift-interface';
const props = defineProps<{
rows: Shift[];
week_dates: string[];
}>();
const emit = defineEmits<{
(e: 'save', payload: CreateShiftPayload[]): void;
}>();
const buildPayload = (): CreateShiftPayload[] => {
const dates = props.week_dates;
if(!Array.isArray(dates) || dates.length === 0) return [];
return props.rows.flatMap((row, idx) => {
const date = dates[idx];
const has_data = !!(row.type || row.start_time || row.end_time || row.comment);
if(!date || !has_data) return [];
const item: CreateShiftPayload = {
date,
type: row.type,
start_time: row.start_time,
end_time: row.end_time,
is_remote: row.is_remote,
};
if(row.comment) item.description = row.comment;
return[item];
})
};
const payload = computed(buildPayload);
const canSave = computed(()=> payload.value.length > 0);
const saveWeek = () => {
emit('save', payload.value);
}
</script>
<template>
<!-- save button -->
<q-btn
color="primary"
icon="save_alt"
class="col-1"
push
rounded
:disable="!canSave"
@click="saveWeek"
>
<q-tooltip
anchor="top middle"
self="center middle"
class="bg-primary text-uppercase text-weight-bold"
>{{ $t('timesheet.save_button') }}
</q-tooltip>
</q-btn>
</template>

View File

@ -1,42 +1,29 @@
<!-- list of days with shifts infos-->
<script setup lang="ts"> <script setup lang="ts">
/* eslint-disable */ /* eslint-disable */
import { computed, ref, watch } from 'vue'; import { computed, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useTimesheetStore } from 'src/stores/timesheet-store'; import { useTimesheetStore } from 'src/stores/timesheet-store';
import TimesheetShiftComment from '../shift/timesheet-shift-comment.vue'; import TimesheetShiftComment from '../shift/timesheet-shift-comment.vue';
import TimesheetNavigation from './timesheet-navigation.vue'; import TimesheetSavePayload from './timesheet-save-payload.vue';
import type { Shift } from '../../types/timesheet-shift-interface';
import type { CreateShiftPayload } from '../../types/timesheet-shift-interface'; import type { CreateShiftPayload } from '../../types/timesheet-shift-interface';
import { useTimesheetApi } from '../../composables/use-timesheet-api';
import { date as qdate } from 'quasar';
import { useI18n } from 'vue-i18n';
type ShiftRow = {
type: string;
start_time: string;
end_time: string;
comment: string;
is_approved: boolean;
}
const { t, tm } = useI18n();
const emit = defineEmits<{
(e: 'save', payload: CreateShiftPayload[]): void;
}>();
const timesheet_store = useTimesheetStore(); const timesheet_store = useTimesheetStore();
const timesheet_api = useTimesheetApi();
const SHIFT_KEY = ['REGULAR','WORK_FROM_HOME', 'EVENING', 'EMERGENCY', 'HOLIDAY', 'VACATION', 'SICK'] as const; const { t, tm, locale } = useI18n();
const days = computed(()=> (tm('timesheet.days') as string[]) ?? []);
const shift_options = computed(()=> SHIFT_KEY.map(key => ({ value: key, label: t(`timeSheet.shiftType.${key}`)}))); const SHIFT_KEY = ['REGULAR', 'EVENING', 'EMERGENCY', 'HOLIDAY', 'VACATION', 'SICK'] as const;
const days = computed(()=> {
void locale.value;
return (tm('timesheet.days') as string[]) ?? [];
});
const shift_type = computed(() => SHIFT_KEY.map(key => (`timesheet.shiftTypes.${key}`))); const shift_options = computed(()=> {
void locale.value;
return SHIFT_KEY.map(key => ({ value: key, label: t(`timesheet.shift_types.${key}`)}))
});
const empty_row = { type: '', start_time: '', end_time: '', comment: '', is_approved: false, is_remote: false };
const empty_row = { type: '', start_time: '', end_time: '', comment: '', is_approved: false };
//Week dates //Week dates
const week_dates = computed(() => { const week_dates = computed(() => {
const start_date = timesheet_store.current_timesheet.start_day; const start_date = timesheet_store.current_timesheet.start_day;
@ -46,17 +33,22 @@ const week_dates = computed(() => {
if(!mm) return []; if(!mm) return [];
const year = Number(mm[1]), month = Number(mm[2]), day = Number(mm[3]) const year = Number(mm[1]), month = Number(mm[2]), day = Number(mm[3])
const base = new Date(year, month - 1, day); const base = new Date(Date.UTC(year, month - 1, day));
const yyyymmdd = (date: Date) => `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2,'0')}-${String(date.getDate()).padStart(2,'0')}`; const yyyymmdd = (date: Date) => {
const yyyy = date.getFullYear();
const mm = String(date.getUTCMonth() + 1).padStart(2,'0');
const dd = String(date.getUTCDate()).padStart(2,'0');
return `${yyyy}-${mm}-${dd}`};
return Array.from({length:7 }, (_, i) => { return Array.from({length:7 }, (_, i) => {
const date = new Date(base); const date = new Date(base);
date.setDate(base.getDate() + i); date.setUTCDate(base.getDate() + i);
return yyyymmdd(date); return yyyymmdd(date);
}); });
}); });
//filling timesheet with shifts //filling timesheet with shifts
const rows = ref<ShiftRow[]>( const rows = ref<Shift[]>(
days.value.map((_,index) => { days.value.map((_,index) => {
const date_ISO = week_dates.value[index]; const date_ISO = week_dates.value[index];
const shift = timesheet_store.current_timesheet.shifts.find(sh => sh.date === date_ISO); const shift = timesheet_store.current_timesheet.shifts.find(sh => sh.date === date_ISO);
@ -66,31 +58,21 @@ const rows = ref<ShiftRow[]>(
end_time: shift.end_time || '', end_time: shift.end_time || '',
comment: shift.description || '', comment: shift.description || '',
is_approved: !!shift.is_approved, is_approved: !!shift.is_approved,
is_remote: !!shift.is_remote,
} }
: { ...empty_row }; : { ...empty_row };
}) })
); );
const hasData = (row: Shift) => !!(row.type || row.start_time || row.end_time || row.comment);
const show_comment = ref(false); const show_comment = ref(false);
const selected_index = ref<number | null>(null); const selected_index = ref<number | null>(null);
const selected_row = computed<ShiftRow | undefined>(()=> const selected_row = computed<Shift | undefined>(()=>
selected_index.value != null ? rows.value[selected_index.value] : undefined selected_index.value != null ? rows.value[selected_index.value] : undefined
); );
const is_calendar_limit = computed( () => {
return timesheet_store.current_pay_period.pay_year === 2024 &&
timesheet_store.current_pay_period.pay_period_no <= 1;
});
const onDateSelected = async (date_string: string) => {
const when = qdate.extractDate(date_string, 'YYYY-MM-DD');
timesheet_api.getCurrentWeekTimesheetOverview(when);
};
const setComment = (comment: string) => { const setComment = (comment: string) => {
if(selected_row.value) { if(selected_row.value) selected_row.value.comment = comment;
selected_row.value.comment = comment;
}
show_comment.value = false; show_comment.value = false;
} }
@ -99,34 +81,12 @@ const onClickComment = (index: number)=> {
show_comment.value = true; show_comment.value = true;
}; };
const buildPayload = (): CreateShiftPayload[] => { const clearRow = (index: number) => {
const dates = week_dates.value; rows.value[index] = { ...empty_row };
if(!Array.isArray(dates) || dates.length === 0) return [];
return rows.value.flatMap((row, idx) => {
const date = dates[idx];
const has_data = !!(row.type || row.start_time || row.end_time || row.comment);
if(!date || !has_data) return [];
const item: CreateShiftPayload = {
date,
type: row.type,
start_time: row.start_time,
end_time: row.end_time,
};
if(row.comment) item.description = row.comment;
return[item];
})
};
const saveWeek = () => {
const payload = buildPayload();
emit('save', payload);
} }
const clearRow = (idx: number) => {
rows.value[idx] = { ...empty_row }; const emit = defineEmits<{ (e: 'save', payload: CreateShiftPayload[]): void }>();
}
watch( watch(
() => [timesheet_store.current_timesheet.start_day, timesheet_store.current_timesheet.shifts], () => [timesheet_store.current_timesheet.start_day, timesheet_store.current_timesheet.shifts],
@ -135,8 +95,20 @@ watch(
rows.value = days.value.map((_, idx)=> { rows.value = days.value.map((_, idx)=> {
const shift = timesheet_store.current_timesheet.shifts.find(sh => sh.date === dates[idx]); const shift = timesheet_store.current_timesheet.shifts.find(sh => sh.date === dates[idx]);
return shift return shift
? { type: shift.bank_type || '', start_time: shift.start_time || '', end_time: shift.end_time || '', comment: shift.description || '', is_approved: shift.is_approved} ? { type: shift.bank_type || '',
: { type: '', start_time: '', end_time: '', comment: '', is_approved: false }; start_time: shift.start_time || '',
end_time: shift.end_time || '',
comment: shift.description || '',
is_approved: shift.is_approved,
is_remote: shift.is_remote,
}
: { type: '',
start_time: '',
end_time: '',
comment: '',
is_approved: false,
is_remote: false
};
}); });
}, },
{ deep: true, immediate: true } { deep: true, immediate: true }
@ -145,7 +117,7 @@ watch(
</script> </script>
<template> <template>
<q-card class="q-pa-md q-ma-md"> <q-card class="bg-transparent q-pa-md q-ma-md">
<q-dialog <q-dialog
v-model="show_comment" v-model="show_comment"
transition-show="fade" transition-show="fade"
@ -159,32 +131,45 @@ watch(
@click-close="show_comment = false" @click-close="show_comment = false"
/> />
</q-dialog> </q-dialog>
<q-form autofocus>
<!-- navigation buttons --> <q-form id="timesheet_shifts" autofocus class="bg-white q-pa-sm q-pt-lg rounded-10">
<TimesheetNavigation
:is-disabled="timesheet_store.is_loading" <div v-for="(row, index) in rows" :key="week_dates[index] ?? index" class="q-gutter-sm q-mb-sm" :class="$q.screen.lt.md ? 'column' : 'row'" >
:is-previous-limit="is_calendar_limit" <!--Week days-->
@date-selected="onDateSelected"
@pressed-previous-button="timesheet_api.previous_week()"
@pressed-current-button="timesheet_api.getCurrentWeekTimesheetOverview()"
@pressed-next-button="timesheet_api.next_week()"
/>
<div v-for="row, index in rows" :key="index" class="row q-gutter-sm q-mb-sm">
<!--Week days-->
<span class="text-weight-bold text-primary col-1">{{ days[index] }}</span> <span class="text-weight-bold text-primary col-1">{{ days[index] }}</span>
<!-- remote work toggle -->
<q-toggle
v-model="row.is_remote"
:disable="row.is_approved"
color="primary"
checked-icon="home_work"
unchecked-icon="business"
icon="home_work"
class="col-auto q-ml-sm"
>
<q-tooltip
anchor="top middle"
self="center middle"
class="bg-primary text-uppercase text-weight-bold"
>{{ $t('timesheet.remote_button') }}
</q-tooltip>
</q-toggle>
<!-- type selection --> <!-- type selection -->
<q-select <q-select
v-model="row.type" v-model="row.type"
:options="shift_type" :options="shift_options"
:readonly="row.is_approved" :readonly="row.is_approved"
class="col-1" class="col-3"
:label="$t('timesheet.shift_types')" :label="$t('timesheet.shift_types_label')"
dense dense
filled filled
color="primary" color="primary"
standout="bg-primary text-white" standout="bg-primary text-white"
options-dense options-dense
emit-value
map-options map-options
option-value="value"
option-label="label"
hide-dropdown-icon hide-dropdown-icon
/> />
<!-- start time input --> <!-- start time input -->
@ -213,41 +198,59 @@ watch(
step="300" step="300"
standout="bg-primary text-white" standout="bg-primary text-white"
/> />
<!-- comment icon --> <div class="col-3">
<q-btn <!-- comment button -->
:icon="row.comment.length > 0 ? 'announcement':'chat_bubble_outline'" <q-btn
:color="row.comment.length > 0 ? 'primary' : 'grey-8'" :icon="row.comment.length > 0 ? 'announcement':'chat_bubble_outline'"
:disable="row.is_approved" :color="row.comment.length > 0 ? 'primary' : 'grey-8'"
flat :disable="row.is_approved"
class="col-auto" flat
@click="onClickComment(index)" class="col-auto"
/> @click="onClickComment(index)"
<!-- reset entries icon --> />
<!-- expense button -->
<q-btn
:icon="row.comment.length > 0 ? 'receipt_long':'attach_money'"
flat
dense
class="q-pa-none q-ma-sm col-1"
:color="hasData(row) ? 'primary' : 'grey-8'"
@click="clearRow(index)"
/>
</div>
<!-- reset entries button -->
<q-btn <q-btn
icon="cleaning_services" icon="cleaning_services"
flat flat
dense dense
class="q-pa-none q-ma-none justify-right col-1" class="q-pa-none q-ma-sm col-auto"
:color="row.type.length || row.start_time.length || row.end_time.length || row.comment.length > 0 ? 'primary' : 'accent'" :color="hasData(row) ? 'primary' : 'grey-4'"
@click="clearRow(index)" @click="clearRow(index)"
/> />
<!-- add one more shift buttons -->
<q-btn
icon="more_time"
flat
dense
class="q-pa-none q-ma-sm col-auto"
color="primary"
>
<q-tooltip
anchor="top middle"
self="center middle"
class="bg-primary text-uppercase text-weight-bold"
>{{ $t('timesheet.add_shift') }}
</q-tooltip>
</q-btn>
</div> </div>
<!-- save button --> <TimesheetSavePayload
<q-btn :week_dates="week_dates"
color="primary" :rows="rows"
icon="save_alt" @save="(payload) => emit('save', payload)"
class="col-1 " />
rounded
push
@click="saveWeek"
>
<q-tooltip
anchor="top middle"
self="center middle"
class="bg-primary text-uppercase text-weight-bold"
>{{ $t('timesheet.save_button') }}
</q-tooltip>
</q-btn>
</q-form> </q-form>
</q-card>
</q-card>
</template> </template>

View File

@ -1,53 +1,59 @@
<script setup lang="ts"> <script setup lang="ts">
import { date } from 'quasar';
import { useTimesheetStore } from 'src/stores/timesheet-store'; import { useTimesheetStore } from 'src/stores/timesheet-store';
import { useTimesheetApi } from '../composables/use-timesheet-api'; import { useTimesheetApi } from '../composables/use-timesheet-api';
import { computed, onMounted } from 'vue'; import { computed, onMounted } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import TimesheetShiftForm from '../components/timesheet/timesheet-shift-form.vue'; import TimesheetShiftForm from '../components/timesheet/timesheet-shift-form.vue';
import type { CreateShiftPayload } from '../types/timesheet-shift-interface'; import type { CreateShiftPayload } from '../types/timesheet-shift-interface';
import TimesheetNavigation from '../components/timesheet/timesheet-navigation.vue';
import { date as qdate } from 'quasar';
const { locale } = useI18n();
const timesheet_store = useTimesheetStore();
const timesheet_api = useTimesheetApi();
const { this_week, saveTimesheetShifts } = timesheet_api;
const { locale } = useI18n(); onMounted(async () => {
const timesheet_store = useTimesheetStore(); await this_week();
const { this_week, saveTimesheetShifts } = useTimesheetApi(); });
onMounted(async () => { const date_options: Intl.DateTimeFormatOptions = {
await this_week(); day: 'numeric',
}); month: 'long',
year: 'numeric',
};
const date_options: Intl.DateTimeFormatOptions = { const timesheet_label = computed(() => {
day: 'numeric', const dates = timesheet_store.current_timesheet.label.split('.');
month: 'long', const start_date = new Intl.DateTimeFormat(locale.value, date_options).format(qdate.extractDate(dates[0] as string, 'YYYY-MM-DD'));
year: 'numeric', const end_date = new Intl.DateTimeFormat(locale.value, date_options).format(qdate.extractDate(dates[1] as string, 'YYYY-MM-DD'));
};
const timesheet_label = computed(() => { if ( dates.length === 1) {
const dates = timesheet_store.current_timesheet.label.split('.'); return { start_date: '_', end_date: '_', }
const start_date = new Intl.DateTimeFormat(locale.value, date_options).format(date.extractDate(dates[0] as string, 'YYYY-MM-DD')); }
const end_date = new Intl.DateTimeFormat(locale.value, date_options).format(date.extractDate(dates[1] as string, 'YYYY-MM-DD')); return { start_date, end_date };
});
if ( dates.length === 1) { const onSaveShifts = async (payload: CreateShiftPayload[]) => {
return { await saveTimesheetShifts(payload);
start_date: '_', };
end_date: '_',
}
}
return { start_date, end_date };
});
const onSaveShifts = async (payload: CreateShiftPayload[]) => { const is_calendar_limit = computed( () => {
await saveTimesheetShifts(payload); return timesheet_store.current_pay_period.pay_year === 2024 &&
}; timesheet_store.current_pay_period.pay_period_no <= 1;
});
const onDateSelected = async (date_string: string) => {
const when = qdate.extractDate(date_string, 'YYYY-MM-DD');
await timesheet_api.getCurrentWeekTimesheetOverview(when);
};
</script> </script>
<template> <template>
<q-page
padding <q-page padding class="q-pa-md bg-secondary" >
class="q-pa-md bg secondary" <div class="text-h4 row justify-center q-mt-lg text-uppercase text-weight-bolder text-grey-8 col-auto">
>
<div class="text-h4 row justify-center q-mt-lg text-uppercase text-weight-bolder text-grey-8">
{{ $t('pageTitles.timeSheets') }} {{ $t('pageTitles.timeSheets') }}
</div> </div>
<div class="row items-center justify-center q-py-none q-my-none"> <div class="row items-center justify-center q-py-none q-my-none">
@ -61,9 +67,20 @@ import type { CreateShiftPayload } from '../types/timesheet-shift-interface';
{{ timesheet_label.end_date }} {{ timesheet_label.end_date }}
</div> </div>
</div> </div>
<q-card class="q-mt-md"> <div>
<q-inner-loading :showing="timesheet_store.is_loading"/> <TimesheetNavigation
<TimesheetShiftForm @save="onSaveShifts"/> :is-disabled="timesheet_store.is_loading"
</q-card> :is-previous-limit="is_calendar_limit"
@date-selected="onDateSelected"
@pressed-previous-button="timesheet_api.previous_week()"
@pressed-current-button="timesheet_api.getCurrentWeekTimesheetOverview()"
@pressed-next-button="timesheet_api.next_week()"
/>
<q-card flat class="q-mt-md bg-secondary">
<TimesheetShiftForm @save="onSaveShifts" class="col-12"/>
<q-inner-loading :showing="timesheet_store.is_loading" color="primary"/>
</q-card>
</div>
<!-- navigation buttons -->
</q-page> </q-page>
</template> </template>

View File

@ -14,6 +14,7 @@ type Shifts = {
end_time: string; end_time: string;
description: string; description: string;
is_approved: boolean; is_approved: boolean;
is_remote: boolean;
} }
type Expenses = { type Expenses = {

View File

@ -4,6 +4,7 @@ export type CreateShiftPayload = {
start_time: string; start_time: string;
end_time: string; end_time: string;
description?: string; description?: string;
is_remote?: boolean;
}; };
export type CreateWeekShiftPayload = { export type CreateWeekShiftPayload = {
@ -11,7 +12,10 @@ export type CreateWeekShiftPayload = {
} }
export type Shift = { export type Shift = {
type: string;
start_time: string;
end_time: string;
comment: string;
is_approved: boolean; is_approved: boolean;
start: string; is_remote: boolean;
end: string;
} }