Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

echo "✅ Husky pre-commit is running..."
npx lint-staged --allow-empty

Expand Down
1 change: 0 additions & 1 deletion src/controllers/lbdashboard/bookingsController.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const paypal = require('@paypal/checkout-server-sdk');

const nodemailer = require('nodemailer');
const Joi = require('joi');
require('dotenv').config();
Expand Down
3 changes: 3 additions & 0 deletions src/controllers/timeEntryController.js
Original file line number Diff line number Diff line change
Expand Up @@ -1558,6 +1558,9 @@ const timeEntrycontroller = function (TimeEntry) {
* recalculate the hoursByCategory for all users and update the field
*/
const recalculateHoursByCategoryAllUsers = async function (taskId) {
if (mongoose.connection.readyState === 0) {
return;
}
const session = await mongoose.startSession();
session.startTransaction();

Expand Down
42 changes: 39 additions & 3 deletions src/cronjobs/userProfileJobs.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ const moment = require('moment-timezone');
const userhelper = require('../helpers/userHelper')();

const userProfileJobs = () => {
/* eslint-disable no-unused-vars */
// 1: Minute (0-59)
// 2: Hour (0-23)
// 3: Day of Month (1-31)
// 4: Month (0-11)
// 5: Day of Week (0-6) (0 is Sunday)
const allUserProfileJobs = new CronJob(
// '* * * * *', // Comment out for testing. Run Every minute.
'1 0 * * 0', // Every Sunday, 1 minute past midnight.

'0 0 * * 0', // Every Sunday, 12 AM.
async () => {
const SUNDAY = 0; // will change back to 0 after fix
const SUNDAY = 0;
if (moment().tz('America/Los_Angeles').day() === SUNDAY) {
await userhelper.getProfileImagesFromWebsite();
await userhelper.assignBlueSquareForTimeNotMet();
Expand All @@ -24,7 +29,37 @@ const userProfileJobs = () => {
'America/Los_Angeles',
);

// 1: Minute (0-59)
// 2: Hour (0-23)
// 3: Day of Month (1-31)
// 4: Month (0-11)
// 5: Day of Week (0-6) (0 is Sunday)
const summaryNotSubmittedJobs = new CronJob(
'0 4 * * 0', // Every Sunday at 4AM
async () => {
try {
console.log(
'Starting summaryNotSubmittedJobs at:',
moment().tz('America/Los_Angeles').format(),
);
await userhelper.completeHoursAndMissedSummary();
await userhelper.inCompleteHoursEmailFunction();
await userhelper.weeklyBlueSquareReminderFunction();
} catch (error) {
console.error('Error during summaryNotSubmittedJobs:', error);
}
},
null,
false,
'America/Los_Angeles',
);

// Job to run every day, 1 minute past midnight to deactivate the user
// 1: Minute (0-59)
// 2: Hour (0-23)
// 3: Day of Month (1-31)
// 4: Month (0-11)
// 5: Day of Week (0-6) (0 is Sunday)
const dailyUserDeactivateJobs = new CronJob(
// '* * * * *', // Comment out for testing. Run Every minute.
'1 0 * * *', // Every day, 1 minute past midnight
Expand All @@ -38,5 +73,6 @@ const userProfileJobs = () => {
);
allUserProfileJobs.start();
dailyUserDeactivateJobs.start();
summaryNotSubmittedJobs.start();
};
module.exports = userProfileJobs;
106 changes: 106 additions & 0 deletions src/helpers/__tests__/getInfringementEmailBody.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
const userHelperFactory = require('../userHelper');

const { getInfringementEmailBody } = userHelperFactory();

describe('getInfringementEmailBody', () => {
const baseAdministrativeContent = {
startDate: '1-1-2023',
role: 'Core Team',
userTitle: 'Volunteer',
historyInfringements: 'History snapshot',
};

it('returns default messaging when timeRemaining is undefined', () => {
const infringement = {
date: '2025-01-05',
description: 'Should not be used because the time off body is provided',
};

const result = getInfringementEmailBody(
'Jane',
'Doe',
infringement,
3,
undefined,
null,
'<span>Approved time off</span>',
baseAdministrativeContent,
);

expect(result).toContain('This action usually includes removal from our team though');
expect(result).toContain('This is your <b>3rd</b> blue square of 5.');
expect(result).toContain('<span>Approved time off</span>');
});

it('highlights critical phrases and calculates owed hours when time remaining exists', () => {
const infringement = {
date: '2025-02-09',
description:
'System auto-assigned infringement for two reasons: not meeting weekly volunteer time commitment as well as not submitting a weekly summary. In the week starting Sunday details. You logged 4 hours.',
};

const result = getInfringementEmailBody(
'John',
'Smith',
infringement,
6,
4,
1,
undefined,
baseAdministrativeContent,
10,
);

expect(result).toContain(
'<p><b>Total Infringements:</b> This is your <b>6th</b> blue square of 5 and that means you have 1 hour(s) added',
);
expect(result).toContain(
'<b>not meeting weekly volunteer time commitment as well as not submitting a weekly summary</b>',
);
expect(result).toContain('logged <b>4 hours</b>');
expect(result).toContain('Please complete ALL owed time this week (15 hours)');
});

it('wraps plain descriptions in bold tags when no keywords match', () => {
const infringement = {
date: '2025-03-01',
description: 'Missed posting weekly update',
};

const result = getInfringementEmailBody(
'Alex',
'Lee',
infringement,
2,
1,
0,
undefined,
baseAdministrativeContent,
5,
);

expect(result).toContain('<b>Missed posting weekly update<b>');
});

it('formats editing infringement details to emphasize the edit count', () => {
const infringement = {
date: '2025-04-07',
description:
'System auto-assigned infringement for editing your time entries <3> times. Additional supporting details.',
};

const result = getInfringementEmailBody(
'Evan',
'Taylor',
infringement,
6,
2,
0,
undefined,
baseAdministrativeContent,
8,
);

expect(result).toContain('time entries <b>3 times</b>');
});
});
Loading
Loading