Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
a7a1bf3
todolistwidget format and add interfact for assigned task
NavrajBal Oct 26, 2025
3143385
small formating fixes and adding of error handling
NavrajBal Oct 26, 2025
2e9b0e8
move node cron from devdependency to project
NavrajBal Oct 26, 2025
03ad959
fix linter errors in assignedTaskResolver
NavrajBal Oct 26, 2025
8ef11cd
updateLoginStreak formatting fix
NavrajBal Oct 26, 2025
be90e8b
evaluateBadge formatting fix
NavrajBal Oct 26, 2025
0916b34
badgeResolver formatting and linter fixes
NavrajBal Oct 26, 2025
c884807
createAssignedTask formatting fixes
NavrajBal Oct 26, 2025
8949484
Replace use of "for of" loop with Promise.all() in creatAssignedTask
NavrajBal Oct 26, 2025
06c11d2
participant resolver formatting and error handling
NavrajBal Oct 26, 2025
70e96c6
login resolver formatting fixes and switch from require to import for…
NavrajBal Oct 26, 2025
dd8e74d
add back old linter
applepie7864 Oct 28, 2025
fb0a2cf
used unused variables for error handling and fixed use of any type
NavrajBal Oct 28, 2025
3025aa2
graphql middleware typing fixes
NavrajBal Oct 28, 2025
cff7972
added reason to update marillac bucks temporary fix
NavrajBal Oct 28, 2025
1f97010
remove unused import
NavrajBal Oct 28, 2025
18ca189
disable linter for seed-snaplet, removed unused imports, and switched…
NavrajBal Oct 28, 2025
26d0f3d
Merge branch 'dev' into formatting-fixes
yanxue06 Oct 29, 2025
ddbcbb4
bump setup-node version
yanxue06 Oct 29, 2025
09c56f0
update yarn lock and resolve ts compilation errors
yanxue06 Oct 29, 2025
a8935c2
generate schema in CI
yanxue06 Oct 29, 2025
5bf5a01
always run lints regardless if past steps fail
yanxue06 Oct 29, 2025
d36a8e3
Fix linting errors in frontend utils and backend resolvers
yanxue06 Oct 29, 2025
75329cd
backend linting
yanxue06 Oct 29, 2025
89d2ec5
Fix linting errors in resolver files
yanxue06 Oct 29, 2025
2e4eb7e
backend linting
yanxue06 Oct 29, 2025
4227ad5
unused types
yanxue06 Oct 29, 2025
67ba2be
Merge branch 'dev' into formatting-fixes
yanxue06 Oct 31, 2025
2d8332a
use userAnnouncement for getAnnouncementsByParticipantId
NavrajBal Oct 31, 2025
eeb078e
frontend formating fixes
NavrajBal Oct 31, 2025
f1a8da5
Merge branch 'dev' into formatting-fixes
NavrajBal Oct 31, 2025
52d9227
quick prettier fixes in backend
NavrajBal Oct 31, 2025
451f3bb
Merge branch 'dev' into formatting-fixes
yanxue06 Nov 5, 2025
b949f5c
participant resolver formatting fix
NavrajBal Nov 5, 2025
34d7e0c
disable linter and type checking for reportresolver and sendreport email
NavrajBal Nov 5, 2025
65c58fa
fix type errors in generatedatareportscsv
NavrajBal Nov 5, 2025
194d4b3
mutation formatting fix
NavrajBal Nov 5, 2025
b435bab
disable eslint any check on component.ts
NavrajBal Nov 5, 2025
0830b16
formatting ran on all front end files
NavrajBal Nov 5, 2025
71556ae
various frontend formatting and unused imports warnings adressed
NavrajBal Nov 5, 2025
04f17bc
prettier warnings fixed
NavrajBal Nov 5, 2025
735d6d4
useref for prevpined annoucement expandedview
NavrajBal Nov 5, 2025
77756e7
Merge branch 'dev' into formatting-fixes
NavrajBal Nov 5, 2025
b29042e
prettier warning fixes
NavrajBal Nov 5, 2025
506812a
add generate eslint breakdown script
NavrajBal Nov 5, 2025
76e5e51
remove duplicates in resolvers.ts
NavrajBal Nov 5, 2025
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
7 changes: 6 additions & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ jobs:
run: |
cd backend && yarn install
cd ../frontend && yarn install


