From 2e32a9fcfd076e722d0d729d12e30943db279cb1 Mon Sep 17 00:00:00 2001 From: sammyfischer Date: Mon, 9 Jun 2025 15:00:33 -0400 Subject: [PATCH 1/5] POR-3192: create new audits page layout and add types (#706) --- src/router.js | 4 +- src/shared/models/audits/notifications.js | 58 +++++ src/views/AuditsV2.vue | 257 ++++++++++++++++++++++ 3 files changed, 317 insertions(+), 2 deletions(-) create mode 100644 src/shared/models/audits/notifications.js create mode 100644 src/views/AuditsV2.vue diff --git a/src/router.js b/src/router.js index b22356d58..d45ebbee4 100644 --- a/src/router.js +++ b/src/router.js @@ -7,7 +7,7 @@ import PTOCashOuts from '@/views/PTOCashOuts.vue'; import Employees from '@/views/Employees.vue'; import Employee from '@/views/Employee.vue'; import StatsDashboard from '@/views/StatsDashboard.vue'; -import Audits from '@/views/Audits.vue'; +import AuditsV2 from '@/views/AuditsV2.vue'; import Expenses from '@/views/MyExpenses.vue'; import Help from '@/views/Help.vue'; import Reports from '@/views/Reports.vue'; @@ -67,7 +67,7 @@ const router = createRouter({ { path: '/audits', name: 'audits', - component: Audits, + component: AuditsV2, beforeEnter: multiguard([requireAuth, isAdminOrManager]) }, { diff --git a/src/shared/models/audits/notifications.js b/src/shared/models/audits/notifications.js new file mode 100644 index 000000000..b6caecb06 --- /dev/null +++ b/src/shared/models/audits/notifications.js @@ -0,0 +1,58 @@ +/** + * Model for the notification_reason type in the audits database + */ +// eslint-disable-next-line no-unused-vars +export const NotificationReason = Object.freeze({ + EXPENSE_REVISAL_REQUEST: 'expense_revisal_request', + EXPENSE_REJECTION: 'expense_rejection', + WEEKLY_TIME_REMINDER: 'weekly_timesheet_reminder', + MONTHLY_TIME_REMINDER: 'montly_timesheet_reminder', + TRAINING_HOUR_EXCHANGE: 'training_hour_exchange', + HIGH_FIVE: 'high_five' +}); + +/** + * Model for a notification in the audits database + */ +// eslint-disable-next-line no-unused-vars +export class Notification { + /** + * Creates a new model of a notificaiton + * @param {number} id Database ID + * @param {Date} createdAt When the notification was sent + * @param {string} receiverId The UUID of the employee that received the notification + * @param {string} sentTo Where the notification was sent (phone number, address, etc.) + * @param {NotificationReason} reason The reason the notification was sent (i.e. type of notification) + */ + constructor(id, createdAt, receiverId, sentTo, reason) { + /** + * The database id for this notification + * @type {number} + */ + this.id = id; + + /** + * The time this notification was sent + * @type {Date} + */ + this.createdAt = createdAt; + + /** + * The UUID of the employee who received the notification + * @type {string} + */ + this.receiverId = receiverId; + + /** + * The email or phone number to which this was sent + * @type {string} + */ + this.sentTo = sentTo; + + /** + * The reason/purpose of the notification + * @type {NotificationReason} + */ + this.reason = reason; + } +} diff --git a/src/views/AuditsV2.vue b/src/views/AuditsV2.vue new file mode 100644 index 000000000..7bce5ea27 --- /dev/null +++ b/src/views/AuditsV2.vue @@ -0,0 +1,257 @@ + + + + + From c7c29ed5fac8bafcf0c43de1105b34bcd145b2ca Mon Sep 17 00:00:00 2001 From: sammyfischer Date: Thu, 12 Jun 2025 09:49:11 -0400 Subject: [PATCH 2/5] POR-3202: load audit data from back end (#709) * POR-3202: improved reactive filtering on audits page * POR-3202: fetch audit data from back end * POR-3202: update store employees when they don't exist yet --- src/components/shared/DatePicker.vue | 3 + src/shared/api.js | 20 +- src/shared/employeeUtils.js | 2 +- src/shared/models/audits/audts.js | 46 +++ src/shared/models/audits/notifications.js | 12 +- src/views/AuditsV2.vue | 329 ++++++++++++++++++---- 6 files changed, 344 insertions(+), 68 deletions(-) create mode 100644 src/shared/models/audits/audts.js diff --git a/src/components/shared/DatePicker.vue b/src/components/shared/DatePicker.vue index 61b4bb2af..f2951024f 100644 --- a/src/components/shared/DatePicker.vue +++ b/src/components/shared/DatePicker.vue @@ -31,6 +31,7 @@ v-model="formattedModel" :multiple="multiple" :label="label" + :density="density" :disabled="disabled" readonly :clearable="clearable" @@ -52,6 +53,7 @@ :hint="hint ?? defaults.hint" :prepend-inner-icon="icon" :variant="variant" + :density="density" :hide-details="hideDetails" :disabled="disabled" :rules="rules" @@ -79,6 +81,7 @@ const props = defineProps({ adjacentDays: { type: Boolean, default: false }, hideDetails: { type: Boolean, default: false }, disabled: { type: Boolean, default: false }, + density: { type: String, default: 'default' }, // LOGIC/CONFIG rules: { type: Array, default: () => [] }, diff --git a/src/shared/api.js b/src/shared/api.js index 5821e89d3..9a5ecd4b5 100644 --- a/src/shared/api.js +++ b/src/shared/api.js @@ -1,6 +1,10 @@ import axios from 'axios'; import { API_CONFIG } from './apiVariables'; import { getAccessToken } from '@/utils/auth'; +// eslint-disable-next-line no-unused-vars +import { AuditRequestFilters } from './models/audits/audts'; + +// routes const EXPENSE_TYPES = 'expense-types'; const EXPENSES = 'expenses'; const EMPLOYEES = 'employees'; @@ -10,12 +14,13 @@ const UTILITY = 'utility'; const TIMESHEETS = 'timesheets'; const BASECAMP = 'basecamp'; const GOOGLE_MAPS = 'googleMaps'; -const AUDIT = 'audits'; +const AUDIT = 'auditsV2'; const RESUME = 'resume'; const CONTRACTS = 'contracts'; const HIGH_FIVES = 'highFives'; const PTO_CASH_OUTS = 'ptoCashOuts'; const TAGS = 'tags'; + const API_HOSTNAME = API_CONFIG.apiHostname; const API_PORT = API_CONFIG.apiPort; const PORT = API_PORT === '443' ? '' : `:${API_PORT}`; @@ -171,15 +176,12 @@ async function getAllEvents() { } // getAllEvents /** - * gets all the audits for a specific type in a specific range - * - * @param type - the type of the audit - * @param startDate - the start date of the wanted range - * @param endDate - the end date of the wanted range - * @return - the audit data + * Gets audits with specified filters + * @param {AuditRequestFilters?} filters Filters to query specific audits + * @returns {*} The api response or error */ -async function getAudits(type, startDate, endDate) { - return await execute('get', `/${AUDIT}/${type}/${startDate}/${endDate}`); +async function getAudits(filters) { + return await execute('get', `/${AUDIT}`, filters); } // getAudits /** diff --git a/src/shared/employeeUtils.js b/src/shared/employeeUtils.js index 4087ac508..6f2d2f051 100644 --- a/src/shared/employeeUtils.js +++ b/src/shared/employeeUtils.js @@ -145,7 +145,7 @@ export function nicknameAndLastName(employee) { * Gets an employee object based on the employee ID parameter * @param {Number} employeeId - employee ID * @param {Array} employees - list of employees - * @returns Employee object + * @returns {import('./models/employeeTypes').Employee} The employee */ export function getEmployeeByID(employeeId, employees) { return employees.find((e) => e.id == employeeId); diff --git a/src/shared/models/audits/audts.js b/src/shared/models/audits/audts.js new file mode 100644 index 000000000..8af122c70 --- /dev/null +++ b/src/shared/models/audits/audts.js @@ -0,0 +1,46 @@ +// eslint-disable-next-line no-unused-vars +import { NotificationReason } from './notifications'; + +/** + * Enum for the type of audit + * @readonly + * @enum {string} + */ +export const AuditType = { + CRUD: 'crud', + LOGIN: 'login', + NOTIFICATION: 'notification', + ERROR: 'error' +}; + +export class AuditRequestFilters { + constructor(properties) { + if (!properties) return; + + /** + * The types of audits to include in the filter + * @type AuditType[] + */ + this.types = properties?.types; + + /** + * The uuid of the employee who caused the audit + * @type {string} + */ + this.actor = properties?.actor; + + /** + * The uuid of the employee whose data was changed, or (if the audit is a notification) received the notification + * @type {string} + */ + this.receiver = properties?.receiver; + + /** @type Date */ + this.startDate = properties?.startDate; + /** @type Date */ + this.endDate = properties?.endDate; + + /** @type NotificationReason */ + this.notifReason = properties?.notifReason; + } +} diff --git a/src/shared/models/audits/notifications.js b/src/shared/models/audits/notifications.js index b6caecb06..f85f62c06 100644 --- a/src/shared/models/audits/notifications.js +++ b/src/shared/models/audits/notifications.js @@ -1,20 +1,20 @@ /** - * Model for the notification_reason type in the audits database + * Enum for the notification_reason type in the audits database + * @readonly + * @enum {string} */ -// eslint-disable-next-line no-unused-vars -export const NotificationReason = Object.freeze({ +export const NotificationReason = { EXPENSE_REVISAL_REQUEST: 'expense_revisal_request', EXPENSE_REJECTION: 'expense_rejection', WEEKLY_TIME_REMINDER: 'weekly_timesheet_reminder', - MONTHLY_TIME_REMINDER: 'montly_timesheet_reminder', + MONTHLY_TIME_REMINDER: 'monthly_timesheet_reminder', TRAINING_HOUR_EXCHANGE: 'training_hour_exchange', HIGH_FIVE: 'high_five' -}); +}; /** * Model for a notification in the audits database */ -// eslint-disable-next-line no-unused-vars export class Notification { /** * Creates a new model of a notificaiton diff --git a/src/views/AuditsV2.vue b/src/views/AuditsV2.vue index 7bce5ea27..7cad59575 100644 --- a/src/views/AuditsV2.vue +++ b/src/views/AuditsV2.vue @@ -9,25 +9,72 @@ - This contains the search filters -

Type:

+ General filters + + + +
+ + + +
+
- Other settings specific to audit type + {{ filters.auditType }} filters
+ + -
+ +
+ Search
@@ -56,9 +103,7 @@

Audits

- Loads some recent audits by default, otherwise contains search results - - + @@ -69,84 +114,254 @@ diff --git a/src/views/EmployeeHistory.vue b/src/views/EmployeeHistory.vue new file mode 100644 index 000000000..6da67d988 --- /dev/null +++ b/src/views/EmployeeHistory.vue @@ -0,0 +1,284 @@ + + + + + diff --git a/src/views/ExpenseHistory.vue b/src/views/ExpenseHistory.vue new file mode 100644 index 000000000..d26b71f77 --- /dev/null +++ b/src/views/ExpenseHistory.vue @@ -0,0 +1,283 @@ + + + + + From 3645e395669bee4adc766872a471d4728834d1d7 Mon Sep 17 00:00:00 2001 From: maxvincex <168754332+maxvincex@users.noreply.github.com> Date: Wed, 6 Aug 2025 08:53:30 -0400 Subject: [PATCH 4/5] POR 3217 - Added functionality to timeline view (#740) * saving changes * timeline view is in! --------- Co-authored-by: Max D --- src/shared/api.js | 20 ++- src/views/AuditsV2.vue | 2 +- src/views/ExpenseHistory.vue | 237 ++++++++++++++++++----------------- 3 files changed, 137 insertions(+), 122 deletions(-) diff --git a/src/shared/api.js b/src/shared/api.js index d50c7f71f..1945ae9fe 100644 --- a/src/shared/api.js +++ b/src/shared/api.js @@ -175,13 +175,22 @@ async function getAllEvents() { } // getAllEvents /** - * Gets audits with specified filters + * Gets notification audits with specified filters * @param {AuditRequestFilters?} filters Filters to query specific audits * @returns {*} The api response or error */ -async function getAudits(filters) { - return await execute('get', `/${AUDIT}`, filters); -} // getAudits +async function getNotificationAudits(filters) { + return await execute('get', `/${AUDIT}/notification`, filters); +} // getNotificationAudits + +/** + * Gets CRUD audits with specified filters + * @param {AuditRequestFilters?} filters Filters to query specific audits + * @returns {*} The api response or error + */ +async function getCrudAudits(filters) { + return await execute('get', `/${AUDIT}/crud`, filters); +} // getCrudAudits /** * gets all items from a specific route @@ -527,7 +536,8 @@ export default { getAllEvents, getAllExpenseTypeExpenses, getAttachment, - getAudits, + getNotificationAudits, + getCrudAudits, getBasecampAvatars, getBasecampCampfires, getCity, diff --git a/src/views/AuditsV2.vue b/src/views/AuditsV2.vue index aaa159640..65cc4c009 100644 --- a/src/views/AuditsV2.vue +++ b/src/views/AuditsV2.vue @@ -237,7 +237,7 @@ async function query() { // break; // } - const res = await api.getAudits({ + const res = await api.getNotificationAudits({ startDate: filters.startDate, endDate: filters.endDate, notifReason: notifTypeMap(filters.notifType) diff --git a/src/views/ExpenseHistory.vue b/src/views/ExpenseHistory.vue index d26b71f77..cfa9a5ac4 100644 --- a/src/views/ExpenseHistory.vue +++ b/src/views/ExpenseHistory.vue @@ -7,14 +7,14 @@

Expense History

- - + + -
{{ data.date }}
+
{{ audit.date }}