Merge pull request 'feat(timesheet-approval): allow creation of expenses from employee details window' (#83) from release/nicolas/v1.1 into main
Reviewed-on: Targo/targo_frontend#83
This commit is contained in:
commit
d5e26d632b
|
|
@ -16,7 +16,8 @@ declare module 'vue' {
|
||||||
// for each client)
|
// for each client)
|
||||||
const api = axios.create({
|
const api = axios.create({
|
||||||
baseURL: import.meta.env.VITE_TARGO_BACKEND_URL,
|
baseURL: import.meta.env.VITE_TARGO_BACKEND_URL,
|
||||||
withCredentials: true
|
withCredentials: true,
|
||||||
|
timeout: 5 * 60 * 1000,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default defineBoot(({ app }) => {
|
export default defineBoot(({ app }) => {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ export default {
|
||||||
error: {
|
error: {
|
||||||
NO_REPLY_RECEIVED: "encountered an error while waiting for chatbot to reply",
|
NO_REPLY_RECEIVED: "encountered an error while waiting for chatbot to reply",
|
||||||
SEND_MESSAGE_FAILED: "unable to send message to chatbot",
|
SEND_MESSAGE_FAILED: "unable to send message to chatbot",
|
||||||
|
GENERIC_RESPONSE_ERROR: "An error was encountered while waiting for the chatbot to reply, please try again.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ export default {
|
||||||
chat_thinking: "Réflexion en cours...",
|
chat_thinking: "Réflexion en cours...",
|
||||||
error: {
|
error: {
|
||||||
NO_REPLY_RECEIVED: "Une erreur est survenu lors de la réception de la réponse du chatbot",
|
NO_REPLY_RECEIVED: "Une erreur est survenu lors de la réception de la réponse du chatbot",
|
||||||
SEND_MESSAGE_FAILED: "Une erreur est survenu lors de l'envoi de votre message",
|
SEND_MESSAGE_FAILED: "Une erreur est survenue lors de l'envoi de votre message",
|
||||||
|
GENERIC_RESPONSE_ERROR: "Le chatbot ne réponds pas pour le moment, veuillez réessayer.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,15 @@
|
||||||
// const is_remembered = ref<boolean>(false);
|
// const is_remembered = ref<boolean>(false);
|
||||||
const is_employee_email = computed(() => email.value.includes('@targ'));
|
const is_employee_email = computed(() => email.value.includes('@targ'));
|
||||||
const is_game_time = computed(() => email.value.includes('allumette'));
|
const is_game_time = computed(() => email.value.includes('allumette'));
|
||||||
|
|
||||||
|
const onSubmitConnectionRequest = () => {
|
||||||
|
console.log('submit requested');
|
||||||
|
if (is_employee_email.value) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClickEmployeeConnect = () => {
|
||||||
|
auth_api.oidcLogin();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -27,28 +36,31 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="q-pt-sm q-px-xl q-pb-lg ">
|
<div class="q-px-xl q-pb-lg ">
|
||||||
<q-card-section class="text-center text-uppercase">
|
<div class="text-h6 text-weight-bold text-center text-uppercase q-py-sm">
|
||||||
<div class="text-h6 text-weight-bold">
|
{{ $t('login.page_header') }}
|
||||||
{{ $t('login.page_header') }}
|
</div>
|
||||||
</div>
|
|
||||||
</q-card-section>
|
|
||||||
|
|
||||||
<q-form @submit="auth_api.login">
|
<q-form
|
||||||
|
@submit="onSubmitConnectionRequest"
|
||||||
|
autocomplete="on"
|
||||||
|
>
|
||||||
<q-input
|
<q-input
|
||||||
v-model="email"
|
v-model="email"
|
||||||
dense
|
dense
|
||||||
outlined
|
outlined
|
||||||
|
label-slot
|
||||||
|
name="email"
|
||||||
color="accent"
|
color="accent"
|
||||||
label-color="accent"
|
label-color="accent"
|
||||||
class="rounded-5 inset-shadow bg-white"
|
class="rounded-5 inset-shadow bg-white"
|
||||||
label-slot
|
|
||||||
input-class="text-h6 text-primary"
|
input-class="text-h6 text-primary"
|
||||||
>
|
>
|
||||||
<template #label>
|
<template #label>
|
||||||
<span class="text-weight-bolder text-uppercase text-overline"> {{ $t('login.email') }} </span>
|
<span class="text-weight-bolder text-uppercase text-overline"> {{ $t('login.email') }} </span>
|
||||||
</template>
|
</template>
|
||||||
</q-input>
|
</q-input>
|
||||||
|
|
||||||
<!-- Stay-logged-in section, removed temporarly until customer module is up -->
|
<!-- Stay-logged-in section, removed temporarly until customer module is up -->
|
||||||
<!-- <q-card-section
|
<!-- <q-card-section
|
||||||
horizontal
|
horizontal
|
||||||
|
|
@ -86,58 +98,59 @@
|
||||||
<!-- <q-card-section class="text-center q-pa-none q-mt-none">
|
<!-- <q-card-section class="text-center q-pa-none q-mt-none">
|
||||||
<RouterLink disabled class="text-primary" to="/signup">{{ $t('loginPage.signUp') }}</RouterLink>
|
<RouterLink disabled class="text-primary" to="/signup">{{ $t('loginPage.signUp') }}</RouterLink>
|
||||||
</q-card-section> -->
|
</q-card-section> -->
|
||||||
|
|
||||||
|
<q-card-section class="row q-pt-sm">
|
||||||
|
<q-separator
|
||||||
|
size="2px"
|
||||||
|
color="accent"
|
||||||
|
class="col self-center"
|
||||||
|
/>
|
||||||
|
<span class="col text-accent text-weight-bolder text-center text-uppercase self-center">{{
|
||||||
|
$t('shared.misc.or') }}</span>
|
||||||
|
<q-separator
|
||||||
|
size="2px"
|
||||||
|
color="accent"
|
||||||
|
class="col self-center"
|
||||||
|
/>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-card-section class="column q-px-sm q-pt-none">
|
||||||
|
<q-btn
|
||||||
|
rounded
|
||||||
|
push
|
||||||
|
disabled
|
||||||
|
color="blue-grey-7"
|
||||||
|
icon="img:src/assets/Facebook-f_Logo-White-Logo.wine.svg"
|
||||||
|
:label="$t('login.button.facebook')"
|
||||||
|
class="full-width row q-mb-sm"
|
||||||
|
>
|
||||||
|
<q-tooltip
|
||||||
|
anchor="top middle"
|
||||||
|
class="bg-primary"
|
||||||
|
>{{ $t('login.tooltip.coming_soon') }}</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
<q-slide-transition>
|
||||||
|
<div v-if="is_employee_email">
|
||||||
|
<transition
|
||||||
|
slow
|
||||||
|
enter-active-class="animated zoomIn"
|
||||||
|
leave-active-class="animated zoomOut"
|
||||||
|
>
|
||||||
|
<q-btn
|
||||||
|
push
|
||||||
|
rounded
|
||||||
|
type="submit"
|
||||||
|
color="accent"
|
||||||
|
icon="img:src/assets/logo-targo-simple.svg"
|
||||||
|
:label="$t('login.button.employee')"
|
||||||
|
class="full-width row"
|
||||||
|
@click="onClickEmployeeConnect"
|
||||||
|
/>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</q-slide-transition>
|
||||||
|
</q-card-section>
|
||||||
</q-form>
|
</q-form>
|
||||||
|
|
||||||
<q-card-section class="row q-pt-sm">
|
|
||||||
<q-separator
|
|
||||||
size="2px"
|
|
||||||
color="accent"
|
|
||||||
class="col self-center"
|
|
||||||
/>
|
|
||||||
<span class="col text-accent text-weight-bolder text-center text-uppercase self-center">{{
|
|
||||||
$t('shared.misc.or') }}</span>
|
|
||||||
<q-separator
|
|
||||||
size="2px"
|
|
||||||
color="accent"
|
|
||||||
class="col self-center"
|
|
||||||
/>
|
|
||||||
</q-card-section>
|
|
||||||
|
|
||||||
<q-card-section class="column q-px-sm q-pt-none">
|
|
||||||
<q-btn
|
|
||||||
rounded
|
|
||||||
push
|
|
||||||
disabled
|
|
||||||
color="blue-grey-7"
|
|
||||||
icon="img:src/assets/Facebook-f_Logo-White-Logo.wine.svg"
|
|
||||||
:label="$t('login.button.facebook')"
|
|
||||||
class="full-width row q-mb-sm"
|
|
||||||
>
|
|
||||||
<q-tooltip
|
|
||||||
anchor="top middle"
|
|
||||||
class="bg-primary"
|
|
||||||
>{{ $t('login.tooltip.coming_soon') }}</q-tooltip>
|
|
||||||
</q-btn>
|
|
||||||
<q-slide-transition>
|
|
||||||
<div v-if="is_employee_email">
|
|
||||||
<transition
|
|
||||||
slow
|
|
||||||
enter-active-class="animated zoomIn"
|
|
||||||
leave-active-class="animated zoomOut"
|
|
||||||
>
|
|
||||||
<q-btn
|
|
||||||
push
|
|
||||||
rounded
|
|
||||||
color="accent"
|
|
||||||
icon="img:src/assets/logo-targo-simple.svg"
|
|
||||||
:label="$t('login.button.employee')"
|
|
||||||
class="full-width row"
|
|
||||||
@click="auth_api.oidcLogin"
|
|
||||||
/>
|
|
||||||
</transition>
|
|
||||||
</div>
|
|
||||||
</q-slide-transition>
|
|
||||||
</q-card-section>
|
|
||||||
</div>
|
</div>
|
||||||
</q-card>
|
</q-card>
|
||||||
<div v-if="is_game_time">
|
<div v-if="is_game_time">
|
||||||
|
|
|
||||||
|
|
@ -2,49 +2,33 @@
|
||||||
setup
|
setup
|
||||||
lang="ts"
|
lang="ts"
|
||||||
>
|
>
|
||||||
|
import MarkdownIt from 'markdown-it';
|
||||||
|
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
// import MarkdownIt from 'markdown-it'
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useAuthStore } from 'src/stores/auth-store';
|
import { useAuthStore } from 'src/stores/auth-store';
|
||||||
import type { Message } from 'src/modules/chatbot/models/dialogue-message.model';
|
import type { Message } from 'src/modules/chatbot/models/dialogue-message.model';
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
// ========== state ========================================
|
||||||
const auth_store = useAuthStore();
|
|
||||||
|
|
||||||
const { message } = defineProps<{
|
const { message } = defineProps<{
|
||||||
message: Message;
|
message: Message;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const message_text = computed(() => message.text.includes('chatbot.') ? t(message.text) : message.text)
|
const { t } = useI18n();
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
const md = new MarkdownIt({
|
||||||
|
linkify: true,
|
||||||
|
typographer: true,
|
||||||
|
})
|
||||||
|
|
||||||
const is_error = computed(() => message.text.includes('NO_REPLY_RECEIVED') || message.text.includes('SEND_MESSAGE_FAILED'));
|
// ========== computed ========================================
|
||||||
|
|
||||||
// Initialize Markdown parser
|
const message_text = computed(() => message.text.includes('chatbot.') ?
|
||||||
// const md = new MarkdownIt({
|
t(message.text) :
|
||||||
// // breaks: false, // Support line breaks
|
message.text.replaceAll("\\n", "\n")
|
||||||
// linkify: true, // Make URLs clickable
|
);
|
||||||
// html: false // Prevent raw HTML injection
|
const is_error = computed(() => message.text.includes('chatbot.error.'));
|
||||||
// })
|
|
||||||
|
|
||||||
// // Removes all the h1,h2,h3 to make the Md more user friendly
|
|
||||||
// const cleanMarkdown = (markdown: string): string => {
|
|
||||||
// if (!markdown) return ''
|
|
||||||
// return markdown
|
|
||||||
// .replace(/\r\n/g, '\n') // normalize Windows line endings
|
|
||||||
// .replace(/([.!?])(\s+)(?=[A-Z])/g, '$1\n') // insert line break after sentence-ending punctuation
|
|
||||||
// .replace(/\n{3,}/g, '\n\n') // squash triple+ line breaks
|
|
||||||
// .replace(/^#{1,3}\s(.*)$/gm, '#### $1') // downgrade headings
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Compute parsed content
|
|
||||||
// const parsedText = computed((): string => {
|
|
||||||
// const cleaned = cleanMarkdown(message.text || '')
|
|
||||||
// if (cleaned.includes('chatbot.')) {
|
|
||||||
// const translated_message = t(message.text);
|
|
||||||
// return md.render(translated_message);
|
|
||||||
// }
|
|
||||||
// return md.render(cleaned);
|
|
||||||
// })
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -67,11 +51,12 @@
|
||||||
|
|
||||||
<q-chat-message
|
<q-chat-message
|
||||||
v-else
|
v-else
|
||||||
|
text-html
|
||||||
:sent="message.sent"
|
:sent="message.sent"
|
||||||
:name="message.sent ? auth_store.user?.first_name : 'TargoBot'"
|
:name="message.sent ? authStore.user?.first_name : 'TargoBot'"
|
||||||
:bg-color="message.sent ? 'accent' : 'info'"
|
:bg-color="message.sent ? 'accent' : 'info'"
|
||||||
:text-color="message.sent ? 'white' : ''"
|
:text-color="message.sent ? 'white' : ''"
|
||||||
:text="[message_text]"
|
:text="[md.renderInline(message_text)]"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,18 @@
|
||||||
import DetailsDialogChartExpenses from 'src/modules/timesheet-approval/components/details-dialog-chart-expenses.vue';
|
import DetailsDialogChartExpenses from 'src/modules/timesheet-approval/components/details-dialog-chart-expenses.vue';
|
||||||
import TimesheetWrapper from 'src/modules/timesheets/components/timesheet-wrapper.vue';
|
import TimesheetWrapper from 'src/modules/timesheets/components/timesheet-wrapper.vue';
|
||||||
import ExpenseDialogList from 'src/modules/timesheets/components/expense-dialog-list.vue';
|
import ExpenseDialogList from 'src/modules/timesheets/components/expense-dialog-list.vue';
|
||||||
|
import ExpenseDialogForm from 'src/modules/timesheets/components/expense-dialog-form.vue';
|
||||||
import { useTimesheetApprovalApi } from '../composables/use-timesheet-approval-api';
|
import { useTimesheetApprovalApi } from '../composables/use-timesheet-approval-api';
|
||||||
import { useShiftApi } from 'src/modules/timesheets/composables/use-shift-api';
|
import { useShiftApi } from 'src/modules/timesheets/composables/use-shift-api';
|
||||||
|
import { useExpensesStore } from 'src/stores/expense-store';
|
||||||
|
import { Expense } from 'src/modules/timesheets/models/expense.models';
|
||||||
|
import { date } from 'quasar';
|
||||||
|
|
||||||
// ========== state ========================================
|
// ========== state ========================================
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const timesheetStore = useTimesheetStore();
|
const timesheetStore = useTimesheetStore();
|
||||||
|
const expenseStore = useExpensesStore();
|
||||||
const timesheetApprovalApi = useTimesheetApprovalApi();
|
const timesheetApprovalApi = useTimesheetApprovalApi();
|
||||||
const shiftApi = useShiftApi();
|
const shiftApi = useShiftApi();
|
||||||
const isDialogOpen = ref(false);
|
const isDialogOpen = ref(false);
|
||||||
|
|
@ -24,11 +29,14 @@
|
||||||
// ========== computed ========================================
|
// ========== computed ========================================
|
||||||
|
|
||||||
const isApproved = computed(() => timesheetStore.timesheets.every(timesheet => timesheet.is_approved));
|
const isApproved = computed(() => timesheetStore.timesheets.every(timesheet => timesheet.is_approved));
|
||||||
|
|
||||||
const approveButtonLabel = computed(() => isApproved.value ?
|
const approveButtonLabel = computed(() => isApproved.value ?
|
||||||
t('shared.label.unlock') :
|
t('shared.label.unlock') :
|
||||||
t('shared.label.lock')
|
t('shared.label.lock')
|
||||||
);
|
);
|
||||||
|
|
||||||
const approveButtonIcon = computed(() => isApproved.value ? 'las la-lock' : 'las la-unlock');
|
const approveButtonIcon = computed(() => isApproved.value ? 'las la-lock' : 'las la-unlock');
|
||||||
|
|
||||||
const hasExpenses = computed(() => timesheetStore.timesheets.some(timesheet =>
|
const hasExpenses = computed(() => timesheetStore.timesheets.some(timesheet =>
|
||||||
Object.values(timesheet.weekly_expenses).some(hours => hours > 0))
|
Object.values(timesheet.weekly_expenses).some(hours => hours > 0))
|
||||||
);
|
);
|
||||||
|
|
@ -49,6 +57,15 @@
|
||||||
|
|
||||||
const onClickSaveTimesheets = async () => {
|
const onClickSaveTimesheets = async () => {
|
||||||
await shiftApi.saveShiftChanges(timesheetStore.current_pay_period_overview?.email);
|
await shiftApi.saveShiftChanges(timesheetStore.current_pay_period_overview?.email);
|
||||||
|
expenseStore.is_showing_create_form = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const onClickExpenseCreate = () => {
|
||||||
|
expenseStore.mode = 'create';
|
||||||
|
if (timesheetStore.pay_period)
|
||||||
|
expenseStore.current_expense = new Expense(timesheetStore.pay_period.period_start);
|
||||||
|
else
|
||||||
|
expenseStore.current_expense = new Expense(date.formatDate(new Date(), 'YYYY-MM-DD'));
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -118,6 +135,7 @@
|
||||||
<!-- employee pay period details using chart -->
|
<!-- employee pay period details using chart -->
|
||||||
<div
|
<div
|
||||||
v-if="isDialogOpen"
|
v-if="isDialogOpen"
|
||||||
|
:key="hasExpenses ? 0 : 1"
|
||||||
class="col-auto q-px-md no-wrap"
|
class="col-auto q-px-md no-wrap"
|
||||||
:class="$q.platform.is.mobile ? 'column' : 'row'"
|
:class="$q.platform.is.mobile ? 'column' : 'row'"
|
||||||
>
|
>
|
||||||
|
|
@ -129,12 +147,51 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-auto">
|
<div class="col-auto column">
|
||||||
|
<q-separator
|
||||||
|
spaced
|
||||||
|
size="4px"
|
||||||
|
class="q-mx-md"
|
||||||
|
/>
|
||||||
|
|
||||||
<ExpenseDialogList mode="approval" />
|
<ExpenseDialogList mode="approval" />
|
||||||
|
|
||||||
|
<q-expansion-item
|
||||||
|
v-if="!isApproved"
|
||||||
|
v-model="expenseStore.is_showing_create_form"
|
||||||
|
hide-expand-icon
|
||||||
|
:dense="!$q.platform.is.mobile"
|
||||||
|
group="expenses"
|
||||||
|
@show="onClickExpenseCreate()"
|
||||||
|
header-class="bg-accent text-white q-mx-md rounded-5"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div class="row items-center">
|
||||||
|
<q-icon
|
||||||
|
name="add_circle_outline"
|
||||||
|
size="md"
|
||||||
|
class="col-auto"
|
||||||
|
:class="expenseStore.is_showing_create_form ? 'invisible' : ''"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span class="col-auto text-uppercase text-weight-bold text-h6 q-ml-xs q-mr-sm">
|
||||||
|
{{ $t('timesheet.expense.add_expense') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<ExpenseDialogForm :email="timesheetStore.current_pay_period_overview?.email"/>
|
||||||
|
</q-expansion-item>
|
||||||
|
|
||||||
|
<q-separator
|
||||||
|
spaced
|
||||||
|
size="4px"
|
||||||
|
class="q-mx-md"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- list of shifts -->
|
<!-- list of shifts -->
|
||||||
<div class="col-auto">
|
<div class="col-auto q-mt-md">
|
||||||
<TimesheetWrapper
|
<TimesheetWrapper
|
||||||
mode="approval"
|
mode="approval"
|
||||||
:employee-email="timesheetStore.current_pay_period_overview?.email"
|
:employee-email="timesheetStore.current_pay_period_overview?.email"
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,10 @@
|
||||||
const expense = defineModel<Expense>({ default: new Expense(new Date().toISOString().slice(0, 10)) })
|
const expense = defineModel<Expense>({ default: new Expense(new Date().toISOString().slice(0, 10)) })
|
||||||
const file = defineModel<File>('file');
|
const file = defineModel<File>('file');
|
||||||
|
|
||||||
|
const {email} = defineProps<{
|
||||||
|
email?: string | undefined;
|
||||||
|
}>();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const ui_store = useUiStore();
|
const ui_store = useUiStore();
|
||||||
const timesheet_store = useTimesheetStore();
|
const timesheet_store = useTimesheetStore();
|
||||||
|
|
@ -60,9 +64,16 @@
|
||||||
|
|
||||||
const requestExpenseCreationOrUpdate = async () => {
|
const requestExpenseCreationOrUpdate = async () => {
|
||||||
if (file.value)
|
if (file.value)
|
||||||
await expenses_api.upsertExpense(expenses_store.current_expense, employeeEmail ?? auth_store.user?.email ?? 'MISSING_EMAIL', file.value);
|
await expenses_api.upsertExpense(
|
||||||
|
expenses_store.current_expense,
|
||||||
|
email ?? employeeEmail ?? auth_store.user?.email ?? 'MISSING_EMAIL',
|
||||||
|
file.value
|
||||||
|
);
|
||||||
else
|
else
|
||||||
await expenses_api.upsertExpense(expenses_store.current_expense, employeeEmail ?? auth_store.user?.email ?? 'MISSING_EMAIL');
|
await expenses_api.upsertExpense(
|
||||||
|
expenses_store.current_expense,
|
||||||
|
email ?? employeeEmail ?? auth_store.user?.email ?? 'MISSING_EMAIL'
|
||||||
|
);
|
||||||
|
|
||||||
expenses_store.is_showing_create_form = true;
|
expenses_store.is_showing_create_form = true;
|
||||||
expenses_store.mode = 'create';
|
expenses_store.mode = 'create';
|
||||||
|
|
|
||||||
|
|
@ -211,7 +211,7 @@
|
||||||
backdrop-filter="blur(6px)"
|
backdrop-filter="blur(6px)"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="column flex-center q-pa-md bg-secondary shadow-24 rounded-10"
|
class="column flex-center q-pa-md bg-dark shadow-24 rounded-10"
|
||||||
style="border: 2px solid var(--q-accent);"
|
style="border: 2px solid var(--q-accent);"
|
||||||
>
|
>
|
||||||
<span class="col-auto text-h6 text-bold text-uppercase">{{
|
<span class="col-auto text-h6 text-bold text-uppercase">{{
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export const useExpensesApi = () => {
|
||||||
expense.attachment_name = file.name;
|
expense.attachment_name = file.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
console.log('employee email provided for expense: ', employee_email)
|
||||||
const success = await expenses_store.upsertExpense(expense, employee_email);
|
const success = await expenses_store.upsertExpense(expense, employee_email);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ import type { BackendResponse } from "src/modules/shared/models/backend-response
|
||||||
import type { AttachmentPresignedURLResponse, Expense } from "src/modules/timesheets/models/expense.models";
|
import type { AttachmentPresignedURLResponse, Expense } from "src/modules/timesheets/models/expense.models";
|
||||||
|
|
||||||
export const ExpenseService = {
|
export const ExpenseService = {
|
||||||
createExpense: async (expense: Expense): Promise<{ success: boolean, data: Expense, error?: unknown }> => {
|
createExpense: async (expense: Expense, email?: string): Promise<{ success: boolean, data: Expense, error?: unknown }> => {
|
||||||
const response = await api.post('expense/create', expense);
|
const response = await api.post(`expense/create${email ? '?employee_email=' + email : ''}`, expense);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ export const useExpensesStore = defineStore('expenses', () => {
|
||||||
const upsertExpense = async (expense: Expense, email?: string): Promise<boolean> => {
|
const upsertExpense = async (expense: Expense, email?: string): Promise<boolean> => {
|
||||||
try {
|
try {
|
||||||
if (expense.id < 0) {
|
if (expense.id < 0) {
|
||||||
const data = await ExpenseService.createExpense(expense);
|
const data = await ExpenseService.createExpense(expense, email);
|
||||||
return data.success;
|
return data.success;
|
||||||
}
|
}
|
||||||
const data = await ExpenseService.updateExpense(expense, email);
|
const data = await ExpenseService.updateExpense(expense, email);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user