Merge pull request 'fix(payperiodpicker, expensedialog): change date picker display from dialog to menu, add transition animations for expense dialog form, move update dialog form into expense item.' (#24) from dev/nicolas/timesheet-gui-refactor into main
Reviewed-on: Targo/targo_frontend#24
This commit is contained in:
commit
ce2fd3e024
|
|
@ -39,7 +39,7 @@ body.body--dark {
|
|||
}
|
||||
|
||||
.frosted-glass {
|
||||
background-color: #FFFA !important;
|
||||
background-color: #0008 !important;
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@ $primary : #30303A;
|
|||
$secondary : #DAE0E7;
|
||||
$accent : #0c9a3b;
|
||||
|
||||
$dark-shadow-color : #00220f;
|
||||
$dark-shadow-color : #173625;
|
||||
|
||||
$elevation-dark-umbra : rgba($dark-shadow-color, 1);
|
||||
$elevation-dark-penumbra : rgba($dark-shadow-color, 0.2);
|
||||
$elevation-dark-ambient : rgba($dark-shadow-color, 0.2);
|
||||
$elevation-dark-penumbra : rgba($dark-shadow-color, 0.5);
|
||||
$elevation-dark-ambient : rgba($dark-shadow-color, 0.3);
|
||||
|
||||
$dark-shadow-2 : 0 3px 5px -1px $elevation-dark-umbra, 0 5px 8px $elevation-dark-penumbra, 0 1px 14px $elevation-dark-ambient;
|
||||
$dark-shadow-2 : 2px 3px $elevation-dark-umbra, 2px 3px 6px $elevation-dark-penumbra, 2px 3px 14px $elevation-dark-ambient;
|
||||
$layout-shadow-dark : 0 0 10px 5px rgba($dark-shadow-color, 0.5);
|
||||
|
||||
$input-text-color : #455A64;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
<script setup lang="ts">
|
||||
<script
|
||||
setup
|
||||
lang="ts"
|
||||
>
|
||||
import { computed, ref } from 'vue';
|
||||
import { date} from 'quasar';
|
||||
import { date } from 'quasar';
|
||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||
|
||||
const NEXT = 1;
|
||||
|
|
@ -10,18 +13,18 @@
|
|||
const timesheet_store = useTimesheetStore();
|
||||
|
||||
const is_showing_calendar_picker = ref(false);
|
||||
const calendar_date = ref(date.formatDate( Date.now(), 'YYYY-MM-DD' ));
|
||||
const calendar_date = ref(date.formatDate(Date.now(), 'YYYY-MM-DD'));
|
||||
const is_disabled = computed(() => timesheet_store.pay_period === undefined);
|
||||
|
||||
const emit = defineEmits<{
|
||||
'date-selected': [ value: string ]
|
||||
'date-selected': [value: string]
|
||||
'pressed-previous-button': []
|
||||
'pressed-next-button': []
|
||||
}>();
|
||||
|
||||
const is_previous_pay_period_limit = computed( ()=>
|
||||
( timesheet_store.pay_period?.pay_year === 2024 &&
|
||||
timesheet_store.pay_period?.pay_period_no <= 1 ) ?? false
|
||||
const is_previous_pay_period_limit = computed(() =>
|
||||
(timesheet_store.pay_period?.pay_year === 2024 &&
|
||||
timesheet_store.pay_period?.pay_period_no <= 1) ?? false
|
||||
);
|
||||
|
||||
const onDateSelected = (value: string) => {
|
||||
|
|
@ -59,10 +62,11 @@
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="row" >
|
||||
<div class="row">
|
||||
<!-- navigation to previous week -->
|
||||
<q-btn
|
||||
push rounded
|
||||
push
|
||||
rounded
|
||||
icon="keyboard_arrow_left"
|
||||
color="accent"
|
||||
@click="getPreviousPayPeriod"
|
||||
|
|
@ -74,13 +78,14 @@
|
|||
self="center middle"
|
||||
class="bg-primary text-uppercase text-weight-bold"
|
||||
>
|
||||
{{ $t( 'timesheet.nav_button.previous_week' )}}
|
||||
{{ $t('timesheet.nav_button.previous_week') }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
|
||||
<!-- navigation through calendar date picker -->
|
||||
<q-btn
|
||||
push rounded
|
||||
push
|
||||
rounded
|
||||
icon="calendar_month"
|
||||
color="accent"
|
||||
@click="is_showing_calendar_picker = true"
|
||||
|
|
@ -94,11 +99,29 @@
|
|||
>
|
||||
{{ $t('timesheet.nav_button.calendar_date_picker') }}
|
||||
</q-tooltip>
|
||||
|
||||
<!-- date picker calendar -->
|
||||
<q-menu
|
||||
anchor="bottom middle"
|
||||
self="top middle"
|
||||
:offset="[0, 10]"
|
||||
class="shadow-24"
|
||||
>
|
||||
<q-date
|
||||
v-model="calendar_date"
|
||||
color="primary"
|
||||
today-btn
|
||||
mask="YYYY-MM-DD"
|
||||
:options="date => date >= PAY_PERIOD_DATE_LIMIT"
|
||||
@update:model-value="onDateSelected"
|
||||
/>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
|
||||
<!-- navigation to next week -->
|
||||
<q-btn
|
||||
push rounded
|
||||
push
|
||||
rounded
|
||||
icon="keyboard_arrow_right"
|
||||
color="accent"
|
||||
@click="getNextPayPeriod"
|
||||
|
|
@ -113,21 +136,4 @@
|
|||
</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
|
||||
<!-- date picker calendar -->
|
||||
<q-dialog
|
||||
v-model="is_showing_calendar_picker"
|
||||
transition-show="jump-down"
|
||||
transition-hide="jump-up"
|
||||
position="top">
|
||||
<q-date
|
||||
v-model="calendar_date"
|
||||
color="primary"
|
||||
class="q-mt-xl"
|
||||
today-btn
|
||||
mask="YYYY-MM-DD"
|
||||
:options="date => date >= PAY_PERIOD_DATE_LIMIT"
|
||||
@update:model-value="onDateSelected"
|
||||
/>
|
||||
</q-dialog>
|
||||
</template>
|
||||
|
|
@ -2,17 +2,19 @@
|
|||
setup
|
||||
lang="ts"
|
||||
>
|
||||
import { inject, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useExpensesStore } from 'src/stores/expense-store';
|
||||
import { Expense, EXPENSE_TYPE, TYPES_WITH_AMOUNT_ONLY } from 'src/modules/timesheets/models/expense.models';
|
||||
import { useExpenseRules } from 'src/modules/timesheets/utils/expense.util';
|
||||
import { useExpensesApi } from 'src/modules/timesheets/composables/use-expense-api';
|
||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||
import { date } from 'quasar';
|
||||
import { computed, inject, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useUiStore } from 'src/stores/ui-store';
|
||||
import { useExpensesStore } from 'src/stores/expense-store';
|
||||
import { useTimesheetStore } from 'src/stores/timesheet-store';
|
||||
import { useExpensesApi } from 'src/modules/timesheets/composables/use-expense-api';
|
||||
import { useExpenseRules } from 'src/modules/timesheets/utils/expense.util';
|
||||
import { Expense, EXPENSE_TYPE, TYPES_WITH_AMOUNT_ONLY } from 'src/modules/timesheets/models/expense.models';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const ui_store = useUiStore();
|
||||
const timesheet_store = useTimesheetStore();
|
||||
const expenses_store = useExpensesStore();
|
||||
const expenses_api = useExpensesApi();
|
||||
|
|
@ -23,11 +25,16 @@
|
|||
const employee_email = inject<string>('employeeEmail');
|
||||
const rules = useExpenseRules(t);
|
||||
|
||||
const period_start_date = computed(() => timesheet_store.pay_period?.period_start.replaceAll('-', '/') ?? '');
|
||||
const period_end_date = computed(() => timesheet_store.pay_period?.period_end.replaceAll('-', '/') ?? '');
|
||||
|
||||
const openDatePicker = () => {
|
||||
is_navigator_open.value = true;
|
||||
if (expenses_store.current_expense.date === '') {
|
||||
expenses_store.current_expense.date = date.formatDate(new Date(), 'YYYY-MM-DD');
|
||||
if (timesheet_store.pay_period !== undefined) {
|
||||
expenses_store.current_expense.date = timesheet_store.pay_period.period_start;
|
||||
}
|
||||
console.log('current pay period start date: ', period_start_date.value);
|
||||
console.log('current pay period end date: ', period_end_date.value);
|
||||
};
|
||||
|
||||
const cancelUpdateMode = () => {
|
||||
|
|
@ -40,12 +47,6 @@
|
|||
if (expenses_store.mode === 'create') await expenses_api.createExpenseByEmployeeEmail(employee_email ?? '', expenses_store.current_expense?.date ?? '');
|
||||
else await expenses_api.updateExpenseByEmployeeEmail(employee_email ?? '', expenses_store.current_expense?.date ?? '');
|
||||
};
|
||||
|
||||
const getExpenseCalendarRange = (current_date: string) => {
|
||||
const period = timesheet_store.pay_period;
|
||||
if (period !== undefined) return current_date >= period.period_start && current_date <= period.period_end;
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -55,7 +56,10 @@
|
|||
flat
|
||||
@submit.prevent="requestExpenseCreationOrUpdate"
|
||||
>
|
||||
<div class="text-uppercase text-weight-medium q-pt-sm q-px-lg">
|
||||
<div
|
||||
class="text-uppercase text-weight-medium q-pt-sm q-px-lg q-ma-sm"
|
||||
:class="expenses_store.mode === 'create' ? '' : 'invisible'"
|
||||
>
|
||||
{{ $t('timesheet.expense.add_expense') }}
|
||||
</div>
|
||||
<div class="row justify-between items-start rounded-5 q-px-lg q-pb-sm">
|
||||
|
|
@ -66,8 +70,10 @@
|
|||
outlined
|
||||
readonly
|
||||
stack-label
|
||||
class="col q-px-xs"
|
||||
color="primary"
|
||||
class="col q-px-xs"
|
||||
input-class="text-weight-medium"
|
||||
input-style="font-size: 1.2em;"
|
||||
:label="$t('timesheet.expense.date')"
|
||||
>
|
||||
<template #prepend>
|
||||
|
|
@ -89,11 +95,17 @@
|
|||
v-model="expenses_store.current_expense.date"
|
||||
mask="YYYY-MM-DD"
|
||||
event-color="accent"
|
||||
:options="getExpenseCalendarRange"
|
||||
:options="date => date >= period_start_date && date <= period_end_date"
|
||||
@update:model-value="is_navigator_open = false"
|
||||
/>
|
||||
</q-dialog>
|
||||
</template>
|
||||
|
||||
<template #label>
|
||||
<span class="text-weight-bold text-accent text-uppercase text-caption">
|
||||
{{ $t('timesheet.expense.date') }}
|
||||
</span>
|
||||
</template>
|
||||
</q-input>
|
||||
|
||||
<!-- expenses type selection -->
|
||||
|
|
@ -103,8 +115,8 @@
|
|||
standout="bg-blue-grey-9"
|
||||
dense
|
||||
emit-value
|
||||
map-options
|
||||
hide-dropdown-icon
|
||||
label-slot
|
||||
class="col q-px-xs"
|
||||
color="primary"
|
||||
:label="$t('timesheet.expense.type')"
|
||||
|
|
@ -115,7 +127,26 @@
|
|||
popup-content-style="border: 2px solid var(--q-accent)"
|
||||
:rules="[rules.typeRequired]"
|
||||
:option-label="label => $t(`timesheet.expense.types.${label}`)"
|
||||
/>
|
||||
>
|
||||
<template #label>
|
||||
<span class="text-weight-bold text-accent text-uppercase text-caption">
|
||||
{{ $t('timesheet.expense.type') }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template #selected-item="scope">
|
||||
<div
|
||||
class="row flex-center text-weight-bold q-ma-none q-pa-none no-wrap ellipsis full-width"
|
||||
:class="ui_store.is_mobile_mode ? 'items-center full-height' : 'flex-center'"
|
||||
:tabindex="scope.tabindex"
|
||||
>
|
||||
<span
|
||||
style="line-height: 0.9em;"
|
||||
class="col-auto ellipsis"
|
||||
>{{ scope.opt.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</q-select>
|
||||
|
||||
<!-- amount input -->
|
||||
<div v-if="TYPES_WITH_AMOUNT_ONLY.includes(expenses_store.current_expense?.type ?? 'EXPENSES')">
|
||||
|
|
@ -207,6 +238,12 @@
|
|||
color="primary"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #label>
|
||||
<span class="text-weight-bold text-accent text-uppercase text-caption">
|
||||
{{ $t('timesheet.expense.hints.attach_file') }}
|
||||
</span>
|
||||
</template>
|
||||
</q-file>
|
||||
</div>
|
||||
<div class="col row full-width items-center">
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
import { useAuthStore } from 'src/stores/auth-store';
|
||||
import { CAN_APPROVE_PAY_PERIODS } from 'src/modules/shared/models/user.models';
|
||||
import { Expense } from 'src/modules/timesheets/models/expense.models';
|
||||
import ExpenseDialogForm from 'src/modules/timesheets/components/expense-dialog-form.vue';
|
||||
|
||||
const { expense, horizontal = false } = defineProps<{
|
||||
expense: Expense;
|
||||
|
|
@ -29,6 +30,7 @@
|
|||
const background_style = computed(() => deepEqual(expense, expenses_store.current_expense) ? 'border: 3px solid var(--q-accent);' : '');
|
||||
const approved_class = computed(() => expense.is_approved ? ' bg-accent text-white' : '')
|
||||
const is_authorized_to_approve = computed(() => CAN_APPROVE_PAY_PERIODS.includes(auth_store.user?.role ?? 'GUEST'))
|
||||
const is_showing_update_form = ref(false);
|
||||
|
||||
const requestExpenseDeletion = async () => {
|
||||
await expenses_api.deleteExpenseById(expense.id);
|
||||
|
|
@ -45,28 +47,27 @@
|
|||
if (deepEqual(expense, expenses_store.current_expense)) {
|
||||
expenses_store.mode = 'create';
|
||||
expenses_store.current_expense = new Expense(date.formatDate(new Date(), 'YYYY-MM-DD'));
|
||||
is_showing_update_form.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
expenses_store.mode = 'update';
|
||||
expenses_store.current_expense = expense;
|
||||
expenses_store.initial_expense = unwrapAndClone(expense);
|
||||
is_showing_update_form.value = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<transition
|
||||
enter-active-class="animated pulse"
|
||||
mode="out-in"
|
||||
<q-item
|
||||
:key="refresh_key"
|
||||
:clickable="horizontal"
|
||||
class="column col-4 items-center q-my-sm q-py-none shadow-3 rounded-5 bg-dark"
|
||||
:class="background_class + approved_class"
|
||||
:style="background_style"
|
||||
@click="onExpenseClicked"
|
||||
>
|
||||
<q-item
|
||||
:key="refresh_key"
|
||||
:clickable="horizontal"
|
||||
class="row col-4 items-center q-my-sm q-py-none shadow-3 rounded-5 bg-dark"
|
||||
:class="background_class + approved_class"
|
||||
:style="background_style"
|
||||
@click="onExpenseClicked"
|
||||
>
|
||||
<div class="row full-width items-center">
|
||||
<!-- avatar type icon section -->
|
||||
<q-item-section avatar>
|
||||
<q-icon
|
||||
|
|
@ -194,6 +195,13 @@
|
|||
@click.stop="requestExpenseDeletion"
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</transition>
|
||||
</div>
|
||||
|
||||
<q-slide-transition
|
||||
@hide="expenses_store.is_hiding_create_form = false"
|
||||
:duration="200"
|
||||
>
|
||||
<ExpenseDialogForm v-if="is_showing_update_form && expenses_store.is_hiding_create_form" />
|
||||
</q-slide-transition>
|
||||
</q-item>
|
||||
</template>
|
||||
|
|
@ -14,9 +14,10 @@
|
|||
|
||||
const expenses_list = computed(() => {
|
||||
if (timesheet_store.timesheets !== undefined) {
|
||||
return timesheet_store.timesheets.flatMap(week => week.days).flatMap(day => day.expenses);
|
||||
const current_expenses = timesheet_store.timesheets.flatMap(week => week.days).flatMap(day => day.expenses);
|
||||
console.log('current expenses: ', current_expenses);
|
||||
return current_expenses;
|
||||
}
|
||||
|
||||
return [];
|
||||
})
|
||||
</script>
|
||||
|
|
@ -29,10 +30,12 @@
|
|||
:class="horizontal ? 'row flex-center' : ''"
|
||||
>
|
||||
<q-item-label
|
||||
v-if="expenses_list.length > 0"
|
||||
class="text-italic q-px-sm"
|
||||
v-if="expenses_list.length < 1"
|
||||
class="text-italic text-center q-pa-sm rounded-4"
|
||||
>
|
||||
<q-separator spaced />
|
||||
{{ $t('timesheet.expense.empty_list') }}
|
||||
<q-separator spaced />
|
||||
</q-item-label>
|
||||
|
||||
<ExpenseDialogListItem
|
||||
|
|
|
|||
|
|
@ -38,20 +38,9 @@
|
|||
|
||||
<ExpenseDialogList />
|
||||
|
||||
<transition
|
||||
appear
|
||||
enter-active-class="animated fadeInDown faster"
|
||||
leave-active-class="animated fadeOutDown faster"
|
||||
mode="out-in"
|
||||
>
|
||||
<ExpenseDialogForm v-if="!expense_store.current_expense.is_approved" />
|
||||
<q-icon
|
||||
v-else
|
||||
name="block"
|
||||
color="negative"
|
||||
size="lg"
|
||||
/>
|
||||
</transition>
|
||||
<q-slide-transition @hide="expense_store.is_hiding_create_form = true" :duration="200">
|
||||
<ExpenseDialogForm v-if="!expense_store.current_expense.is_approved && expense_store.mode !== 'update' && expense_store.is_hiding_create_form === false" />
|
||||
</q-slide-transition>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
<script
|
||||
setup
|
||||
lang="ts"
|
||||
>
|
||||
import { type Shift, SHIFT_TYPES } from 'src/modules/timesheets/models/shift.models';
|
||||
|
||||
const shift = defineModel<Shift>({ required: true });
|
||||
|
||||
defineEmits<{
|
||||
'onCommentBlur': [void];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="row full-width justify-center">
|
||||
<div class="col-sm-6 col-md-3 row q-mx-xs q-my-none">
|
||||
<div class="col-auto column items-center">
|
||||
<span
|
||||
class="text-caption q-pa-none q-ma-none"
|
||||
style="line-height: 0.7em; font-size: 0.7em;"
|
||||
>{{ $t('timesheet.shift.types.REMOTE') }}</span>
|
||||
<q-toggle
|
||||
v-model="shift.is_remote"
|
||||
class="q-pa-none q-ma-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<q-select
|
||||
v-model="shift.type"
|
||||
options-dense
|
||||
:options="SHIFT_TYPES"
|
||||
:label="$t('timesheet.shift.types.label')"
|
||||
class="col q-pa-none"
|
||||
color="primary"
|
||||
outlined
|
||||
dense
|
||||
square
|
||||
hide-dropdown-icon
|
||||
emit-value
|
||||
map-options
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-auto row q-mx-xs">
|
||||
<q-input
|
||||
v-model="shift.start_time"
|
||||
:label="$t('timesheet.shift.fields.start')"
|
||||
outlined
|
||||
dense
|
||||
square
|
||||
inputmode="numeric"
|
||||
mask="##:##"
|
||||
class="col-auto q-mx-xs"
|
||||
/>
|
||||
|
||||
<q-input
|
||||
v-model="shift.end_time"
|
||||
:label="$t('timesheet.shift.fields.end')"
|
||||
outlined
|
||||
dense
|
||||
square
|
||||
inputmode="numeric"
|
||||
mask="##:##"
|
||||
class="col-auto q-mx-xs"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<q-input
|
||||
v-model="shift.comment"
|
||||
type="textarea"
|
||||
autogrow
|
||||
filled
|
||||
dense
|
||||
square
|
||||
:label="$t('timesheet.shift.fields.header_comment')"
|
||||
:counter="true"
|
||||
:maxlength="512"
|
||||
class="col-grow"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -85,7 +85,8 @@
|
|||
<template>
|
||||
<q-slide-item
|
||||
right-color="negative"
|
||||
class="q-my-xs rounded-5 bg-transparent"
|
||||
class="rounded-5 bg-transparent"
|
||||
:class="ui_store.is_mobile_mode ? 'q-my-md' : ''"
|
||||
@right="details => slideDeleteShift(details.reset)"
|
||||
>
|
||||
<template
|
||||
|
|
@ -94,172 +95,226 @@
|
|||
>
|
||||
<q-icon name="delete" />
|
||||
</template>
|
||||
<div :class="ui_store.is_mobile_mode ? 'column' : 'row'">
|
||||
<div class="row items-center text-uppercase rounded-5 bg-transparent q-mb-xs" :class="ui_store.is_mobile_mode ? 'col' : 'col-4'">
|
||||
<!-- mobile comment button -->
|
||||
<q-btn
|
||||
v-if="ui_store.is_mobile_mode && !dense"
|
||||
:icon="shift.comment ? 'chat' : 'chat_bubble_outline'"
|
||||
:text-color="shift.comment ? 'accent' : 'grey-5'"
|
||||
class="col-auto full-height q-mx-xs rounded-5 shadow-1"
|
||||
>
|
||||
<q-popup-edit
|
||||
v-model="shift.comment"
|
||||
:title="$t('timesheet.shift.fields.header_comment')"
|
||||
auto-save
|
||||
v-slot="scope"
|
||||
class="bg-dark"
|
||||
>
|
||||
<q-input
|
||||
color="white"
|
||||
v-model="scope.value"
|
||||
dense
|
||||
:readonly="shift.is_approved"
|
||||
autofocus
|
||||
counter
|
||||
bottom-slots
|
||||
:maxlength="COMMENT_LENGTH_MAX"
|
||||
class="q-pb-lg"
|
||||
:class="shift.is_approved ? 'cursor-not-allowed' : ''"
|
||||
@keyup.enter="scope.set"
|
||||
>
|
||||
<template #append>
|
||||
<q-icon name="edit" />
|
||||
</template>
|
||||
|
||||
<div
|
||||
class="row flex-center text-uppercase rounded-5 bg-transparent"
|
||||
>
|
||||
<!-- shift type -->
|
||||
<q-select
|
||||
ref="select"
|
||||
v-model="shift_type_selected"
|
||||
standout="bg-blue-grey-9"
|
||||
dense
|
||||
:readonly="shift.is_approved"
|
||||
:options-dense="!ui_store.is_mobile_mode"
|
||||
hide-dropdown-icon
|
||||
:menu-offset="[0, 10]"
|
||||
menu-anchor="bottom middle"
|
||||
menu-self="top middle"
|
||||
:options="SHIFT_OPTIONS"
|
||||
class="rounded-5 q-mx-xs bg-dark"
|
||||
:class="(ui_store.is_mobile_mode ? 'col-12 q-mb-xs ' : 'col ')"
|
||||
popup-content-class="text-uppercase text-weight-bold text-center rounded-5"
|
||||
popup-content-style="border: 2px solid var(--q-accent)"
|
||||
@blur="onBlurShiftTypeSelect"
|
||||
@update:model-value="option => shift.type = option.value"
|
||||
>
|
||||
<template #selected-item="scope">
|
||||
<div
|
||||
class="row flex-center text-weight-bold q-ma-none q-pa-none no-wrap ellipsis full-width"
|
||||
:class="ui_store.is_mobile_mode ? 'items-center full-height' : 'flex-center'"
|
||||
:tabindex="scope.tabindex"
|
||||
>
|
||||
<q-icon
|
||||
:name="scope.opt.icon"
|
||||
:color="scope.opt.icon_color"
|
||||
size="sm"
|
||||
class="col-auto q-mx-xs"
|
||||
/>
|
||||
<span
|
||||
style="line-height: 0.9em;"
|
||||
class="col-auto ellipsis"
|
||||
>{{ scope.opt.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</q-select>
|
||||
<template #counter>
|
||||
<div class="row flex-center">
|
||||
<q-space />
|
||||
<q-knob
|
||||
:model-value="scope.value?.length"
|
||||
readonly
|
||||
:max="COMMENT_LENGTH_MAX"
|
||||
size="1.6em"
|
||||
:thickness="0.4"
|
||||
:color="getCommentCounterColor(scope.value?.length ?? 0)"
|
||||
track-color="grey-4"
|
||||
class="col-auto q-mr-xs"
|
||||
/>
|
||||
<span
|
||||
:class="'col-auto text-weight-bolder text-' + getCommentCounterColor(scope.value?.length ?? 0)"
|
||||
>{{ 280 - (scope.value?.length ?? 0) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</q-input>
|
||||
</q-popup-edit>
|
||||
</q-btn>
|
||||
|
||||
<!-- punch in field -->
|
||||
<q-input
|
||||
v-model="shift.start_time"
|
||||
dense
|
||||
:readonly="shift.is_approved"
|
||||
type="time"
|
||||
:standout="$q.dark.isActive ? 'bg-blue-grey-9' : 'bg-blue-grey-1 text-white'"
|
||||
label-slot
|
||||
label-color="accent"
|
||||
:input-class="'text-weight-medium ' + (shift.id === -2 ? 'text-white ' : ' ') + (shift.is_approved ? 'cursor-not-allowed' : '')"
|
||||
input-style="font-size: 1.2em;"
|
||||
class="col rounded-5 bg-dark"
|
||||
:class="(shift.id === -2 ? 'bg-negative ' : ' ') + (ui_store.is_mobile_mode ? 'q-mr-xs ' : 'q-mx-xs ') + (shift.is_approved ? 'cursor-not-allowed' : '')"
|
||||
>
|
||||
<template #label>
|
||||
<span
|
||||
class="text-weight-bolder"
|
||||
style="font-size: 0.95em;"
|
||||
>{{ $t('shared.misc.in') }}</span>
|
||||
</template>
|
||||
</q-input>
|
||||
<!-- shift type -->
|
||||
<q-select
|
||||
ref="select"
|
||||
v-model="shift_type_selected"
|
||||
standout="bg-blue-grey-9"
|
||||
dense
|
||||
:readonly="shift.is_approved"
|
||||
:options-dense="!ui_store.is_mobile_mode"
|
||||
hide-dropdown-icon
|
||||
:menu-offset="[0, 10]"
|
||||
menu-anchor="bottom middle"
|
||||
menu-self="top middle"
|
||||
:options="SHIFT_OPTIONS"
|
||||
class="col rounded-5 q-mx-xs bg-dark"
|
||||
popup-content-class="text-uppercase text-weight-bold text-center rounded-5"
|
||||
popup-content-style="border: 2px solid var(--q-accent)"
|
||||
@blur="onBlurShiftTypeSelect"
|
||||
@update:model-value="option => shift.type = option.value"
|
||||
>
|
||||
<template #selected-item="scope">
|
||||
<div
|
||||
class="row flex-center text-weight-bold q-ma-none q-pa-none no-wrap ellipsis full-width"
|
||||
:class="ui_store.is_mobile_mode ? 'items-center full-height' : 'flex-center'"
|
||||
:tabindex="scope.tabindex"
|
||||
>
|
||||
<q-icon
|
||||
:name="scope.opt.icon"
|
||||
:color="scope.opt.icon_color"
|
||||
size="sm"
|
||||
class="col-auto q-mx-xs"
|
||||
/>
|
||||
<span
|
||||
style="line-height: 0.9em;"
|
||||
class="col-auto ellipsis"
|
||||
>{{ scope.opt.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
|
||||
<!-- punch out field -->
|
||||
<q-input
|
||||
v-model="shift.end_time"
|
||||
dense
|
||||
:readonly="shift.is_approved"
|
||||
type="time"
|
||||
standout="bg-blue-grey-9"
|
||||
label-slot
|
||||
label-color="accent"
|
||||
:input-class="'text-weight-medium ' + (shift.id === -2 ? 'text-white ' : ' ') + (shift.is_approved ? 'cursor-not-allowed' : '')"
|
||||
input-style="font-size: 1.2em;"
|
||||
class="col rounded-5 bg-dark"
|
||||
:class="(shift.id === -2 ? 'bg-negative ' : ' ') + (ui_store.is_mobile_mode ? 'q-ml-xs ' : 'q-mx-xs ') + (shift.is_approved ? 'cursor-not-allowed' : '')"
|
||||
>
|
||||
<template #label>
|
||||
<span
|
||||
class="text-weight-bolder"
|
||||
style="font-size: 0.95em;"
|
||||
>{{ $t('shared.misc.out') }}</span>
|
||||
</template>
|
||||
</q-input>
|
||||
<div class="col row flex-center text-uppercase rounded-5 bg-transparent q-pa-xs">
|
||||
<!-- punch in field -->
|
||||
<q-input
|
||||
v-model="shift.start_time"
|
||||
dense
|
||||
:readonly="shift.is_approved"
|
||||
type="time"
|
||||
:standout="$q.dark.isActive ? 'bg-blue-grey-9' : 'bg-blue-grey-1 text-white'"
|
||||
label-slot
|
||||
label-color="accent"
|
||||
:input-class="'text-weight-medium ' + (shift.id === -2 ? 'text-white ' : ' ') + (shift.is_approved ? 'cursor-not-allowed' : '')"
|
||||
input-style="font-size: 1.2em;"
|
||||
class="col rounded-5 bg-dark"
|
||||
:class="(shift.id === -2 ? 'bg-negative ' : ' ') + (ui_store.is_mobile_mode ? 'q-mr-xs ' : 'q-mx-xs ') + (shift.is_approved ? 'cursor-not-allowed' : '')"
|
||||
>
|
||||
<template #label>
|
||||
<span
|
||||
class="text-weight-bolder"
|
||||
style="font-size: 0.95em;"
|
||||
>{{ $t('shared.misc.in') }}</span>
|
||||
</template>
|
||||
</q-input>
|
||||
|
||||
<!-- comment and delete buttons -->
|
||||
<div :class="ui_store.is_mobile_mode ? 'col-12 row' : 'col-auto'">
|
||||
<q-icon
|
||||
v-if="shift.type && dense"
|
||||
:name="shift.comment ? 'comment' : ''"
|
||||
color="primary"
|
||||
:size="dense ? 'xs' : 'sm'"
|
||||
class="col"
|
||||
/>
|
||||
<!-- punch out field -->
|
||||
<q-input
|
||||
v-model="shift.end_time"
|
||||
dense
|
||||
:readonly="shift.is_approved"
|
||||
type="time"
|
||||
standout="bg-blue-grey-9"
|
||||
label-slot
|
||||
label-color="accent"
|
||||
:input-class="'text-weight-medium ' + (shift.id === -2 ? 'text-white ' : ' ') + (shift.is_approved ? 'cursor-not-allowed' : '')"
|
||||
input-style="font-size: 1.2em;"
|
||||
class="col rounded-5 bg-dark"
|
||||
:class="(shift.id === -2 ? 'bg-negative ' : ' ') + (ui_store.is_mobile_mode ? 'q-ml-xs ' : 'q-mx-xs ') + (shift.is_approved ? 'cursor-not-allowed' : '')"
|
||||
>
|
||||
<template #label>
|
||||
<span
|
||||
class="text-weight-bolder"
|
||||
style="font-size: 0.95em;"
|
||||
>{{ $t('shared.misc.out') }}</span>
|
||||
</template>
|
||||
</q-input>
|
||||
|
||||
<q-btn
|
||||
v-else
|
||||
flat
|
||||
dense
|
||||
:icon="shift.comment ? 'chat' : 'chat_bubble_outline'"
|
||||
:text-color="shift.comment ? 'accent' : 'grey-5'"
|
||||
class="col"
|
||||
:class="ui_store.is_mobile_mode ? 'q-mt-xs bg-dark' : ''"
|
||||
>
|
||||
<q-popup-edit
|
||||
v-model="shift.comment"
|
||||
:title="$t('timesheet.shift.fields.header_comment')"
|
||||
auto-save
|
||||
v-slot="scope"
|
||||
class="bg-dark"
|
||||
>
|
||||
<q-input
|
||||
color="white"
|
||||
v-model="scope.value"
|
||||
dense
|
||||
:readonly="shift.is_approved"
|
||||
autofocus
|
||||
counter
|
||||
bottom-slots
|
||||
:maxlength="COMMENT_LENGTH_MAX"
|
||||
class="q-pb-lg"
|
||||
:class="shift.is_approved ? 'cursor-not-allowed' : ''"
|
||||
@keyup.enter="scope.set"
|
||||
>
|
||||
<template #append>
|
||||
<q-icon name="edit" />
|
||||
</template>
|
||||
<!-- comment and delete buttons -->
|
||||
<div :class="ui_store.is_mobile_mode ? 'col-12 row' : 'col-auto'">
|
||||
<q-icon
|
||||
v-if="shift.type && dense"
|
||||
:name="shift.comment ? 'comment' : ''"
|
||||
color="primary"
|
||||
:size="dense ? 'xs' : 'sm'"
|
||||
class="col"
|
||||
/>
|
||||
|
||||
<template #counter>
|
||||
<div class="row flex-center">
|
||||
<q-space />
|
||||
<q-knob
|
||||
:model-value="scope.value?.length"
|
||||
readonly
|
||||
:max="COMMENT_LENGTH_MAX"
|
||||
size="1.6em"
|
||||
:thickness="0.4"
|
||||
:color="getCommentCounterColor(scope.value?.length ?? 0)"
|
||||
track-color="grey-4"
|
||||
class="col-auto q-mr-xs"
|
||||
/>
|
||||
<span
|
||||
:class="'col-auto text-weight-bolder text-' + getCommentCounterColor(scope.value?.length ?? 0)"
|
||||
>{{ 280 - (scope.value?.length ?? 0) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</q-input>
|
||||
</q-popup-edit>
|
||||
</q-btn>
|
||||
<!-- desktop comment button -->
|
||||
<q-btn
|
||||
v-else-if="!ui_store.is_mobile_mode"
|
||||
flat
|
||||
dense
|
||||
:icon="shift.comment ? 'chat' : 'chat_bubble_outline'"
|
||||
:text-color="shift.comment ? 'accent' : 'grey-5'"
|
||||
class="col"
|
||||
:class="ui_store.is_mobile_mode ? 'q-mt-xs bg-dark' : ''"
|
||||
>
|
||||
<q-popup-edit
|
||||
v-model="shift.comment"
|
||||
:title="$t('timesheet.shift.fields.header_comment')"
|
||||
auto-save
|
||||
v-slot="scope"
|
||||
class="bg-dark"
|
||||
>
|
||||
<q-input
|
||||
color="white"
|
||||
v-model="scope.value"
|
||||
dense
|
||||
:readonly="shift.is_approved"
|
||||
autofocus
|
||||
counter
|
||||
bottom-slots
|
||||
:maxlength="COMMENT_LENGTH_MAX"
|
||||
class="q-pb-lg"
|
||||
:class="shift.is_approved ? 'cursor-not-allowed' : ''"
|
||||
@keyup.enter="scope.set"
|
||||
>
|
||||
<template #append>
|
||||
<q-icon name="edit" />
|
||||
</template>
|
||||
|
||||
<q-btn
|
||||
v-if="!ui_store.is_mobile_mode"
|
||||
flat
|
||||
dense
|
||||
:disable="shift.is_approved"
|
||||
tabindex="-1"
|
||||
icon="cancel"
|
||||
text-color="negative"
|
||||
class="col"
|
||||
:class="shift.is_approved ? 'invisible' : ''"
|
||||
@click="$emit('requestDelete')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<template #counter>
|
||||
<div class="row flex-center">
|
||||
<q-space />
|
||||
<q-knob
|
||||
:model-value="scope.value?.length"
|
||||
readonly
|
||||
:max="COMMENT_LENGTH_MAX"
|
||||
size="1.6em"
|
||||
:thickness="0.4"
|
||||
:color="getCommentCounterColor(scope.value?.length ?? 0)"
|
||||
track-color="grey-4"
|
||||
class="col-auto q-mr-xs"
|
||||
/>
|
||||
<span
|
||||
:class="'col-auto text-weight-bolder text-' + getCommentCounterColor(scope.value?.length ?? 0)"
|
||||
>{{ 280 - (scope.value?.length ?? 0) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</q-input>
|
||||
</q-popup-edit>
|
||||
</q-btn>
|
||||
|
||||
<q-btn
|
||||
v-if="!ui_store.is_mobile_mode"
|
||||
flat
|
||||
dense
|
||||
:disable="shift.is_approved"
|
||||
tabindex="-1"
|
||||
icon="cancel"
|
||||
text-color="negative"
|
||||
class="col"
|
||||
:class="shift.is_approved ? 'invisible' : ''"
|
||||
@click="$emit('requestDelete')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-slide-item>
|
||||
</template>
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="column justify-center q-py-xs" :class="approved ? 'bg-dark' : ''">
|
||||
<div class="column justify-center q-py-xs" :class="approved ? '' : ''">
|
||||
<ShiftListDayRow
|
||||
v-for="shift, shift_index in day.shifts"
|
||||
:key="shift_index"
|
||||
|
|
|
|||
|
|
@ -57,17 +57,20 @@
|
|||
v-if="ui_store.is_mobile_mode"
|
||||
class="col column full-width"
|
||||
>
|
||||
<q-card class="rounded-5 q-my-md" :class="getDayApproval(day) ? 'bg-accent' : 'bg-dark'">
|
||||
<q-card
|
||||
class="rounded-10 bg-dark"
|
||||
:style="ui_store.is_mobile_mode ? (getDayApproval(day) ? 'border: 3px solid var(--q-accent)' : 'border: 1px solid var(--q-accent);') : ''"
|
||||
>
|
||||
|
||||
<q-card-section
|
||||
class="text-white text-weight-bolder text-uppercase text-h6 q-py-xs"
|
||||
:class="getDayApproval(day) ? 'bg-dark' : 'bg-accent'"
|
||||
style="line-height: 1em;"
|
||||
class="text-weight-bolder text-uppercase text-h6 q-py-xs"
|
||||
:class="getDayApproval(day) ? 'bg-dark text-accent' : 'bg-primary text-white'"
|
||||
style="line-height: 1em;"
|
||||
>
|
||||
<span> {{ $d(extractDate(day.date, 'YYYY-MM-DD'), {
|
||||
weekday: 'long', day: 'numeric', month:
|
||||
'long'
|
||||
}) }}</span>
|
||||
<span> {{ $d(extractDate(day.date, 'YYYY-MM-DD'), {
|
||||
weekday: 'long', day: 'numeric', month:
|
||||
'long'
|
||||
}) }}</span>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section
|
||||
|
|
@ -85,7 +88,6 @@
|
|||
<q-card-actions class="q-pa-none">
|
||||
<q-btn
|
||||
v-if="!getDayApproval(day)"
|
||||
push
|
||||
square
|
||||
color="accent"
|
||||
icon="more_time"
|
||||
|
|
@ -98,7 +100,7 @@
|
|||
<q-badge
|
||||
v-if="getDayApproval(day)"
|
||||
floating
|
||||
class="bg-secondary q-pa-none rounded-50"
|
||||
class="transparent q-pa-none rounded-50"
|
||||
style="transform: translate(15px, -5px);"
|
||||
>
|
||||
<q-icon
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@
|
|||
v-if="$q.screen.lt.md"
|
||||
push
|
||||
rounded
|
||||
color="primary"
|
||||
color="accent"
|
||||
icon="receipt_long"
|
||||
:label="$t('timesheet.expense.open_btn')"
|
||||
class="q-mt-sm"
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ export const ShiftService = {
|
|||
updateShifts: async (existing_shifts: Shift[]) => {
|
||||
console.log('sent shifts: ', existing_shifts)
|
||||
const response = await api.patch(`/shift/update`, existing_shifts);
|
||||
console.log('API response to existing shifts: ', response.data);
|
||||
return response;
|
||||
}
|
||||
};
|
||||
|
|
@ -20,7 +20,7 @@ export const timesheetService = {
|
|||
},
|
||||
|
||||
getTimesheetsByPayPeriod: async (year: number, period_number: number): Promise<TimesheetResponse> => {
|
||||
const response = await api.get('timesheets', { params: { year, period_number } });
|
||||
return response.data;
|
||||
const response = await api.get(`timesheets/${year}/${period_number}`);
|
||||
return response.data.data;
|
||||
},
|
||||
};
|
||||
|
|
@ -1,22 +1,25 @@
|
|||
import { ref } from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
import { useTimesheetStore } from "src/stores/timesheet-store";
|
||||
import { Expense } from "src/modules/timesheets/models/expense.models";
|
||||
import { ExpenseService } from "src/modules/timesheets/services/expense-service";
|
||||
import { date } from "quasar";
|
||||
|
||||
|
||||
|
||||
export const useExpensesStore = defineStore('expenses', () => {
|
||||
const timesheet_store = useTimesheetStore();
|
||||
const is_open = ref(false);
|
||||
const is_loading = ref(false);
|
||||
const is_hiding_create_form = ref(false);
|
||||
const mode = ref<'create' | 'update' | 'delete'>('create');
|
||||
const current_expense = ref<Expense>(new Expense(date.formatDate(new Date(), 'YYYY-MM-DD')));
|
||||
const initial_expense = ref<Expense>(new Expense(date.formatDate(new Date(), 'YYYY-MM-DD')));
|
||||
|
||||
const open = (): void => {
|
||||
is_open.value = true;
|
||||
current_expense.value = new Expense(date.formatDate(new Date(), 'YYYY-MM-DD'));
|
||||
initial_expense.value = new Expense(date.formatDate(new Date(), 'YYYY-MM-DD'));
|
||||
if (timesheet_store.pay_period !== undefined) {
|
||||
current_expense.value = new Expense(timesheet_store.pay_period.period_start);
|
||||
initial_expense.value = new Expense(timesheet_store.pay_period.period_start);
|
||||
}
|
||||
mode.value = 'create';
|
||||
}
|
||||
|
||||
|
|
@ -45,6 +48,7 @@ export const useExpensesStore = defineStore('expenses', () => {
|
|||
return {
|
||||
is_open,
|
||||
is_loading,
|
||||
is_hiding_create_form,
|
||||
mode,
|
||||
current_expense,
|
||||
initial_expense,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user