diff --git a/src/layouts/components/main-layout-left-drawer.vue b/src/layouts/components/main-layout-left-drawer.vue
index 371a57a..498d4cf 100644
--- a/src/layouts/components/main-layout-left-drawer.vue
+++ b/src/layouts/components/main-layout-left-drawer.vue
@@ -13,7 +13,7 @@
const DRAWER_BUTTONS: { i18n_key: string, icon: string, route: RouteNames, required_module?: UserModuleAccess }[] = [
{ i18n_key: 'nav_bar.home', icon: "home", route: RouteNames.DASHBOARD, required_module: ModuleNames.DASHBOARD },
{ i18n_key: 'nav_bar.timesheet_approvals', icon: "event_available", route: RouteNames.TIMESHEET_APPROVALS, required_module: ModuleNames.TIMESHEETS_APPROVAL },
- { i18n_key: 'nav_bar.employee_list', icon: "groups", route: RouteNames.EMPLOYEE_LIST },
+ { i18n_key: 'nav_bar.employee_list', icon: "groups", route: RouteNames.EMPLOYEE_LIST, required_module: ModuleNames.EMPLOYEE_LIST },
{ i18n_key: 'nav_bar.timesheet', icon: "punch_clock", route: RouteNames.TIMESHEET, required_module: ModuleNames.TIMESHEETS },
{ i18n_key: 'nav_bar.profile', icon: "account_box", route: RouteNames.PROFILE, required_module: ModuleNames.PERSONAL_PROFILE },
{ i18n_key: 'nav_bar.help', icon: "contact_support", route: RouteNames.HELP },
@@ -26,7 +26,6 @@
const is_mini = ref(true);
const onClickDrawerPage = (page_name: RouteNames) => {
- ui_store.current_page = page_name;
is_mini.value = true;
router.push({ name: page_name }).catch(error => {
@@ -43,7 +42,7 @@
};
onMounted(() => {
- if(q.platform.is.mobile) {
+ if (q.platform.is.mobile) {
ui_store.is_left_drawer_open = false;
}
})
@@ -66,22 +65,26 @@
v-for="button, index in DRAWER_BUTTONS"
:key="index"
v-show="button.required_module ?? true"
- class="row items-center full-width q-py-sm cursor-pointer"
- :class="ui_store.current_page === button.route ? ($q.dark.isActive ? 'bg-green-10' : 'bg-green-2') : ''"
@click="onClickDrawerPage(button.route)"
>
-
-
- {{ $t(button.i18n_key) }}
+
+
+
+ {{ $t(button.i18n_key) }}
+
diff --git a/src/modules/auth/components/login-connection-panel.vue b/src/modules/auth/components/login-connection-panel.vue
index 1b68a96..f5b1513 100644
--- a/src/modules/auth/components/login-connection-panel.vue
+++ b/src/modules/auth/components/login-connection-panel.vue
@@ -1,25 +1,31 @@
-
-
-
+
+
-
+
diff --git a/src/modules/dashboard/components/main-carousel.vue b/src/modules/dashboard/components/main-carousel.vue
index 29a6ca7..5b44748 100644
--- a/src/modules/dashboard/components/main-carousel.vue
+++ b/src/modules/dashboard/components/main-carousel.vue
@@ -19,6 +19,7 @@ import { ref } from 'vue';
arrows
:autoplay="9001"
control-color="accent"
+ control-type="outline"
class="bg-dark full-width rounded-15 shadow-18"
>
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 7921f03..a94d980 100644
--- a/src/modules/employee-list/components/employee-list-table-item.vue
+++ b/src/modules/employee-list/components/employee-list-table-item.vue
@@ -2,20 +2,30 @@
setup
lang="ts"
>
+ import { useQuasar } from 'quasar';
import type { EmployeeProfile } from 'src/modules/employee-list/models/employee-profile.models';
+import { ref } from 'vue';
- // const getEmployeeAvatar = (first_name: string, last_name: string) => {
- // // add logic here to see if user has an avatar image and return that instead of initials
- // return first_name.charAt(0) + last_name.charAt(0);
- // };
+ const q = useQuasar();
+ const is_mouseover = ref(false);
- const { row, index = -1 } = defineProps<{
+ const { row, index = -1, isManagement = false } = defineProps<{
row: EmployeeProfile
index?: number
+ isManagement?: boolean;
}>()
- const emit = defineEmits<{
+
+ defineEmits<{
onProfileClick: [email: string]
}>();
+
+ const getItemStyle = (): string => {
+ const active_style = row.last_work_day === null ? '' : 'opacity: 0.6;';
+ const dark_style = q.dark.isActive ? 'border: 2px solid var(--q-accent);' : '';
+ const hover_style = isManagement ? (is_mouseover.value ? `transform: scale(1.1); z-index: 2;` :'transform: scale(1) skew(0)') : '';
+
+ return `${active_style} ${dark_style} ${hover_style}`;
+ }
@@ -24,10 +34,13 @@
:style="`animation-delay: ${index / 25}s;`"
>
-
\ No newline at end of file
+
+
+
\ 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 d442f1a..b612718 100644
--- a/src/modules/employee-list/components/employee-list-table.vue
+++ b/src/modules/employee-list/components/employee-list-table.vue
@@ -7,13 +7,18 @@
import { onMounted, ref } from 'vue';
import { date, type QTableColumn } from 'quasar';
import { useUiStore } from 'src/stores/ui-store';
+ import { useAuthStore } from 'src/stores/auth-store';
import { useEmployeeStore } from 'src/stores/employee-store';
import { useTimesheetStore } from 'src/stores/timesheet-store';
import { employee_list_columns, type EmployeeProfile, type EmployeeListFilters } from 'src/modules/employee-list/models/employee-profile.models';
+ const ui_store = useUiStore();
+ const auth_store = useAuthStore();
const employee_store = useEmployeeStore();
const timesheet_store = useTimesheetStore();
- const ui_store = useUiStore();
+
+ const is_management = auth_store.user?.user_module_access.includes('employee_management') ?? false;
+
const visible_columns = ref<(keyof EmployeeProfile)[]>(['first_name', 'email', 'job_title', 'last_work_day']);
const table_grid_container = ref(null);
@@ -88,6 +93,7 @@
@@ -177,7 +183,8 @@
:key="props.rowIndex"
:row="props.row"
:index="props.rowIndex"
- @on-profile-click="employee_store.openAddModifyDialog"
+ :is-management="is_management"
+ @on-profile-click="is_management ? employee_store.openAddModifyDialog : ''"
/>
@@ -185,7 +192,7 @@
{{ scope.opt.label }}
+
+
+
+
+ {{ shift.is_remote ? $t('timesheet.shift.types.REMOTE') :
+ $t('timesheet.shift.types.OFFICE') }}
+
+
+
diff --git a/src/modules/timesheet-approval/components/overview-list.vue b/src/modules/timesheet-approval/components/overview-list.vue
index b593d34..2f2d955 100644
--- a/src/modules/timesheet-approval/components/overview-list.vue
+++ b/src/modules/timesheet-approval/components/overview-list.vue
@@ -40,14 +40,13 @@
}>();
const is_showing_filters = ref(false);
- const search_string = ref('');
const overview_rows = computed(() => timesheet_store.pay_period_overviews.filter(overview => overview));
const overview_filters = ref({
is_showing_inactive: false,
is_showing_team_only: false,
supervisors: [],
- name_search_string: search_string.value,
+ name_search_string: '',
});
const onClickedDetails = async (row: TimesheetApprovalOverview) => {
@@ -106,7 +105,7 @@
:no-data-label="$t('shared.error.no_data_found')"
:no-results-label="$t('shared.error.no_search_results')"
:loading-label="$t('shared.label.loading')"
- :style="`max-height: ${maxHeight}px;`"
+ :style="overview_rows.length > 0 ? `max-height: ${maxHeight - (timesheet_store.is_approval_grid_mode ? 0 : 20)}px;` : ''"
@row-click="(_evt, row: TimesheetApprovalOverview) => onClickedDetails(row)"
>
@@ -158,7 +157,7 @@
/>
@@ -279,7 +278,7 @@
-
+
();
const { day, dense = false, approved = false } = defineProps<{
@@ -63,7 +65,7 @@ import { isShiftOverlap } from 'src/modules/timesheets/utils/shift.util';
leave-active-class="animated zoomOut fast"
>
- {
- if (currentDayComponent.value && timesheet_page.value) {
+ if (currentDayComponent.value && timesheet_page.value && q.platform.is.mobile) {
console.log('setting scroll position to offsetTop of currentDayComponent: ', currentDayComponent.value[0]!.offsetTop);
timesheet_page.value.setScrollPosition('vertical', currentDayComponent.value[0]!.offsetTop, 800);
return;
@@ -78,7 +79,7 @@
@@ -91,7 +92,7 @@
>
@@ -123,7 +124,7 @@
leave-active-class="animated fadeOutUp"
>
-
{{ $t('timesheet.page_header') }}
+
{{ $t('timesheet.page_header') }}
@@ -100,9 +103,12 @@
-
+
-
+
{
const saveShiftChanges = async () => {
timesheet_store.is_loading = true;
- const create_success = await shift_store.createNewShifts();
const update_success = await shift_store.updateShifts();
+ const create_success = await shift_store.createNewShifts();
if (create_success || update_success){
await timesheet_store.getTimesheetsByOptionalEmployeeEmail(auth_store.user?.email ?? '');
diff --git a/src/modules/timesheets/composables/use-timesheet-api.ts b/src/modules/timesheets/composables/use-timesheet-api.ts
index dd3cd6e..33eb159 100644
--- a/src/modules/timesheets/composables/use-timesheet-api.ts
+++ b/src/modules/timesheets/composables/use-timesheet-api.ts
@@ -5,6 +5,7 @@ export const useTimesheetApi = () => {
const timesheet_store = useTimesheetStore();
const getTimesheetsByDate = async (date_string: string, employee_email?: string) => {
+ timesheet_store.timesheets = [];
timesheet_store.is_loading = true;
const success = await timesheet_store.getPayPeriodByDateOrYearAndNumber(date_string);
diff --git a/src/modules/timesheets/models/timesheet.models.ts b/src/modules/timesheets/models/timesheet.models.ts
index 9e06725..e293813 100644
--- a/src/modules/timesheets/models/timesheet.models.ts
+++ b/src/modules/timesheets/models/timesheet.models.ts
@@ -5,6 +5,7 @@ export const TIME_FORMAT_PATTERN = /^(\d{2}:\d{2})?$/;
export const DATE_FORMAT_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
export interface TimesheetResponse {
+ has_preset_schedule: boolean;
employee_fullname: string;
timesheets: Timesheet[];
}
@@ -41,79 +42,4 @@ export interface TotalExpenses {
per_diem: number;
on_call: number;
mileage: number;
-}
-
-// export const test_timesheets: Timesheet[] = [
-// {
-// timehsid: 1,
-// is_approved: false,
-// weekly_hours: { regular: 8, evening: 0, emergency: 0, overtime: 0, vacation: 0, holiday: 0, sick: 0, absent: 0 },
-// weekly_expenses: { expenses: 15.5, mileage: 0 },
-// days: [
-// {
-// date: '2025-10-18',
-// daily_hours: { regular: 8, evening: 0, emergency: 0, overtime: 0, vacation: 0, holiday: 0, sick: 0, absent: 0 },
-// daily_expenses: { expenses: 15.5, mileage: 0 },
-// shifts: [
-// { id: 101, date: '2025-01-06', type: 'REGULAR', start_time: '08:00', end_time: '12:00', comment: 'blah', is_approved: false, is_remote: false, },
-// { id: 102, date: '2025-01-06', type: 'REGULAR', start_time: '13:00', end_time: '17:00', comment: undefined, is_approved: false, is_remote: false, },
-// ],
-// expenses: [
-// { id: 201, date: '2025-01-06', type: 'EXPENSES', amount: 15.5, comment: 'Lunch receipt', is_approved: false, },
-// ],
-// },
-// ],
-// },
-// {
-// id: 2,
-// is_approved: true,
-// weekly_hours: {
-// regular: 0,
-// evening: 0,
-// emergency: 0,
-// overtime: 8,
-// vacation: 0,
-// holiday: 0,
-// sick: 0,
-// absent: 0,
-// },
-// weekly_expenses: {
-// expenses: 0,
-// mileage: 32.4,
-// },
-// days: [
-// {
-// date: '2025-10-27',
-// daily_hours: {
-// regular: 0,
-// evening: 0,
-// emergency: 0,
-// overtime: 8,
-// vacation: 0,
-// holiday: 0,
-// sick: 0,
-// absent: 0,
-// },
-// daily_expenses: {
-// expenses: 0,
-// mileage: 32.4,
-// },
-// shifts: [
-// { id: 101, date: '2025-10-27', type: 'REGULAR', start_time: '08:00', end_time: '12:00', comment: undefined, is_approved: false, is_remote: false, },
-// { id: 102, date: '2025-10-27', type: 'REGULAR', start_time: '13:00', end_time: '17:00', comment: undefined, is_approved: false, is_remote: false, },
-// ],
-// expenses: [
-// {
-// id: 202,
-// date: '2025-10-27',
-// type: 'MILEAGE',
-// amount: 0,
-// mileage: 32.4,
-// comment: 'Travel to client site',
-// is_approved: true,
-// },
-// ],
-// },
-// ],
-// },
-// ];
+}
\ No newline at end of file
diff --git a/src/pages/dashboard-page.vue b/src/pages/dashboard-page.vue
index a65a8b4..f31d7a2 100644
--- a/src/pages/dashboard-page.vue
+++ b/src/pages/dashboard-page.vue
@@ -62,7 +62,7 @@