Skip to content

Commit

Permalink
Merge pull request #135 from Code-Hammers/CHE-189/story/Create-Inacti…
Browse files Browse the repository at this point in the history
…vity-Alert-System

[CHE-189] Create Inactivity Alert System
  • Loading branch information
brok3turtl3 authored Jun 9, 2024
2 parents a0bd196 + 8078b75 commit f185cde
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 15 deletions.
65 changes: 64 additions & 1 deletion client/src/pages/ApplicationsPage/ApplicationsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react';
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
import { useAppSelector } from '../../app/hooks';
Expand All @@ -14,6 +14,7 @@ const ApplicationsPage = (): JSX.Element => {
async function fetchApplications() {
try {
const response = await axios.get(`/api/applications?user_id=${user?._id}`);

setApplications(response.data);
} catch (error) {
console.error('Error fetching applications:', error);
Expand All @@ -23,6 +24,37 @@ const ApplicationsPage = (): JSX.Element => {
fetchApplications();
}, []);

const calculateIsInactive = (application: IApplication) => {
const { last_updated, notification_period, notifications_paused } = application;
if (notifications_paused) return false;

const lastUpdatedDate = new Date(last_updated);
const notificationPeriodMs = (notification_period * 24 * 60 * 60 * 1000) / 60 / 60 / 24;
return new Date().getTime() - lastUpdatedDate.getTime() > notificationPeriodMs;
};

const handleTogglePause = async (id: number, pause: boolean) => {
try {
await axios.put(`/api/applications/${id}/pause-notifications`, { pause });
setApplications((prevApps) =>
prevApps.map((app) => (app.id === id ? { ...app, notifications_paused: pause } : app)),
);
} catch (error) {
console.error('Error updating notification pause:', error);
}
};

const handlePeriodChange = async (id: number, period: number) => {
try {
await axios.put(`/api/applications/${id}/notification-period`, { period });
setApplications((prevApps) =>
prevApps.map((app) => (app.id === id ? { ...app, notification_period: period } : app)),
);
} catch (error) {
console.error('Error updating notification period:', error);
}
};

return (
<div className="bg-gray-900 flex flex-col items-center justify-center min-h-screen p-4 pt-40 text-white">
<ApplicationDashboard />
Expand All @@ -42,6 +74,37 @@ const ApplicationsPage = (): JSX.Element => {
</div>
<div className="text-gray-400 text-sm ">Status: {application.status}</div>
<div className="text-gray-400 text-sm ">Notes: {application.general_notes}</div>
{calculateIsInactive(application) && (
<div className="text-red-500 text-sm font-bold">
This application needs attention!
</div>
)}
<div className="flex items-center mt-2">
<label className="mr-2 text-gray-400">Notification Period:</label>
<select
className="bg-gray-700 text-white p-1 rounded"
value={application.notification_period}
onChange={(e) => handlePeriodChange(application.id, parseInt(e.target.value))}
>
<option value={30}>30 seconds</option>
<option value={86400}>1 day</option>
<option value={259200}>3 days</option>
<option value={604800}>1 week</option>
<option value={2592000}>1 month</option>
</select>
</div>
<div className="flex items-center mt-2">
<label className="mr-2 text-gray-400">Notifications:</label>
<input
type="checkbox"
checked={!application.notifications_paused}
onChange={(e) => handleTogglePause(application.id, !e.target.checked)}
className="bg-gray-700 text-white p-1 rounded"
/>
<span className="ml-2 text-gray-400">
{application.notifications_paused ? 'Paused' : 'Active'}
</span>
</div>
<button
className="bg-blue-500 focus:outline-none focus:shadow-outline font-bold hover:bg-blue-700 mt-2 px-4 py-2 rounded text-white"
onClick={() => navigate(`/app/update-application/${application.id}`)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ const UpdateApplicationPage = (): JSX.Element => {

async function fetchApplication() {
try {
console.log('HITTT!!!!!');
const response = await axios.get(`/api/applications/${id}`);
const applicationData = response.data;
applicationData.date_applied = new Date(applicationData.date_applied)
Expand Down
3 changes: 3 additions & 0 deletions client/types/applications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ export interface IApplication {
company: string;
status: string;
general_notes: string;
last_updated: string;
notification_period: number;
notifications_paused: boolean;
}

export interface IApplicationFormData {
Expand Down
11 changes: 7 additions & 4 deletions scripts/sql_db_init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ CREATE TABLE applications (
quick_apply BOOLEAN NOT NULL,
date_applied TIMESTAMPTZ DEFAULT NOW(),
general_notes TEXT,
last_updated TIMESTAMPTZ DEFAULT NOW(),
notification_period INT DEFAULT 3,
notifications_paused BOOLEAN DEFAULT FALSE,
FOREIGN KEY (job_id) REFERENCES jobs(id),
FOREIGN KEY (status_id) REFERENCES statuses(id)
);
Expand All @@ -79,7 +82,7 @@ INSERT INTO jobs (title, company, location, description, url) VALUES
('Project Manager', 'Calabogie Zoo', 'Calabogie, ON', 'Developing new website', 'https://www.torontozoo.com');


INSERT INTO applications (job_id, status_id, user_id, quick_apply, date_applied, general_notes) VALUES
(1, 1, '6644c602515c654def9b2ae7', true, NOW(), 'Quick applied for Software Engineer at Dogs R Us.'),
(2, 1, '6644c768515c654def9b2b09', true, NOW(), 'Full CS style application.'),
(3, 2, '6644c7f7515c654def9b2b18', true, NOW(), 'Phone screen scheduled!');
INSERT INTO applications (job_id, status_id, user_id, quick_apply, date_applied, general_notes, last_updated, notification_period, notifications_paused) VALUES
(1, 1, '6644c602515c654def9b2ae7', true, NOW(), 'Quick applied for Software Engineer at Dogs R Us.', NOW(), 3, false),
(2, 1, '6644c768515c654def9b2b09', true, NOW(), 'Full CS style application.', NOW(), 3, false),
(3, 2, '6644c7f7515c654def9b2b18', true, NOW(), 'Phone screen scheduled!', NOW(), 3, false);
70 changes: 61 additions & 9 deletions server/controllers/applicationsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ const getAllApplications = async (req: Request, res: Response) => {
jobs.company,
jobs.title,
statuses.name AS status,
applications.general_notes
applications.general_notes,
applications.last_updated,
applications.notification_period,
applications.notifications_paused
FROM
applications
INNER JOIN jobs ON applications.job_id = jobs.id
Expand Down Expand Up @@ -69,8 +72,8 @@ const createApplication = async (req: Request, res: Response) => {
const job_id = jobResult.rows[0].id;

const applicationQuery = `
INSERT INTO applications (job_id, status_id, user_id, quick_apply, date_applied, general_notes)
VALUES ($1, $2, $3, $4, $5, $6)
INSERT INTO applications (job_id, status_id, user_id, quick_apply, date_applied, general_notes, last_updated)
VALUES ($1, $2, $3, $4, $5, $6, NOW())
RETURNING id
`;
const applicationValues = [
Expand Down Expand Up @@ -133,25 +136,36 @@ const getApplicationById = async (req: CustomRequest<{ id: string }>, res: Respo

const updateApplication = async (req: CustomRequest<{ id: string }>, res: Response) => {
const { id } = req.params;
if (!req.user || req.user.id !== id)
return res.status(401).json({ message: 'You are not authorized to retrieve those records' });

if (!req.user) {
return res.status(401).json({ message: 'You are not authorized to update this record' });
}

try {
const { id } = req.params;
const { job_id, status_id, user_id, quick_apply, date_applied, general_notes } = req.body;
const query = `
UPDATE applications
SET job_id = $1, status_id = $2, user_id = $3, quick_apply = $4, date_applied = $5, general_notes = $6
WHERE id = $7
SET job_id = $1, status_id = $2, user_id = $3, quick_apply = $4, date_applied = $5, general_notes = $6, last_updated = NOW()
WHERE id = $7 AND user_id = $8
RETURNING id
`;
await pool.query(query, [
const { rows } = await pool.query(query, [
job_id,
status_id,
user_id,
quick_apply,
date_applied,
general_notes,
id,
req.user.id,
]);

if (rows.length === 0) {
return res
.status(401)
.json({ message: 'You are not authorized to update this record or application not found' });
}

res.status(200).json({ message: 'Application updated successfully' });
} catch (error) {
console.error('Error updating application:', error);
Expand Down Expand Up @@ -190,11 +204,49 @@ const getAggregatedUserStats = async (req: CustomRequest<{ userId: string }>, re
}
};

const updateNotificationPeriod = async (req: Request, res: Response) => {
try {
const { id } = req.params;
const { period } = req.body;

const query = `
UPDATE applications
SET notification_period = $1
WHERE id = $2
`;
await pool.query(query, [period, id]);
res.status(200).json({ message: 'Notification period updated successfully' });
} catch (error) {
console.error('Error updating notification period:', error);
res.status(500).json({ message: 'Internal server error' });
}
};

const pauseNotifications = async (req: Request, res: Response) => {
try {
const { id } = req.params;
const { pause } = req.body;

const query = `
UPDATE applications
SET notifications_paused = $1
WHERE id = $2
`;
await pool.query(query, [pause, id]);
res.status(200).json({ message: `Notifications ${pause ? 'paused' : 'resumed'} successfully` });
} catch (error) {
console.error('Error pausing/resuming notifications:', error);
res.status(500).json({ message: 'Internal server error' });
}
};

export {
getAllApplications,
getStatuses,
createApplication,
updateApplication,
getApplicationById,
getAggregatedUserStats,
updateNotificationPeriod,
pauseNotifications,
};
4 changes: 4 additions & 0 deletions server/routes/applicationsRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
updateApplication,
getApplicationById,
getAggregatedUserStats,
updateNotificationPeriod,
pauseNotifications,
} from '../controllers/applicationsController';
import { protect } from '../middleware/authMiddleware';

Expand All @@ -17,5 +19,7 @@ router.get('/', protect, getAllApplications);
router.get('/:id', protect, getApplicationById);
router.post('/', protect, createApplication);
router.put('/:id', protect, updateApplication);
router.put('/:id/notification-period', protect, updateNotificationPeriod);
router.put('/:id/pause-notifications', protect, pauseNotifications);

export default router;

0 comments on commit f185cde

Please sign in to comment.