Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CHE-192] Create Filters #139

46 changes: 42 additions & 4 deletions client/src/pages/ApplicationsPage/ApplicationsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,32 @@ import { useAppSelector } from '../../app/hooks';
import { IApplication } from '../../../types/applications';
import ApplicationDashboard from '../../components/ApplicationDashBoard/ApplicationDashBoard';

const ApplicationsPage = (): JSX.Element => {
interface Params {
userId: string;
status?: string;
date?: string;
}

const ApplicationsPage = () => {
const navigate = useNavigate();
const [applications, setApplications] = useState<IApplication[]>([]);
seantokuzo marked this conversation as resolved.
Show resolved Hide resolved
const [showRejected, setShowRejected] = useState(true);
const [dateFilter, setDateFilter] = useState(false);
const user = useAppSelector((state) => state.user.userData);

useEffect(() => {
async function fetchApplications() {
try {
const response = await axios.get(`/api/applications?user_id=${user?._id}`);
const params: Params = { userId: user?._id ?? '' };
if (!showRejected) params.status = 'Rejected';

//TODO adjust time delay for production - Let user select dif times from dropdown?
if (dateFilter) {
const thirtySecondsAgo = new Date(Date.now() - 30 * 3000);
params.date = thirtySecondsAgo.toISOString();
}

const response = await axios.get(`/api/applications`, { params });

setApplications(response.data);
seantokuzo marked this conversation as resolved.
Show resolved Hide resolved
} catch (error) {
Expand All @@ -22,14 +39,17 @@ const ApplicationsPage = (): JSX.Element => {
}

fetchApplications();
}, []);
// TODO Not sure I want to keep this. here for testing
const intervalId = setInterval(fetchApplications, 5000);
seantokuzo marked this conversation as resolved.
Show resolved Hide resolved
return () => clearInterval(intervalId);
}, [showRejected, dateFilter]);

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;
const notificationPeriodMs = notification_period * 5000;
return new Date().getTime() - lastUpdatedDate.getTime() > notificationPeriodMs;
};

Expand Down Expand Up @@ -66,6 +86,24 @@ const ApplicationsPage = (): JSX.Element => {
Create New Application
</button>
<div className="max-w-4xl w-full bg-gray-800 p-6 rounded-lg shadow-lg">
<div className="mb-4">
<label className="block mb-2">
<input
type="checkbox"
checked={!showRejected}
onChange={() => setShowRejected(!showRejected)}
/>
Remove Rejected Applications
</label>
<label className="block">
<input
type="checkbox"
checked={dateFilter}
onChange={() => setDateFilter(!dateFilter)}
/>
Remove Applications Older than 30 Days
</label>
</div>
<ul className="divide-gray-700 divide-y ">
{applications.map((application) => (
<li key={application.id} className="py-4">
Expand Down
26 changes: 21 additions & 5 deletions client/src/pages/CreateApplicationPage/CreateApplicationPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { useAppSelector, useAppDispatch } from '../../app/hooks';
import { createApplication } from '../../features/applications/applicationSlice';
import { IStatus, IApplicationFormData } from '../../../types/applications';

const CreateApplicationPage = (): JSX.Element => {
const CreateApplicationPage = () => {
const user = useAppSelector((state) => state.user.userData);
const { status } = useAppSelector((state) => state.application);

const [statuses, setStatuses] = useState<IStatus[]>([]);
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [formData, setFormData] = useState<IApplicationFormData>({
title: '',
company: '',
Expand All @@ -18,7 +19,7 @@ const CreateApplicationPage = (): JSX.Element => {
status_id: 1,
user_id: user?._id || '',
quick_apply: false,
date_applied: new Date().toISOString().split('T')[0],
date_applied: new Date().toISOString().slice(0, 16),
general_notes: '',
job_id: 0,
});
Expand All @@ -40,8 +41,15 @@ const CreateApplicationPage = (): JSX.Element => {

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const selectedDate = new Date(formData.date_applied);
const currentDate = new Date();

if (selectedDate > currentDate) {
setErrorMessage("Sean S says time travel isn't a thing.");
return;
}
setErrorMessage(null);
dispatch(createApplication(formData));
console.log(formData);
};

const handleChange = (
Expand All @@ -54,6 +62,11 @@ const CreateApplicationPage = (): JSX.Element => {
...prevFormData,
[name]: checked,
}));
} else if (name === 'date_applied') {
setFormData((prevFormData) => ({
...prevFormData,
[name]: value,
}));
} else {
setFormData((prevFormData) => ({
...prevFormData,
Expand All @@ -66,6 +79,9 @@ const CreateApplicationPage = (): JSX.Element => {
<div className="pt-40 min-h-screen bg-gray-900 text-white flex flex-col items-center justify-center p-4">
<h1 className="text-4xl font-extrabold mb-4">Create Applications</h1>
<form className="w-full max-w-lg" onSubmit={handleSubmit}>
{errorMessage && (
<div className="bg-red-500 text-white p-2 rounded mb-4">{errorMessage}</div>
)}
<label className="block text-sm font-bold mb-2" htmlFor="title">
Job Title
<input
Expand Down Expand Up @@ -159,8 +175,8 @@ const CreateApplicationPage = (): JSX.Element => {
className="w-full p-2 rounded bg-gray-800 text-white"
id="date_applied"
name="date_applied"
type="date"
value={formData.date_applied}
type="datetime-local"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

including the time might be overkill

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely. It is like this so that in the current environment I can test functionally without waiting days. I need the seconds for now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good. should we prevent people from choosing a date/time in the future?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes we should! New code inc.

value={new Date(formData.date_applied).toISOString().slice(0, 16)}
onChange={handleChange}
required
/>
Expand Down
3 changes: 3 additions & 0 deletions docker-compose-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ services:
environment:
- NODE_ENV=development
- DATABASE_URL=postgres://codehammers:ch-dev@postgres:5432/ch-dev-database
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=ch-dev
- POSTGRES_DB=ch-dev-database
depends_on:
- postgres
postgres:
Expand Down
2 changes: 1 addition & 1 deletion docker-compose-lint.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: '3'
services:
dev:
image: codehammers/ch-dev-dep-v2:latest
image: codehammers/ch-tracker-dev:latest
container_name: ch-lint
ports:
- '3333:3333'
Expand Down
2 changes: 1 addition & 1 deletion docker-compose-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: '3'

services:
test:
image: codehammers/ch-dev-dep-v2:latest
image: codehammers/ch-tracker-dev:latest
container_name: ch-test
ports:
- '3000:3000'
Expand Down
33 changes: 22 additions & 11 deletions server/controllers/applicationsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ interface StatusCount {

const getAllApplications = async (req: Request, res: Response) => {
try {
const userId = req.query.user_id;
const { userId, status, date } = req.query;

const query = `
let query = `
SELECT
applications.id,
jobs.company,
jobs.title,
statuses.name AS status,
applications.general_notes,
applications.date_applied,
applications.last_updated,
applications.notification_period,
applications.notifications_paused
Expand All @@ -29,7 +30,22 @@ const getAllApplications = async (req: Request, res: Response) => {
applications.user_id = $1
`;

const { rows } = await pool.query(query, [userId]);
const queryParams = [userId];
let paramIndex = 2;

if (status) {
query += ` AND statuses.name != $${paramIndex}`;
queryParams.push(status);
paramIndex += 1;
}

if (date) {
query += ` AND applications.date_applied >= $${paramIndex}`;
queryParams.push(date);
}

const { rows } = await pool.query(query, queryParams);

res.json(rows);
} catch (error) {
console.error('Error fetching job applications:', error);
Expand Down Expand Up @@ -62,6 +78,8 @@ const createApplication = async (req: Request, res: Response) => {
user_id,
} = req.body;

const appliedDate = new Date(date_applied).toISOString();

const jobQuery = `
INSERT INTO jobs (title, company, location, description, url)
VALUES ($1, $2, $3, $4, $5)
Expand All @@ -76,14 +94,7 @@ const createApplication = async (req: Request, res: Response) => {
VALUES ($1, $2, $3, $4, $5, $6, NOW())
RETURNING id
`;
const applicationValues = [
job_id,
status_id,
user_id,
quick_apply,
date_applied,
general_notes,
];
const applicationValues = [job_id, status_id, user_id, quick_apply, appliedDate, general_notes];
const applicationResult = await pool.query(applicationQuery, applicationValues);

res.status(201).json({ id: applicationResult.rows[0].id });
Expand Down
Loading