feat(profile): finalize get/update of user preferences, begin planning for employee-management module
This commit is contained in:
parent
4c79820128
commit
a4904ee80d
|
|
@ -1,5 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
//
|
||||
<script
|
||||
setup
|
||||
lang="ts"
|
||||
>
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
|||
|
|
@ -61,10 +61,14 @@ export default {
|
|||
},
|
||||
preferences: {
|
||||
tab_title: "preferences",
|
||||
display_options: "display options",
|
||||
display_options: "Color mode",
|
||||
language_options: "language options",
|
||||
'fr-FR': "French",
|
||||
'en-CA': "English",
|
||||
dark_mode: "dark",
|
||||
light_mode: "light",
|
||||
update_successful: "Preferences saved",
|
||||
update_failed: "Failed to save preferences",
|
||||
},
|
||||
schedule_presets: {
|
||||
tab_title: "Schedule",
|
||||
|
|
|
|||
|
|
@ -61,10 +61,14 @@ export default {
|
|||
},
|
||||
preferences: {
|
||||
tab_title: "préférences",
|
||||
display_options: "Options d'affichage",
|
||||
display_options: "Mode d'affichage",
|
||||
language_options: "Options de langue",
|
||||
'fr-FR': "Français",
|
||||
'en-CA': "Anglais",
|
||||
dark_mode: "sombre",
|
||||
light_mode: "clair",
|
||||
update_successful: "Préférences enregistrées",
|
||||
update_failed: "Échec de sauvegarde",
|
||||
},
|
||||
schedule_presets: {
|
||||
tab_title: "horaire",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,37 @@
|
|||
<script lang="ts" setup>
|
||||
<script
|
||||
lang="ts"
|
||||
setup
|
||||
>
|
||||
import { RouterView } from 'vue-router';
|
||||
import HeaderBar from 'src/layouts/components/main-layout-header-bar.vue';
|
||||
import FooterBar from 'src/layouts/components/main-layout-footer-bar.vue';
|
||||
import LeftDrawer from 'src/layouts/components/main-layout-left-drawer.vue';
|
||||
import { useUiStore } from 'src/stores/ui-store';
|
||||
import { onMounted, watch, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const ui_store = useUiStore();
|
||||
const user_preferences = ref(ui_store.user_preferences);
|
||||
|
||||
onMounted(async () => {
|
||||
console.log('current preferences on load: ', ui_store.user_preferences);
|
||||
if (ui_store.user_preferences.id === -1) {
|
||||
console.log('fetching preferences');
|
||||
await ui_store.getUserPreferences();
|
||||
}
|
||||
});
|
||||
|
||||
watch(user_preferences, async () => {
|
||||
if (ui_store.user_preferences.id !== -1) {
|
||||
console.log('triggered watcher');
|
||||
await ui_store.updateUserPreferences(t);
|
||||
return
|
||||
}
|
||||
console.log('watcher triggered but store has no preferences')
|
||||
await ui_store.getUserPreferences();
|
||||
}, {deep: true});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
|||
|
|
@ -55,18 +55,14 @@
|
|||
color="accent"
|
||||
class="col-auto"
|
||||
/>
|
||||
|
||||
<transition
|
||||
enter-active-class="animated rubberBand fast"
|
||||
leave-active-class=""
|
||||
mode="out-in"
|
||||
>
|
||||
<span
|
||||
:key="is_remembered ? 'yep' : 'nope'"
|
||||
class="col-auto text-weight-bold self-center q-ml-sm"
|
||||
:class="is_remembered ? 'text-accent' : ''"
|
||||
>{{ $t('login.button.remember_me') }}</span>
|
||||
</transition>
|
||||
>
|
||||
{{ $t('login.button.remember_me') }}
|
||||
</span>
|
||||
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions>
|
||||
|
|
@ -90,14 +86,14 @@
|
|||
<q-card-section class="row q-pt-sm">
|
||||
<q-separator
|
||||
size="2px"
|
||||
color="primary"
|
||||
color="accent"
|
||||
class="col self-center"
|
||||
/>
|
||||
<span class="col text-primary text-weight-bolder text-center text-uppercase 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="primary"
|
||||
color="accent"
|
||||
class="col self-center"
|
||||
/>
|
||||
</q-card-section>
|
||||
|
|
|
|||
|
|
@ -2,18 +2,20 @@
|
|||
setup
|
||||
lang="ts"
|
||||
>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useEmployeeListApi } from 'src/modules/employee-list/composables/use-employee-api';
|
||||
import { useEmployeeStore } from 'src/stores/employee-store';
|
||||
import EmployeeListTableItem from 'src/modules/employee-list/components/employee-list-table-item.vue';
|
||||
import { employee_list_columns } from 'src/modules/employee-list/models/employee-profile.models';
|
||||
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useUiStore } from 'src/stores/ui-store';
|
||||
import { useEmployeeStore } from 'src/stores/employee-store';
|
||||
import { useEmployeeListApi } from 'src/modules/employee-list/composables/use-employee-api';
|
||||
import { employee_list_columns, getCompanyName } from 'src/modules/employee-list/models/employee-profile.models';
|
||||
|
||||
const employee_list_api = useEmployeeListApi();
|
||||
const employee_store = useEmployeeStore();
|
||||
const ui_store = useUiStore();
|
||||
const is_loading_list = ref<boolean>(true);
|
||||
|
||||
const filter = ref("");
|
||||
const is_grid_mode = ref(true);
|
||||
|
||||
onMounted(async () => {
|
||||
is_loading_list.value = true;
|
||||
|
|
@ -26,7 +28,6 @@
|
|||
<div class="q-pa-lg">
|
||||
<q-table
|
||||
dense
|
||||
flat
|
||||
hide-pagination
|
||||
virtual-scroll
|
||||
title=" "
|
||||
|
|
@ -36,14 +37,13 @@
|
|||
row-key="name"
|
||||
:rows-per-page-options="[0]"
|
||||
:filter="filter"
|
||||
class="q-pa-md bg-transparent"
|
||||
:class="is_grid_mode ? '' : 'sticky-header-table'"
|
||||
class="bg-transparent no-shadow sticky-header-table"
|
||||
:style="$q.screen.lt.md ? '' : 'width: 80vw;'"
|
||||
:table-class="$q.dark.isActive ? 'q-px-md q-py-none q-mx-md rounded-10 bg-dark' : 'q-px-md q-py-none q-mx-md rounded-10 bg-white'"
|
||||
:table-class="$q.dark.isActive ? 'q-py-none q-mx-md rounded-10 bg-dark shadow-10' : 'q-py-none q-mx-md rounded-10 bg-white shadow-10'"
|
||||
color="accent"
|
||||
table-header-class="text-accent text-uppercase"
|
||||
card-container-class="justify-center"
|
||||
:grid="is_grid_mode"
|
||||
:grid="ui_store.user_preferences.is_employee_list_grid"
|
||||
:loading="is_loading_list"
|
||||
:no-data-label="$t('shared.error.no_data_found')"
|
||||
:no-results-label="$t('shared.error.no_search_results')"
|
||||
|
|
@ -87,7 +87,7 @@
|
|||
<q-space />
|
||||
|
||||
<q-btn-toggle
|
||||
v-model="is_grid_mode"
|
||||
v-model="ui_store.user_preferences.is_employee_list_grid"
|
||||
push
|
||||
rounded
|
||||
color="white"
|
||||
|
|
@ -119,6 +119,16 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<template #body-cell="scope">
|
||||
<q-td
|
||||
:props="scope"
|
||||
class="text-weight-medium"
|
||||
>
|
||||
<span v-if="scope.col.name === 'company_name'"> {{ getCompanyName(scope.value) }}</span>
|
||||
<span v-else>{{ scope.value }}</span>
|
||||
</q-td>
|
||||
</template>
|
||||
|
||||
<!-- Template for custome failed-to-load state -->
|
||||
<template v-slot:no-data="{ message, filter }">
|
||||
<div class="full-width column items-center text-accent q-gutter-sm">
|
||||
|
|
@ -138,14 +148,14 @@
|
|||
<style lang="sass">
|
||||
.sticky-header-table
|
||||
thead tr:first-child th
|
||||
background-color: var(--q-dark)
|
||||
background-color: var(--q-accent)
|
||||
margin-top: none
|
||||
|
||||
thead tr th
|
||||
position: sticky
|
||||
z-index: 1
|
||||
thead tr:first-child th
|
||||
top: 0
|
||||
top: 0px
|
||||
|
||||
&.q-table--loading thead tr:last-child th
|
||||
top: 48px
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
<script setup lang="ts">
|
||||
import { useEmployeeStore } from 'src/stores/employee-store';
|
||||
|
||||
const employeeStore = useEmployeeStore();
|
||||
const employee_store = useEmployeeStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-dialog v-model="employeeStore.isShowingEmployeeAddModifyWindow">
|
||||
<q-dialog v-model="employee_store.isShowingEmployeeAddModifyWindow">
|
||||
<q-card>
|
||||
<q-card-section>
|
||||
LOL
|
||||
</q-card-section>
|
||||
<q-inner-loading :showing="employeeStore.isLoadingEmployeeProfile"/>
|
||||
<q-inner-loading :showing="employee_store.is_loading"/>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</template>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import type { QTableColumn } from "quasar";
|
||||
|
||||
export interface EmployeeProfile {
|
||||
export class EmployeeProfile {
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
supervisor_full_name: string;
|
||||
|
|
@ -12,20 +12,20 @@ export interface EmployeeProfile {
|
|||
last_work_day: string;
|
||||
residence: string;
|
||||
birth_date: string;
|
||||
}
|
||||
|
||||
export const default_employee_profile: EmployeeProfile = {
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
supervisor_full_name: '',
|
||||
company_name: -1,
|
||||
job_title: '',
|
||||
email: '',
|
||||
phone_number: '',
|
||||
first_work_day: '',
|
||||
last_work_day: '',
|
||||
residence: '',
|
||||
birth_date: '',
|
||||
constructor() {
|
||||
this.first_name = '';
|
||||
this.last_name = '';
|
||||
this.supervisor_full_name = '';
|
||||
this.company_name = 271583;
|
||||
this.job_title = '';
|
||||
this.email = '';
|
||||
this.phone_number = '';
|
||||
this.first_work_day = '';
|
||||
this.last_work_day = '';
|
||||
this.residence = '';
|
||||
this.birth_date = '';
|
||||
}
|
||||
}
|
||||
|
||||
export const employee_list_columns: QTableColumn<EmployeeProfile>[] = [
|
||||
|
|
@ -66,3 +66,11 @@ export const employee_list_columns: QTableColumn<EmployeeProfile>[] = [
|
|||
align: 'left'
|
||||
},
|
||||
];
|
||||
|
||||
export const getCompanyName = (company_code: number) => {
|
||||
switch (company_code) {
|
||||
case 271583: return 'Targo';
|
||||
case 271585: return 'Solucom';
|
||||
default: return 'N / A';
|
||||
}
|
||||
}
|
||||
|
|
@ -7,8 +7,13 @@ export const EmployeeListService = {
|
|||
return response.data.data;
|
||||
},
|
||||
|
||||
getEmployeeDetails: async (email: string): Promise<EmployeeProfile> => {
|
||||
const response = await api.get<EmployeeProfile>('employees/profile/' + email);
|
||||
return response.data;
|
||||
getEmployeeDetails: async (): Promise<EmployeeProfile> => {
|
||||
const response = await api.get<{success: boolean, data: EmployeeProfile, error?: string}>('employees/profile');
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
getEmployeeDetailsWithEmployeeEmail: async (employee_email: string): Promise<EmployeeProfile> => {
|
||||
const response = await api.get<{success: boolean, data: EmployeeProfile, error?: string}>(`employees/profile?employee_email=${employee_email}`);
|
||||
return response.data.data;
|
||||
}
|
||||
};
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
import MenuPanelPreferences from 'src/modules/profile/components/shared/menu-panel-preferences.vue';
|
||||
import MenuPanelSchedulePresets from 'src/modules/profile/components/shared/menu-panel-schedule-presets.vue';
|
||||
import MenuTemplate from 'src/modules/profile/components/shared/menu-template.vue';
|
||||
import { default_employee_profile, type EmployeeProfile } from 'src/modules/employee-list/models/employee-profile.models';
|
||||
import { EmployeeProfile } from 'src/modules/employee-list/models/employee-profile.models';
|
||||
import { useAuthStore } from 'src/stores/auth-store';
|
||||
|
||||
const auth_store = useAuthStore();
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
SCHEDULE_PRESETS: 'schedule_presets',
|
||||
};
|
||||
|
||||
const employee_profile = defineModel<EmployeeProfile>({ default: default_employee_profile });
|
||||
const employee_profile = defineModel<EmployeeProfile>({ default: new EmployeeProfile });
|
||||
</script>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,38 +2,49 @@
|
|||
setup
|
||||
lang="ts"
|
||||
>
|
||||
import { ref } from 'vue';
|
||||
import { deepEqual } from 'src/utils/deep-equal';
|
||||
import MenuPanelInputField from 'src/modules/profile/components/shared/menu-panel-input-field.vue';
|
||||
import MenuPanelSelectField from 'src/modules/profile/components/shared/menu-panel-select-field.vue';
|
||||
import type { EmployeeProfile } from 'src/modules/employee-list/models/employee-profile.models';
|
||||
import { unwrapAndClone } from 'src/utils/unwrap-and-clone';
|
||||
|
||||
const employee_profile = defineModel<EmployeeProfile>({required: true});
|
||||
import { ref } from 'vue';
|
||||
import { deepEqual } from 'src/utils/deep-equal';
|
||||
import { unwrapAndClone } from 'src/utils/unwrap-and-clone';
|
||||
import { useAuthStore } from 'src/stores/auth-store';
|
||||
import { useEmployeeStore } from 'src/stores/employee-store';
|
||||
import { CAN_APPROVE_PAY_PERIODS } from 'src/modules/shared/models/user.models';
|
||||
import type { EmployeeProfile } from 'src/modules/employee-list/models/employee-profile.models';
|
||||
|
||||
const COMPANY_OPTIONS = [
|
||||
{ label: 'Targo', value: 271583 },
|
||||
{ label: 'Solucom', value: 271585 }
|
||||
];
|
||||
const SUPERVISOR_OPTIONS = [{ label: 'AAA', value: '1' }, { label: 'BBB', value: '2' }, { label: 'CCC', value: '3' }, { label: 'DDD', value: '4' }];
|
||||
|
||||
const auth_store = useAuthStore();
|
||||
const employee_store = useEmployeeStore();
|
||||
|
||||
const is_editing = ref<boolean>(false);
|
||||
let initial_info: EmployeeProfile = unwrapAndClone(employee_profile.value);
|
||||
const current_company_option = ref(COMPANY_OPTIONS.find(option => option.value === employee_store.employee.company_name) ?? { label: '', value: 0 })
|
||||
let initial_info: EmployeeProfile = unwrapAndClone(employee_store.employee);
|
||||
|
||||
const supervisor_options = [{ label: 'AAA', value: '1' }, { label: 'BBB', value: '2' }, { label: 'CCC', value: '3' }, { label: 'DDD', value: '4' }];
|
||||
|
||||
const onSubmit = () => {
|
||||
if (!is_editing.value) {
|
||||
is_editing.value = true;
|
||||
console.log('clicky!');
|
||||
return;
|
||||
}
|
||||
|
||||
is_editing.value = false;
|
||||
initial_info = unwrapAndClone(employee_profile.value); // update initial value for future possible resets
|
||||
initial_info = unwrapAndClone(employee_store.employee); // update initial value for future possible resets
|
||||
employee_store.employee.company_name = current_company_option.value.value;
|
||||
|
||||
if (!deepEqual(employee_profile.value, initial_info)) {
|
||||
if (!deepEqual(employee_store.employee, initial_info)) {
|
||||
// save the new data here
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const onReset = () => {
|
||||
employee_profile.value = unwrapAndClone(initial_info);
|
||||
employee_store.employee = unwrapAndClone(initial_info);
|
||||
is_editing.value = false;
|
||||
}
|
||||
</script>
|
||||
|
|
@ -46,23 +57,24 @@
|
|||
>
|
||||
<div :class="$q.screen.lt.md ? 'column' : 'row'">
|
||||
<MenuPanelInputField
|
||||
v-model="employee_profile.job_title"
|
||||
v-model="employee_store.employee.job_title"
|
||||
class="col"
|
||||
:is-editing="is_editing"
|
||||
:label-string="$t('profile.employee.job_title')"
|
||||
/>
|
||||
<MenuPanelInputField
|
||||
v-model="employee_profile.company_name"
|
||||
<MenuPanelSelectField
|
||||
v-model="current_company_option"
|
||||
:options="COMPANY_OPTIONS"
|
||||
class="col"
|
||||
:is-editing="is_editing"
|
||||
:label-string="$t('profile.employee.company')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="q-mx-xs">
|
||||
<div>
|
||||
<MenuPanelSelectField
|
||||
v-model="employee_profile.supervisor_full_name"
|
||||
:options="supervisor_options"
|
||||
v-model="employee_store.employee.supervisor_full_name"
|
||||
:options="SUPERVISOR_OPTIONS"
|
||||
:label-string="$t('profile.employee.supervisor')"
|
||||
:is-editing="is_editing"
|
||||
/>
|
||||
|
|
@ -71,15 +83,15 @@
|
|||
|
||||
<div :class="$q.screen.lt.md ? 'column' : 'row'">
|
||||
<MenuPanelInputField
|
||||
v-model="employee_profile.email"
|
||||
v-model="employee_store.employee.email"
|
||||
class="col"
|
||||
:is-editing="is_editing"
|
||||
:label-string="$t('profile.employee.email')"
|
||||
/>
|
||||
<MenuPanelInputField
|
||||
v-model="employee_profile.first_work_day"
|
||||
v-model="employee_store.employee.first_work_day"
|
||||
readonly
|
||||
class="col"
|
||||
class="col-auto"
|
||||
type="date"
|
||||
:is-editing="is_editing"
|
||||
:label-string="$t('profile.employee.hired_date')"
|
||||
|
|
@ -87,6 +99,7 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
v-if="CAN_APPROVE_PAY_PERIODS.includes(auth_store.user?.role ?? 'GUEST')"
|
||||
class="absolute-bottom"
|
||||
:class="$q.screen.lt.md ? 'column' : 'row'"
|
||||
>
|
||||
|
|
@ -94,20 +107,18 @@
|
|||
<q-btn
|
||||
v-if="is_editing"
|
||||
push
|
||||
size="sm"
|
||||
color="negative"
|
||||
type="reset"
|
||||
icon="cancel"
|
||||
class="q-ma-sm"
|
||||
class="q-ma-sm q-py-xs"
|
||||
:label="$t('shared.label.cancel')"
|
||||
/>
|
||||
<q-btn
|
||||
push
|
||||
size="sm"
|
||||
color="primary"
|
||||
color="accent"
|
||||
type="submit"
|
||||
:icon="is_editing ? 'save_alt' : 'create'"
|
||||
class="q-ma-sm"
|
||||
class="q-ma-sm q-py-xs"
|
||||
:label="is_editing ? $t('shared.label.save') : $t('shared.label.update')"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,35 +2,36 @@
|
|||
setup
|
||||
lang="ts"
|
||||
>
|
||||
import MenuPanelInputField from 'src/modules/profile/components/shared/menu-panel-input-field.vue';
|
||||
|
||||
import { ref } from 'vue';
|
||||
import { deepEqual } from 'src/utils/deep-equal';
|
||||
import MenuPanelInputField from 'src/modules/profile/components/shared/menu-panel-input-field.vue';
|
||||
import type { EmployeeProfile } from 'src/modules/employee-list/models/employee-profile.models';
|
||||
import { unwrapAndClone } from 'src/utils/unwrap-and-clone';
|
||||
import { useEmployeeStore } from 'src/stores/employee-store';
|
||||
import type { EmployeeProfile } from 'src/modules/employee-list/models/employee-profile.models';
|
||||
|
||||
const employee_profile = defineModel<EmployeeProfile>({required: true});
|
||||
|
||||
const employee_store = useEmployeeStore();
|
||||
const is_editing = ref<boolean>(false);
|
||||
|
||||
let initial_info: EmployeeProfile = unwrapAndClone(employee_profile.value);
|
||||
const initial_info = ref<EmployeeProfile>(unwrapAndClone(employee_store.employee));
|
||||
|
||||
const onSubmit = () => {
|
||||
if (!is_editing.value) {
|
||||
is_editing.value = true;
|
||||
initial_info.value = unwrapAndClone(employee_store.employee);
|
||||
return;
|
||||
}
|
||||
|
||||
is_editing.value = false;
|
||||
initial_info = unwrapAndClone(employee_profile.value); // update initial value for future possible resets
|
||||
initial_info.value = unwrapAndClone(employee_store.employee); // update initial value for future possible resets
|
||||
|
||||
if (!deepEqual(employee_profile.value, initial_info)) {
|
||||
if (!deepEqual(employee_store.employee, initial_info)) {
|
||||
// save the new data here
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const onReset = () => {
|
||||
employee_profile.value = unwrapAndClone(initial_info);
|
||||
employee_store.employee = unwrapAndClone(initial_info.value);
|
||||
is_editing.value = false;
|
||||
}
|
||||
</script>
|
||||
|
|
@ -43,14 +44,14 @@
|
|||
>
|
||||
<div :class="$q.screen.lt.md ? 'column' : 'row'">
|
||||
<MenuPanelInputField
|
||||
v-model="employee_profile.first_name"
|
||||
v-model="employee_store.employee.first_name"
|
||||
type="text"
|
||||
class="col"
|
||||
:is-editing="is_editing"
|
||||
:label-string="$t('profile.personal.first_name')"
|
||||
/>
|
||||
<MenuPanelInputField
|
||||
v-model="employee_profile.last_name"
|
||||
v-model="employee_store.employee.last_name"
|
||||
class="col"
|
||||
type="text"
|
||||
:is-editing="is_editing"
|
||||
|
|
@ -60,14 +61,14 @@
|
|||
|
||||
<div :class="$q.screen.lt.md ? 'column' : 'row'">
|
||||
<MenuPanelInputField
|
||||
v-model="employee_profile.phone_number"
|
||||
v-model="employee_store.employee.phone_number"
|
||||
class="col"
|
||||
type="text"
|
||||
:is-editing="is_editing"
|
||||
:label-string="$t('profile.personal.phone_number')"
|
||||
/>
|
||||
<MenuPanelInputField
|
||||
v-model="employee_profile.birth_date"
|
||||
v-model="employee_store.employee.birth_date"
|
||||
class="col"
|
||||
mask="#### / ## / ##"
|
||||
hint="ex: 1970 / 01 / 01"
|
||||
|
|
@ -78,7 +79,7 @@
|
|||
|
||||
<div :class="$q.screen.lt.md ? 'column' : 'row'">
|
||||
<MenuPanelInputField
|
||||
v-model="employee_profile.residence"
|
||||
v-model="employee_store.employee.residence"
|
||||
class="col"
|
||||
:is-editing="is_editing"
|
||||
:label-string="$t('profile.personal.address')"
|
||||
|
|
@ -93,21 +94,19 @@
|
|||
<q-space />
|
||||
<q-btn
|
||||
v-if="is_editing"
|
||||
push
|
||||
size="sm"
|
||||
flat
|
||||
color="negative"
|
||||
type="reset"
|
||||
icon="cancel"
|
||||
class="q-ma-sm"
|
||||
class="q-ma-sm q-py-xs"
|
||||
:label="$t('timesheet.cancel_button')"
|
||||
/>
|
||||
<q-btn
|
||||
push
|
||||
size="sm"
|
||||
color="primary"
|
||||
color="accent"
|
||||
type="submit"
|
||||
:icon="is_editing ? 'save_alt' : 'create'"
|
||||
class="q-ma-sm"
|
||||
class="q-ma-sm q-py-xs"
|
||||
:label="is_editing ? $t('shared.label.save') : $t('shared.label.update')"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
<script setup lang="ts">
|
||||
<script
|
||||
setup
|
||||
lang="ts"
|
||||
>
|
||||
import type { ValidationRule } from 'quasar';
|
||||
|
||||
const model = defineModel<string | number>({ required: true });
|
||||
const model = defineModel<string | number | undefined>({ required: true });
|
||||
|
||||
const { readonly = false, hint = '' } = defineProps<{
|
||||
labelString: string;
|
||||
|
|
@ -23,14 +26,19 @@
|
|||
:standout="$q.dark.isActive ? 'bg-blue-grey-3' : 'bg-blue-grey-9'"
|
||||
debounce="500"
|
||||
label-color="accent"
|
||||
class="q-ma-xs text-uppercase"
|
||||
input-class="text-weight-medium text-h6"
|
||||
label-slot
|
||||
class="q-ma-xs"
|
||||
input-class="text-weight-light"
|
||||
input-style="font-size: 1.2em"
|
||||
:hide-hint="hint === ''"
|
||||
:hint="isEditing ? hint : ''"
|
||||
:mask="mask"
|
||||
:readonly="readonly || !isEditing"
|
||||
:type="type"
|
||||
:label="labelString"
|
||||
:rules="rules"
|
||||
/>
|
||||
>
|
||||
<template #label>
|
||||
<span class="text-weight-bolder text-accent text-uppercase">{{ labelString }}</span>
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
|
|
@ -1,46 +1,39 @@
|
|||
<script setup lang="ts">
|
||||
<script
|
||||
setup
|
||||
lang="ts"
|
||||
>
|
||||
import LanguageSwitch from 'src/modules/shared/components/language-switch.vue';
|
||||
import { ref } from 'vue';
|
||||
import { Dark } from 'quasar';
|
||||
import LanguageSwitch from 'src/modules/shared/components/language-switch.vue';
|
||||
|
||||
import { useUiStore } from 'src/stores/ui-store';
|
||||
const ui_store = useUiStore();
|
||||
const initial_dark_mode_value = Dark.isActive;
|
||||
const is_dark_mode = ref<boolean>(initial_dark_mode_value);
|
||||
|
||||
const toggle_dark_mode = (value: boolean) => {
|
||||
is_dark_mode.value = value;
|
||||
if (ui_store.user_preferences) ui_store.user_preferences.is_dark_mode = value;
|
||||
Dark.set(value);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-form class="q-pa-md column fit">
|
||||
<div class="col-auto text-uppercase rounded-5" style="line-height: 1em;">{{ $t('profile.preferences.display_options') }}</div>
|
||||
<q-card
|
||||
flat
|
||||
class="col-auto column justify-center items-center content-center q-mb-lg q-pa-md"
|
||||
style="border: solid #AAA 1px;"
|
||||
class="col-auto justify-center content-center q-mb-lg q-pa-none"
|
||||
:style="$q.dark.isActive ? 'background-color: #FFF1;' : 'background-color: #0001;'"
|
||||
>
|
||||
<q-card-section class="q-py-none">
|
||||
<span class="text-uppercase text-weight-bold text-accent">{{ $t('profile.preferences.display_options')
|
||||
}}</span>
|
||||
</q-card-section>
|
||||
|
||||
<div class="row col">
|
||||
<q-card flat class="col column q-pa-xs bg-white" style="border: solid 1px var(--q-primary);">
|
||||
<div
|
||||
class="col-auto column rounded-4 ellipsis"
|
||||
style="height: 90px; min-width: 80px; background-color: #DAE0E7;"
|
||||
<q-card-section
|
||||
horizontal
|
||||
class="flex-center text-uppercase"
|
||||
>
|
||||
<div class="bg-primary col-1"></div>
|
||||
<div class=" row col">
|
||||
<div class="col-8 q-ma-xs rounded-borders" style="background-color: white;"></div>
|
||||
<div class="col column q-gutter-xs q-py-xs q-pr-xs">
|
||||
<div class="col rounded-borders" style="background-color: white;"></div>
|
||||
<div class="col rounded-borders" style="background-color: white;"></div>
|
||||
<div class="col rounded-borders" style="background-color: white;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-primary col-1"></div>
|
||||
</div>
|
||||
<span class="col-auto text-subtitle2 text-primary text-center text-uppercase">{{$t('profile.preferences.light_mode')}}</span>
|
||||
</q-card>
|
||||
|
||||
<span class="q-mx-md text-weight-medium">{{ $t('profile.preferences.light_mode') }}</span>
|
||||
<q-toggle
|
||||
v-model="is_dark_mode"
|
||||
@update:model-value="value => toggle_dark_mode(value)"
|
||||
|
|
@ -49,36 +42,26 @@
|
|||
checked-icon="dark_mode"
|
||||
unchecked-icon="light_mode"
|
||||
/>
|
||||
|
||||
<q-card flat class="col column q-pa-xs bg-white" style="border: solid 1px var(--q-primary);">
|
||||
<div
|
||||
class="col-auto column rounded-4 ellipsis"
|
||||
style="height: 90px; min-width: 80px; background-color: #0f1114;"
|
||||
>
|
||||
<div class="bg-primary col-1"></div>
|
||||
<div class=" row col">
|
||||
<div class="col-8 q-ma-xs rounded-borders" style="background-color: #333;"></div>
|
||||
<div class="col column q-gutter-xs q-py-xs q-pr-xs">
|
||||
<div class="col rounded-borders" style="background-color: #333;"></div>
|
||||
<div class="col rounded-borders" style="background-color: #333;"></div>
|
||||
<div class="col rounded-borders" style="background-color: #333;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-primary col-1"></div>
|
||||
</div>
|
||||
<span class="col-auto text-subtitle2 text-primary text-center text-uppercase">{{$t('profile.preferences.dark_mode')}}</span>
|
||||
</q-card>
|
||||
</div>
|
||||
<span class="q-mx-md text-weight-medium">{{ $t('profile.preferences.dark_mode') }}</span>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
|
||||
<div class="col-auto text-uppercase rounded-5" style="line-height: 1em;">{{ $t('profile.preferences.language_options') }}</div>
|
||||
<q-card
|
||||
flat
|
||||
class="col-auto column justify-center items-center content-center q-mb-lg q-pa-md"
|
||||
style="border: solid #AAA 1px;"
|
||||
class="col-auto justify-center content-center q-mb-lg q-pa-none"
|
||||
:style="$q.dark.isActive ? 'background-color: #FFF1;' : 'background-color: #0001;'"
|
||||
>
|
||||
<q-card-section class="q-py-none">
|
||||
<span class="text-uppercase text-weight-bold text-accent">{{ $t('profile.preferences.language_options')
|
||||
}}</span>
|
||||
</q-card-section>
|
||||
|
||||
<LanguageSwitch class="q-mr-xs col-auto" />
|
||||
<q-card-section
|
||||
horizontal
|
||||
class="flex-center"
|
||||
>
|
||||
<LanguageSwitch class="col-auto" />
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</q-form>
|
||||
</template>
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
const model = defineModel<string>();
|
||||
<script
|
||||
setup
|
||||
lang="ts"
|
||||
>
|
||||
const model = defineModel<string | number | {label: string, value: unknown} | undefined>();
|
||||
|
||||
const { readonly = false, localizeOptions = false } = defineProps<{
|
||||
options: { label: string, value: string }[];
|
||||
options: { label: string, value: string | number }[];
|
||||
labelString: string;
|
||||
isEditing: boolean;
|
||||
readonly?: boolean;
|
||||
|
|
@ -18,14 +21,23 @@
|
|||
:stack-label="!isEditing"
|
||||
:standout="$q.dark.isActive ? 'bg-blue-grey-3' : 'bg-blue-grey-9'"
|
||||
label-color="accent"
|
||||
class="q-ma-xs text-h6 text-uppercase"
|
||||
popup-content-class="text-weight-medium text-h6"
|
||||
input-class="text-weight-medium"
|
||||
class="q-ma-xs"
|
||||
popup-content-class="text-weight-medium text-h6 rounded-5"
|
||||
options-selected-class="bg-accent text-white"
|
||||
:menu-offset="[0, 10]"
|
||||
:options="options"
|
||||
:readonly="readonly || !isEditing"
|
||||
:hide-dropdown-icon="!isEditing"
|
||||
:label="labelString"
|
||||
:option-label="opt => localizeOptions ? $t(opt) : opt"
|
||||
:option-label="opt => localizeOptions ? $t(opt.label) : opt.label ?? opt"
|
||||
hint=''
|
||||
/>
|
||||
>
|
||||
<template #label>
|
||||
<span class="text-weight-bolder text-accent text-uppercase">{{ labelString }}</span>
|
||||
</template>
|
||||
|
||||
<template #selected-item="scope">
|
||||
<span class="text-weight-light" style="font-size: 1.2em;">{{ scope.opt.label }}</span>
|
||||
</template>
|
||||
</q-select>
|
||||
</template>
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
<script
|
||||
setup
|
||||
lang="ts"
|
||||
>
|
||||
import { ref } from 'vue';
|
||||
import MenuHeader from 'src/modules/profile/components/shared/menu-header.vue';
|
||||
|
||||
|
|
@ -13,7 +16,7 @@
|
|||
|
||||
<template>
|
||||
<div
|
||||
:class="$q.screen.lt.md ? 'column no-wrap' : 'row'"
|
||||
:class="$q.screen.lt.md ? 'column no-wrap flex-center' : 'row'"
|
||||
:style="$q.screen.lt.md ? 'width: 90vw;' : 'width: 40vw;'"
|
||||
>
|
||||
<MenuHeader
|
||||
|
|
@ -21,10 +24,6 @@
|
|||
:user-last-name="lastName"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="col-3 no-wrap"
|
||||
:class="$q.screen.lt.md ? '' : 'column'"
|
||||
>
|
||||
<q-card
|
||||
class="col-auto q-pa-xs"
|
||||
:class="$q.screen.lt.md ? 'q-mb-sm' : 'q-mr-sm'"
|
||||
|
|
@ -32,19 +31,16 @@
|
|||
<q-tabs
|
||||
v-model="current_menu"
|
||||
:vertical="$q.screen.gt.sm"
|
||||
dense
|
||||
active-color="accent"
|
||||
indicator-color="accent"
|
||||
>
|
||||
<slot name="tabs"></slot>
|
||||
</q-tabs>
|
||||
</q-card>
|
||||
<div class="col"></div>
|
||||
</div>
|
||||
|
||||
<q-card
|
||||
class="col"
|
||||
:class="$q.screen.lt.md ? '' : 'q-ml-sm'"
|
||||
:class="$q.screen.lt.md ? 'full-width' : 'q-ml-sm'"
|
||||
>
|
||||
<q-tab-panels
|
||||
v-model="current_menu"
|
||||
|
|
|
|||
21
src/modules/profile/models/preferences.models.ts
Normal file
21
src/modules/profile/models/preferences.models.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import type { MessageLanguages } from "src/boot/i18n";
|
||||
|
||||
export class Preferences {
|
||||
id: number;
|
||||
notifications: boolean;
|
||||
is_dark_mode: boolean | null;
|
||||
display_language: MessageLanguages;
|
||||
is_lefty_mode: boolean;
|
||||
is_employee_list_grid: boolean;
|
||||
is_timesheet_approval_grid: boolean;
|
||||
|
||||
constructor() {
|
||||
this.id = -1;
|
||||
this.notifications = true;
|
||||
this.is_dark_mode = null;
|
||||
this.display_language = 'fr-FR';
|
||||
this.is_lefty_mode = false;
|
||||
this.is_employee_list_grid = true;
|
||||
this.is_timesheet_approval_grid = true;
|
||||
}
|
||||
}
|
||||
15
src/modules/profile/services/profile-service.ts
Normal file
15
src/modules/profile/services/profile-service.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { api } from "src/boot/axios";
|
||||
import type { Preferences } from "src/modules/profile/models/preferences.models";
|
||||
import type { BackendResponse } from "src/modules/shared/models/backend-response.models";
|
||||
|
||||
export const ProfileService = {
|
||||
getUserPreferences: async (): Promise<BackendResponse<Preferences>> => {
|
||||
const response = await api.get<BackendResponse<Preferences>>(`/preferences`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
updateUserPreferences: async (new_preferences: Preferences): Promise<BackendResponse<Preferences>> => {
|
||||
const response = await api.patch<BackendResponse<Preferences>>(`/preferences/update`, new_preferences);
|
||||
return response.data;
|
||||
},
|
||||
};
|
||||
|
|
@ -1,36 +1,45 @@
|
|||
<script lang="ts" setup>
|
||||
import { useI18n } from 'vue-i18n';
|
||||
<script
|
||||
lang="ts"
|
||||
setup
|
||||
>
|
||||
import { useUiStore } from 'src/stores/ui-store';
|
||||
import type { MessageLanguages } from 'src/boot/i18n';
|
||||
|
||||
const { locale } = useI18n();
|
||||
const localeOptions = [
|
||||
{ value: 'en-CA', label: 'English' },
|
||||
{ value: 'fr-FR', label: 'Francais' },
|
||||
];
|
||||
const ui_store = useUiStore();
|
||||
|
||||
const setDisplayLanguage = (locale: MessageLanguages) => {
|
||||
if (ui_store.user_preferences !== undefined) {
|
||||
ui_store.user_preferences.display_language = locale;
|
||||
console.log('triggered language change: ', ui_store.user_preferences.display_language);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<q-list dense class="row">
|
||||
<q-item v-for="option in localeOptions"
|
||||
:key="option.value"
|
||||
tag="label"
|
||||
v-ripple
|
||||
<q-list
|
||||
dense
|
||||
class="row full-width"
|
||||
>
|
||||
<q-item-section avatar>
|
||||
<q-radio v-model="locale" :val="option.value" />
|
||||
<q-item
|
||||
v-for="locale in $i18n.availableLocales"
|
||||
:key="locale"
|
||||
clickable
|
||||
dense
|
||||
v-ripple
|
||||
class="col rounded-5 q-ma-sm shadow-1 "
|
||||
:class="locale === $i18n.locale ? 'bg-accent text-white text-weight-bolder' : ''"
|
||||
@click="setDisplayLanguage(locale as MessageLanguages)"
|
||||
>
|
||||
<q-item-section class="text-uppercase justify-center">
|
||||
<q-item-label> {{ $t(`profile.preferences.${locale}`) }}</q-item-label>
|
||||
</q-item-section>
|
||||
|
||||
<q-item-section>
|
||||
<q-item-label>{{ option.label }}</q-item-label>
|
||||
<q-item-section side>
|
||||
<q-icon
|
||||
v-if="locale === $i18n.locale"
|
||||
name="check"
|
||||
color="white"
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</div>
|
||||
<!-- <q-btn-dropdown push color="primary" :label="$t('shared.languageLabel')" icon="language">
|
||||
<q-list>
|
||||
<q-item clickable v-close-popup v-for="option in localeOptions" :key="option.value" @click="locale = option.value">
|
||||
<q-item-section>{{ option.label }}</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown> -->
|
||||
</template>
|
||||
5
src/modules/shared/models/backend-response.models.ts
Normal file
5
src/modules/shared/models/backend-response.models.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export interface BackendResponse<T> {
|
||||
success: boolean;
|
||||
data?: T | undefined;
|
||||
error?: string | undefined;
|
||||
};
|
||||
|
|
@ -4,19 +4,34 @@
|
|||
>
|
||||
import MenuEmployee from 'src/modules/profile/components/employee/menu-employee.vue';
|
||||
import { useAuthStore } from 'src/stores/auth-store';
|
||||
// import type { EmployeeProfile } from 'src/modules/employee-list/models/employee-profile.models';
|
||||
import { useEmployeeStore } from 'src/stores/employee-store';
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
const auth_store = useAuthStore();
|
||||
const employee_store = useEmployeeStore();
|
||||
const employee_roles = ['SUPERVISOR', 'EMPLOYEE', 'ADMIN', 'HR', 'ACCOUNTING'];
|
||||
|
||||
// const employee_profile = defineModel<EmployeeProfile>({ required: true });
|
||||
const is_employee = employee_roles.includes(auth_store.user?.role.toUpperCase() ?? 'GUEST');
|
||||
|
||||
onMounted(async () => {
|
||||
if (is_employee) {
|
||||
await employee_store.getEmployeeDetails();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-page class="bg-secondary column items-center justify-center">
|
||||
<MenuEmployee
|
||||
v-if="employee_roles.includes(auth_store.user?.role.toUpperCase() ?? 'GUEST')"
|
||||
class="col-auto"
|
||||
class="col-sm-12 col-md-10 col-lg-9 col-xl-8"
|
||||
/>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.q-field--standout.q-field--readonly .q-field__control:before) {
|
||||
border: none !important;
|
||||
background-color: transparent;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -3,8 +3,9 @@ export enum RouteNames {
|
|||
LOGIN = 'login',
|
||||
LOGIN_SUCCESS = 'login-success',
|
||||
DASHBOARD = 'dashboard',
|
||||
TIMESHEET_APPROVALS = 'timesheet-approvals',
|
||||
EMPLOYEE_LIST = 'employee-list',
|
||||
PROFILE = 'user/profile',
|
||||
TIMESHEET = 'timesheet'
|
||||
TIMESHEET_APPROVALS = 'timesheets_approval',
|
||||
EMPLOYEE_LIST = 'employee_list',
|
||||
EMPLOYEE_MANAGEMENT = 'employee_management',
|
||||
PROFILE = 'personal_profile',
|
||||
TIMESHEET = 'timesheets'
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
import { ref } from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
import { EmployeeListService } from "src/modules/employee-list/services/employee-list-service";
|
||||
import { default_employee_profile, type EmployeeProfile } from "src/modules/employee-list/models/employee-profile.models";
|
||||
import { EmployeeProfile } from "src/modules/employee-list/models/employee-profile.models";
|
||||
|
||||
export const useEmployeeStore = defineStore('employee', () => {
|
||||
const employee = ref<EmployeeProfile>( default_employee_profile );
|
||||
const employee = ref<EmployeeProfile>(new EmployeeProfile);
|
||||
const employee_list = ref<EmployeeProfile[]>([]);
|
||||
const isShowingEmployeeAddModifyWindow = ref<boolean>(false);
|
||||
const isLoadingEmployeeProfile = ref(false);
|
||||
const is_loading = ref(false);
|
||||
const isLoadingEmployeeList = ref(false);
|
||||
|
||||
const getEmployeeList = async () => {
|
||||
|
|
@ -22,19 +22,24 @@ export const useEmployeeStore = defineStore('employee', () => {
|
|||
isLoadingEmployeeList.value = false;
|
||||
};
|
||||
|
||||
const getEmployeeDetails = async (email: string) => {
|
||||
isLoadingEmployeeProfile.value = true;
|
||||
const getEmployeeDetails = async (email?: string) => {
|
||||
is_loading.value = true;
|
||||
try {
|
||||
const response = await EmployeeListService.getEmployeeDetails(email);
|
||||
if (email === undefined) {
|
||||
const response = await EmployeeListService.getEmployeeDetails();
|
||||
employee.value = response;
|
||||
} else{
|
||||
const response = await EmployeeListService.getEmployeeDetailsWithEmployeeEmail(email);
|
||||
employee.value = response;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('There was an error retrieving employee info: ', error);
|
||||
//TODO: trigger an alert window with an error message here!
|
||||
}
|
||||
isLoadingEmployeeProfile.value = false;
|
||||
is_loading.value = false;
|
||||
};
|
||||
|
||||
return { employee, employee_list, isShowingEmployeeAddModifyWindow, isLoadingEmployeeList, isLoadingEmployeeProfile, getEmployeeList, getEmployeeDetails };
|
||||
return { employee, employee_list, isShowingEmployeeAddModifyWindow, isLoadingEmployeeList, is_loading, getEmployeeList, getEmployeeDetails };
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,81 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { useQuasar } from 'quasar';
|
||||
import { Notify, LocalStorage, useQuasar, Dark } from 'quasar';
|
||||
import { computed, ref } from 'vue';
|
||||
import { Preferences } from 'src/modules/profile/models/preferences.models';
|
||||
import { ProfileService } from 'src/modules/profile/services/profile-service';
|
||||
import { useI18n, type ComposerTranslation } from 'vue-i18n';
|
||||
|
||||
|
||||
export const useUiStore = defineStore('ui', () => {
|
||||
const q = useQuasar();
|
||||
const is_left_drawer_open = ref(true);
|
||||
const { locale } = useI18n();
|
||||
const is_left_drawer_open = ref(false);
|
||||
const focus_next_component = ref(false);
|
||||
const is_mobile_mode = computed(() => q.screen.lt.md);
|
||||
const user_preferences = ref<Preferences>(new Preferences);
|
||||
|
||||
|
||||
const toggleRightDrawer = () => {
|
||||
is_left_drawer_open.value = !is_left_drawer_open.value;
|
||||
};
|
||||
|
||||
const getUserPreferences = async () => {
|
||||
try {
|
||||
const local_user_preferences = LocalStorage.getItem<Preferences>('user_preferences');
|
||||
|
||||
if (local_user_preferences !== null) {
|
||||
if (local_user_preferences.id !== -1) {
|
||||
Object.assign(user_preferences.value, local_user_preferences);
|
||||
setPreferences();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const response = await ProfileService.getUserPreferences();
|
||||
|
||||
if (response.success && response.data) {
|
||||
LocalStorage.setItem('user_preferences', response.data);
|
||||
Object.assign(user_preferences.value, response.data);
|
||||
setPreferences();
|
||||
}
|
||||
} catch (error) {
|
||||
user_preferences.value = new Preferences;
|
||||
console.error('Could not retrieve user preferences: ', error);
|
||||
}
|
||||
};
|
||||
|
||||
const updateUserPreferences = async (t: ComposerTranslation) => {
|
||||
try {
|
||||
if (user_preferences.value.id === -1) return;
|
||||
|
||||
const response = await ProfileService.updateUserPreferences(user_preferences.value);
|
||||
if (response.success && response.data) {
|
||||
Object.assign(user_preferences.value, response.data);
|
||||
LocalStorage.setItem('user_preferences', response.data);
|
||||
setPreferences();
|
||||
Notify.create({ message: t('profile.preferences.update_successful'), color: 'accent' });
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Could not update user preferences: ', error);
|
||||
}
|
||||
Notify.create({ message: t('profile.preferences.update_failed'), color: 'negative' })
|
||||
};
|
||||
|
||||
const setPreferences = () => {
|
||||
if (user_preferences.value !== undefined) {
|
||||
Dark.set(user_preferences.value.is_dark_mode ?? 'auto');
|
||||
locale.value = user_preferences.value.display_language;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
is_mobile_mode,
|
||||
focus_next_component,
|
||||
is_left_drawer_open,
|
||||
toggleRightDrawer
|
||||
user_preferences,
|
||||
toggleRightDrawer,
|
||||
getUserPreferences,
|
||||
updateUserPreferences,
|
||||
};
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user