- name: Generate Prisma Client
run: |
cd backend
npx prisma generate

- name: Run backend tests
run: |
cd backend
Expand Down
61 changes: 61 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: Lint codebase

on:
push:
branches:
- dev
paths:
- "frontend/**"
- "backend/**"
pull_request:
branches:
- dev
paths:
- "frontend/**"
- "backend/**"

jobs:
run-lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Filter changed files
uses: dorny/paths-filter@v2
id: changes
with:
filters: |
frontend:
- "frontend/**"
backend:
- "backend/**"

- name: Set up Node.js
if: steps.changes.outputs.frontend == 'true' || steps.changes.outputs.backend == 'true'
uses: actions/setup-node@v4
with:
node-version: "18.18.2"
cache: "yarn"
cache-dependency-path: |
frontend/yarn.lock
backend/yarn.lock

- name: Install Node.js dependencies
if: steps.changes.outputs.frontend == 'true' || steps.changes.outputs.backend == 'true'
run: yarn --cwd ./frontend --prefer-offline && yarn --cwd ./backend --prefer-offline

- name: Generate Prisma Client
if: steps.changes.outputs.backend == 'true'
working-directory: ./backend
run: npx prisma generate

- name: Lint frontend
if: always() &&steps.changes.outputs.frontend == 'true'
working-directory: ./frontend
run: yarn lint

- name: Lint backend
if: always() &&steps.changes.outputs.backend == 'true'
working-directory: ./backend
run: yarn lint
19 changes: 13 additions & 6 deletions backend/crons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import expireAdminNotes from "./scripts/expireAdminNotes";
import expireAdminAnnouncements from "./scripts/expireAdminAnnouncements";
import expireLoginStreak from "./scripts/expireLoginStreak";
import createAssignedTasks from "./scripts/createAssignedTask";
import { sendWeeklyReports, sendMonthlyReports } from "./scripts/sendReportEmails";
import {
sendWeeklyReports,
sendMonthlyReports,
} from "./scripts/sendReportEmails";

