diff --git a/eslint.config.js b/eslint.config.js index 2498ecb..e1caa74 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -41,6 +41,11 @@ export default defineConfigWithVueTs( 'error', { prefer: 'type-imports' } ], + "no-unused-vars": "off", + '@typescript-eslint/no-unused-vars': [ + 'warn', + { argsIgnorePattern: '^_', varsIgnorePattern: '^_' } + ], } }, // https://github.com/vuejs/eslint-config-typescript @@ -63,15 +68,15 @@ export default defineConfigWithVueTs( } }, + files: ['**/*.ts', '**/*.vue'], + // add your custom rules here rules: { 'prefer-promise-reject-errors': 'off', // warn about unused but underscored variables - 'no-unused-vars': [ - 'warn', - { argsIgnorePattern: '^_' } - ], + + 'no-unused-vars': 'off', // allow debugger during development only 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' diff --git a/quasar.config.ts b/quasar.config.ts index 47e6b1f..2fc4168 100644 --- a/quasar.config.ts +++ b/quasar.config.ts @@ -29,11 +29,12 @@ export default defineConfig((ctx) => { // 'fontawesome-v6', // 'eva-icons', // 'themify', - // 'line-awesome', + 'line-awesome', + 'material-icons', + 'material-icons-outlined', + + 'roboto-font', // 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both! - - 'roboto-font', // optional, you are not bound to it - 'material-icons', // optional, you are not bound to it ], // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-file#build diff --git a/src/assets/en-CA.png b/src/assets/en-CA.png new file mode 100644 index 0000000..cd42ab8 Binary files /dev/null and b/src/assets/en-CA.png differ diff --git a/src/assets/fr-FR.png b/src/assets/fr-FR.png new file mode 100644 index 0000000..cb902f7 Binary files /dev/null and b/src/assets/fr-FR.png differ diff --git a/src/i18n/en-ca/index.ts b/src/i18n/en-ca/index.ts index 1b03a4a..61cd1d9 100644 --- a/src/i18n/en-ca/index.ts +++ b/src/i18n/en-ca/index.ts @@ -9,6 +9,43 @@ export default { role: "Role", supervisor: "Supervisor", company: "Company", + is_supervisor: "is a supervisor", + active: "active", + inactive: "inactive", + }, + }, + + employee_management: { + add_employee: "Add employee", + modify_employee: "Modify employee", + access_label: "access", + details_label: "details", + schedule_label: "schedule", + schedule_presets: { + preset_list_placeholder: "Select a schedule", + preset_name_placeholder: "schedule preset name", + }, + module_access: { + dashboard: "Dashboard", + employee_list: "employee list", + employee_management: "employee management", + personal_profile: "profile", + timesheets: "timesheets", + timesheets_approval: "timesheet approval", + user_access: "module access", + by_role: "by role", + by_module: "by module", + preset_admin: "administrator", + preset_employee: "employee", + uncheck_all: "remove all", + admin_description: "Check all modules", + employee_description: "Only check modules that are relevant to standard employees with no management access", + none_description: "Uncheck all modules", + usage_description: "You can use roles to enable preset modules, add or remove modules individually, or both", + }, + filter: { + hide_terminated: "Hide inactive employees", + sort_by_tags: "sort by tags", }, }, @@ -58,6 +95,8 @@ export default { company: "company", supervisor: "supervisor", hired_date: "hiring date", + fired_date: "departure date", + bankroll_id: "payroll ID", }, preferences: { tab_title: "preferences", @@ -67,6 +106,7 @@ export default { 'en-CA': "English", dark_mode: "dark", light_mode: "light", + auto_mode: "auto", update_successful: "Preferences saved", update_failed: "Failed to save preferences", }, @@ -119,13 +159,13 @@ export default { remote: "remote work", }, weekday: { - sunday: "dimanche", - monday: "lundi", - tuesday: "mardi", - wednesday: "mercredi", - thursday: "jeudi", - friday: "vendredi", - saturday: "samedi", + sun: "dimanche", + mon: "lundi", + tue: "mardi", + wed: "mercredi", + thu: "jeudi", + fri: "vendredi", + sat: "samedi", }, }, diff --git a/src/i18n/fr-ca/index.ts b/src/i18n/fr-ca/index.ts index 929145d..402646e 100644 --- a/src/i18n/fr-ca/index.ts +++ b/src/i18n/fr-ca/index.ts @@ -9,6 +9,43 @@ export default { role: "rôle", supervisor: "superviseur", company: "Compagnie", + is_supervisor: "est un superviseur", + active: "actif", + inactive: "inactif", + }, + }, + + employee_management: { + add_employee: "Ajouter employé", + modify_employee: "Modifier employé", + access_label: "accès", + details_label: "détails", + schedule_label: "horaire", + schedule_presets: { + preset_list_placeholder: "Sélectionner un horaire", + preset_name_placeholder: "nom de l'horaire", + }, + module_access: { + dashboard: "Accueil", + employee_list: "Répertoire du personnel", + employee_management: "Gestion employés", + personal_profile: "profil personnel", + timesheets: "carte de temps", + timesheets_approval: "validation cartes de temps", + user_access: "module access", + by_role: "par rôle", + by_module: "par module", + preset_admin: "administrateur", + preset_employee: "employé", + uncheck_all: "Tout enlever", + admin_description: "Selectionner tous les modules", + employee_description: "Selectionner seulement les modules qui sont pertinents aux employés sans accès spéciaux", + none_description: "Enlever tous les accès", + usage_description: "Vous pouvez utiliser les rôles pour sélectionner des modules prédéfinis, enlever ou ajouter des modules individuellement, ou les deux", + }, + filter: { + hide_terminated: "Cacher les employés inactifs", + sort_by_tags: "filtrer par identifiants", }, }, @@ -58,6 +95,8 @@ export default { company: "compagnie", supervisor: "nom du superviseur", hired_date: "date d'embauche", + fired_date: "date de départ", + bankroll_id: "identifiant de paie", }, preferences: { tab_title: "préférences", @@ -67,6 +106,7 @@ export default { 'en-CA': "Anglais", dark_mode: "sombre", light_mode: "clair", + auto_mode: "automatique", update_successful: "Préférences enregistrées", update_failed: "Échec de sauvegarde", }, @@ -120,13 +160,13 @@ export default { remote: "télétravail", }, weekday: { - sunday: "dimanche", - monday: "lundi", - tuesday: "mardi", - wednesday: "mercredi", - thursday: "jeudi", - friday: "vendredi", - saturday: "samedi", + sun: "dimanche", + mon: "lundi", + tue: "mardi", + wed: "mercredi", + thu: "jeudi", + fri: "vendredi", + sat: "samedi", }, }, diff --git a/src/layouts/components/main-layout-left-drawer.vue b/src/layouts/components/main-layout-left-drawer.vue index 2d0a146..01e7ed3 100644 --- a/src/layouts/components/main-layout-left-drawer.vue +++ b/src/layouts/components/main-layout-left-drawer.vue @@ -93,7 +93,7 @@ > diff --git a/src/layouts/main-layout.vue b/src/layouts/main-layout.vue index d202b83..97163be 100644 --- a/src/layouts/main-layout.vue +++ b/src/layouts/main-layout.vue @@ -16,20 +16,16 @@ 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}); diff --git a/src/modules/employee-list/components/add-modify-dialog-access.vue b/src/modules/employee-list/components/add-modify-dialog-access.vue new file mode 100644 index 0000000..1dbb15c --- /dev/null +++ b/src/modules/employee-list/components/add-modify-dialog-access.vue @@ -0,0 +1,159 @@ + + + \ No newline at end of file diff --git a/src/modules/employee-list/components/add-modify-dialog-form.vue b/src/modules/employee-list/components/add-modify-dialog-form.vue new file mode 100644 index 0000000..d1db2c4 --- /dev/null +++ b/src/modules/employee-list/components/add-modify-dialog-form.vue @@ -0,0 +1,314 @@ + + + \ No newline at end of file diff --git a/src/modules/employee-list/components/add-modify-dialog-schedule-preview.vue b/src/modules/employee-list/components/add-modify-dialog-schedule-preview.vue new file mode 100644 index 0000000..65c761a --- /dev/null +++ b/src/modules/employee-list/components/add-modify-dialog-schedule-preview.vue @@ -0,0 +1,59 @@ + + + \ No newline at end of file diff --git a/src/modules/employee-list/components/add-modify-dialog-schedule.vue b/src/modules/employee-list/components/add-modify-dialog-schedule.vue new file mode 100644 index 0000000..a156bfd --- /dev/null +++ b/src/modules/employee-list/components/add-modify-dialog-schedule.vue @@ -0,0 +1,99 @@ + + + \ No newline at end of file diff --git a/src/modules/employee-list/components/add-modify-dialog.vue b/src/modules/employee-list/components/add-modify-dialog.vue new file mode 100644 index 0000000..fb3a7e6 --- /dev/null +++ b/src/modules/employee-list/components/add-modify-dialog.vue @@ -0,0 +1,118 @@ + + + \ No newline at end of file diff --git a/src/modules/employee-list/components/customer/customer-profile.vue b/src/modules/employee-list/components/customer/customer-profile.vue deleted file mode 100644 index e69de29..0000000 diff --git a/src/modules/employee-list/components/employee-list-table-item.vue b/src/modules/employee-list/components/employee-list-table-item.vue index d7472d0..7921f03 100644 --- a/src/modules/employee-list/components/employee-list-table-item.vue +++ b/src/modules/employee-list/components/employee-list-table-item.vue @@ -9,7 +9,7 @@ // return first_name.charAt(0) + last_name.charAt(0); // }; - const { row, index = 0 } = defineProps<{ + const { row, index = -1 } = defineProps<{ row: EmployeeProfile index?: number }>() @@ -19,23 +19,21 @@ \ No newline at end of file diff --git a/src/modules/employee-list/components/employee-list-table.vue b/src/modules/employee-list/components/employee-list-table.vue index bbfb6bb..1461281 100644 --- a/src/modules/employee-list/components/employee-list-table.vue +++ b/src/modules/employee-list/components/employee-list-table.vue @@ -5,38 +5,76 @@ import EmployeeListTableItem from 'src/modules/employee-list/components/employee-list-table-item.vue'; import { onMounted, ref } from 'vue'; + import { date, type QTableColumn } from 'quasar'; 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'; + import { useTimesheetStore } from 'src/stores/timesheet-store'; + import { employee_list_columns, type EmployeeProfile, type EmployeeListFilters } from 'src/modules/employee-list/models/employee-profile.models'; + import { animateFlip } from 'src/utils/table-grid-FLIP'; - const employee_list_api = useEmployeeListApi(); const employee_store = useEmployeeStore(); + const timesheet_store = useTimesheetStore(); const ui_store = useUiStore(); - const is_loading_list = ref(true); + const visible_columns = ref<(keyof EmployeeProfile)[]>(['first_name', 'email', 'company_name', 'supervisor_full_name', 'company_name', 'job_title', 'last_work_day']); - const filter = ref(""); + const table_grid_container = ref(null); - onMounted(async () => { - is_loading_list.value = true; - await employee_list_api.getEmployeeList(); - is_loading_list.value = false; + const filters = ref({ + search_bar_string: '', + hide_inactive_users: true, + }); + + const filterEmployeeRows = (rows: readonly EmployeeProfile[], terms: EmployeeListFilters, _cols: readonly QTableColumn[]): EmployeeProfile[] => { + let result = [...rows]; + + if (terms.hide_inactive_users) { + const now = new Date(); + result = result.filter(row => { + if (!row.last_work_day) return true; + const inactiveDate = date.extractDate(row.last_work_day, 'YYYY-MM-DD'); + const limit = new Date(inactiveDate); + limit.setDate(limit.getDate() + 14); + return limit >= now; + }); + } + + if (terms.search_bar_string.trim().length > 0) { + const searchTerms = terms.search_bar_string.split(' ').map(s => s.trim().toLowerCase()); + + result = result.filter(row => { + const row_values = Object.values(row).map(v => String(v ?? '').toLowerCase()); + const row_values_without_emails = row_values.filter(value => !value.includes('@')); + return searchTerms.every(term => + row_values_without_emails.some(value => value.includes(term)) + ); + }); + } + + animateFlip(table_grid_container); + + return result; + }; + + onMounted(() => { + table_grid_container.value = document.querySelector(".q-table__grid-content") as HTMLElement; })