cron.schedule("0 0 * * * *", async () => {
const res = await expireAdminNotes();
Expand Down Expand Up @@ -32,7 +35,7 @@ cron.schedule("0 0 0 * * *", async () => {
}
});

cron.schedule("0 0 * * 1", async () => {
cron.schedule("0 0 * * 1", async () => {
const res = await createAssignedTasks();
if (res) {
console.log("Created assigned tasks");
Expand All @@ -44,8 +47,10 @@ cron.schedule("0 0 * * 1", async () => {
// Weekly reports - every Monday at 9 AM
cron.schedule("0 9 * * 1", async () => {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] Starting weekly report generation and emailing...`);

console.log(
`[${timestamp}] Starting weekly report generation and emailing...`
);

const res = await sendWeeklyReports();
if (res) {
console.log(`[${timestamp}] Weekly reports sent successfully`);
Expand All @@ -57,8 +62,10 @@ cron.schedule("0 9 * * 1", async () => {
// Monthly reports - first day of month at 9 AM
cron.schedule("0 9 1 * *", async () => {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] Starting monthly report generation and emailing...`);

console.log(
`[${timestamp}] Starting monthly report generation and emailing...`
);

const res = await sendMonthlyReports();
if (res) {
console.log(`[${timestamp}] Monthly reports sent successfully`);
Expand Down
171 changes: 96 additions & 75 deletions backend/crons/scripts/createAssignedTask.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import {
TaskType,
RecurrenceFrequency,
TimeOption,
DayOfWeek,
} from "@prisma/client";
import prisma from "../../prisma";
import { TaskType, RecurrenceFrequency, TimeOption, DayOfWeek } from "@prisma/client";
import { formatDateTime, getToday } from "../../utils/formatDateTime";

async function createAssignedTasks(): Promise<boolean> {
try {
const participants = await prisma.participant.findMany({
where: {
OR: [{ departure_date: null }, { departure_date: { gt: getToday() } }],
}
},
});

const requiredTasks = await prisma.task.findMany({
where: { task_type: TaskType.REQUIRED },
})
});

const weekdayOffsets: { [key in DayOfWeek]: number } = {
MONDAY: 0,
Expand All @@ -24,86 +29,102 @@ async function createAssignedTasks(): Promise<boolean> {
SUNDAY: 6,
};

const mondayOfCurrentWeek = new Date();
const mondayOfCurrentWeek = new Date();

await Promise.all(
participants.map(async (participant) => {
return Promise.all(
requiredTasks.map(async (task) => {
if (
task.recurrence_preference ===
RecurrenceFrequency.EVERY_SELECTED_DAYS ||
task.recurrence_preference === RecurrenceFrequency.DAILY
) {
await Promise.all(
task.repeat_days.map(async (repeatDay) => {
const offset = weekdayOffsets[repeatDay];
const taskDate = new Date(mondayOfCurrentWeek);
taskDate.setDate(mondayOfCurrentWeek.getDate() + offset);
let startDate: Date;
let endDate: Date;
if (task.time_preference === TimeOption.SPECIFIC) {
startDate = new Date(taskDate);
endDate = new Date(taskDate);
if (task.start_time) {
const [hours, minutes] = task.start_time
.split(":")
.map(Number);
startDate.setHours(hours, minutes, 0, 0);
}
if (task.end_time) {
const [hours, minutes] = task.end_time
.split(":")
.map(Number);
endDate.setHours(hours, minutes, 0, 0);
}
} else {
startDate = new Date(taskDate);
startDate.setHours(0, 0, 0, 0);
endDate = new Date(taskDate);
endDate.setHours(23, 59, 0, 0);
}
const startDateString = formatDateTime(startDate, true);
const endDateString = formatDateTime(endDate, true);
await prisma.assignedTask.create({
data: {
participant_id: participant.participant_id,
task_name: task.task_name,
task_type: task.task_type,
start_date: startDateString,
end_date: endDateString,
marillac_bucks_addition: task.marillac_bucks_addition,
marillac_bucks_deduction: task.marillac_bucks_deduction,
comment: task.comment,
},
});
})
);
} else if (
task.recurrence_preference ===
RecurrenceFrequency.ANY_SELECTED_DAYS
) {
const firstDayOffset = weekdayOffsets[task.repeat_days[0]];
const lastDayOffset =
weekdayOffsets[task.repeat_days[task.repeat_days.length - 1]];

for (const participant of participants) {
for (const task of requiredTasks) {
if (task.recurrence_preference === RecurrenceFrequency.EVERY_SELECTED_DAYS ||
task.recurrence_preference === RecurrenceFrequency.DAILY) {
for (const repeatDay of task.repeat_days) {
const offset = weekdayOffsets[repeatDay];
const taskDate = new Date(mondayOfCurrentWeek);
taskDate.setDate(mondayOfCurrentWeek.getDate() + offset);
let startDate: Date;
let endDate: Date;
if (task.time_preference === TimeOption.SPECIFIC) {
startDate = new Date(taskDate);
endDate = new Date(taskDate);
if (task.start_time) {
const [hours, minutes] = task.start_time.split(":").map(Number);
startDate.setHours(hours, minutes, 0, 0);
}
if (task.end_time) {
const [hours, minutes] = task.end_time.split(":").map(Number);
endDate.setHours(hours, minutes, 0, 0);
}
} else {
startDate = new Date(taskDate);
const startDate = new Date(mondayOfCurrentWeek);
startDate.setDate(mondayOfCurrentWeek.getDate() + firstDayOffset);
startDate.setHours(0, 0, 0, 0);
endDate = new Date(taskDate);

const endDate = new Date(mondayOfCurrentWeek);
endDate.setDate(mondayOfCurrentWeek.getDate() + lastDayOffset);
endDate.setHours(23, 59, 0, 0);
}
const startDateString = formatDateTime(startDate, true);
const endDateString = formatDateTime(endDate, true);
await prisma.assignedTask.create({
data: {
participant_id: participant.participant_id,
task_name: task.task_name,
task_type: task.task_type,
start_date: startDateString,
end_date: endDateString,
marillac_bucks_addition: task.marillac_bucks_addition,
marillac_bucks_deduction: task.marillac_bucks_deduction,
comment: task.comment,
},
});
}
} else if (task.recurrence_preference === RecurrenceFrequency.ANY_SELECTED_DAYS) {

const firstDayOffset = weekdayOffsets[task.repeat_days[0]];
const lastDayOffset = weekdayOffsets[task.repeat_days[task.repeat_days.length - 1]];

const startDate = new Date(mondayOfCurrentWeek);
startDate.setDate(mondayOfCurrentWeek.getDate() + firstDayOffset);
startDate.setHours(0, 0, 0, 0);

const endDate = new Date(mondayOfCurrentWeek);
endDate.setDate(mondayOfCurrentWeek.getDate() + lastDayOffset);
endDate.setHours(23, 59, 0, 0);

const startDateString = formatDateTime(startDate, true);
const endDateString = formatDateTime(endDate, true);

await prisma.assignedTask.create({
data: {
participant_id: participant.participant_id,
task_name: task.task_name,
task_type: task.task_type,
start_date: startDateString,
end_date: endDateString,
marillac_bucks_addition: task.marillac_bucks_addition,
marillac_bucks_deduction: task.marillac_bucks_deduction,
comment: task.comment,
},
});
}
}
}
const startDateString = formatDateTime(startDate, true);
const endDateString = formatDateTime(endDate, true);

await prisma.assignedTask.create({
data: {
participant_id: participant.participant_id,
task_name: task.task_name,
task_type: task.task_type,
start_date: startDateString,
end_date: endDateString,
marillac_bucks_addition: task.marillac_bucks_addition,
marillac_bucks_deduction: task.marillac_bucks_deduction,
comment: task.comment,
},
});
}
})
);
})
);
return true;
} catch (err) {
console.error(err);
return false;
}
};
}

export default createAssignedTasks;
5 changes: 2 additions & 3 deletions backend/crons/scripts/generateDataReport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ interface LoginStats {
login_date: string;
}

interface DataReport {
export interface DataReport {
reportPeriod: string;
startDate: string;
endDate: string;
Expand All @@ -54,7 +54,7 @@ interface DataReport {
}

// Main function to generate a json for data report
async function generateDataReport(
export async function generateDataReport(
params: DataReportParams
): Promise<DataReport | null> {
try {
Expand Down Expand Up @@ -287,4 +287,3 @@ export async function generateMonthlyReport(): Promise<boolean> {

// Export the main function for generating a data report by default
export default generateDataReport;

20 changes: 10 additions & 10 deletions backend/crons/scripts/generateDataReportCSV.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import path from "path";
import fs from "fs/promises";
import generateDataReport from "./generateDataReport";
import { generateDataReport, DataReport } from "./generateDataReport";

// Convert JSON data to CSV format
function jsonToCsv(data: any[], headers: string[]): string {
if (data.length === 0) return headers.join(",") + "\n";
function jsonToCsv(data: object[], headers: string[]): string {
if (data.length === 0) return `${headers.join(",")}\n`;

const csvRows = [headers.join(",")];

for (const row of data) {
const rows = data.map((row) => {
const values = headers.map((header) => {
const value = row[header];
const value = (row as Record<string, unknown>)[header];
// Escape commas and quotes in CSV
if (
typeof value === "string" &&
Expand All @@ -20,14 +20,15 @@ function jsonToCsv(data: any[], headers: string[]): string {
}
return value ?? "";
});
csvRows.push(values.join(","));
}
return values.join(",");
});
csvRows.push(...rows);

return csvRows.join("\n") + "\n";
return `${csvRows.join("\n")}\n`;
}

// Generate a single comprehensive CSV with all data types
function generateComprehensiveCSV(report: any): string {
function generateComprehensiveCSV(report: DataReport): string {
const csvSections: string[] = [];

// Report header information
Expand Down Expand Up @@ -207,4 +208,3 @@ export async function generateMonthlyReportCSV(): Promise<string | null> {
return null;
}
}

Loading