Skip to content

Commit

Permalink
Initial implementation of guidance records
Browse files Browse the repository at this point in the history
  • Loading branch information
chrsrns committed Nov 20, 2023
1 parent 7777ac6 commit f28d687
Show file tree
Hide file tree
Showing 9 changed files with 532 additions and 9 deletions.
3 changes: 3 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ app.use("/backend/notifications", notifications)
const medrecords = require('./routes/medrecords.routes')
app.use("/backend/medrecords", medrecords)

const guidancerecords = require('./routes/guidancerecords.routes')
app.use("/backend/guidancerecords", guidancerecords)

const feedback = require('./routes/feedback.routes')
app.use("/backend/feedback", feedback)

Expand Down
120 changes: 120 additions & 0 deletions routes/guidancerecords.routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
const { PrismaClient, user_type, user_approval_type } = require("@prisma/client");
const prisma = new PrismaClient();
const express = require('express');
const { isAuthenticated } = require("../middlewares");
const { findUserIdByAccessToken } = require("../routes/users.services");

const router = express.Router();

router.get("/records", async (req, res, next) => {
try {
const authorizationheader = req.headers.authorization;

const token = authorizationheader.replace('Bearer ', '');
const userId = findUserIdByAccessToken(token)
const medRecordsToGet = await prisma.guidanceRecord.findMany({
where: {
userId: userId
}
})
res.json(medRecordsToGet)
} catch (err) {
console.error(err);
res.status(500).json({ error: "an error occurred!" });
}
});

router.get("/records-by/:id", isAuthenticated, async (req, res, next) => {
try {
const { id } = req.params
const authorizationheader = req.headers.authorization

const token = authorizationheader.replace('Bearer ', '')
const userId = findUserIdByAccessToken(token)

const user = await prisma.user.findUnique({
where: {
id: userId,
approved: { not: user_approval_type.Archived }
},
select: {
type: true
}
});
if (user.type == user_type.Student) {
res.sendStatus(401)
return
}

const medRecordsToGet = await prisma.guidanceRecord.findMany({
where: {
userId: id
}
})
res.json(medRecordsToGet)
} catch (err) {
console.error(err);
res.json({ error: err.message });
}
});

router.get("/users", async (req, res, next) => {
try {
const users = await prisma.user.findMany({
where: {
approved: { not: user_approval_type.Archived }
},
select: {
id: true,
fname: true,
mname: true,
lname: true,
type: true
}
})
res.json(users)
} catch (err) {
console.error(err);
res.status(500).json({ error: "an error occurred!" });
}
});

router.post("/record", async (req, res, next) => {
try {
const data = req.body

const record = await prisma.guidanceRecord.create({
data: data,
include: {
user: true,
}
});
const authorizationheader = req.headers.authorization;
const token = authorizationheader.replace('Bearer ', '');
const userId = findUserIdByAccessToken(token)
const user = await prisma.user.findUnique({
where: {
id: userId
}
})
createNotification({
title: `Guidance Record Added`,
message: `Guidance Record created by ${user.login_username} for ${record.user.login_username}`,
users: [record.user]
})
getSocketInstance().to(record.userId).emit("notify", {
title: `Guidance Record Added`,
message: `Guidance Record created by ${user.login_username} for ${record.user.login_username}`,
})
getSocketInstance().emit("update guidance records")
res.json(record);
} catch (error) {
console.error(error);
res
.status(500)
.json({ error: "An error occurred while creating a guidance record" });

}
})

module.exports = router
9 changes: 9 additions & 0 deletions src-frontend-react/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
enable as enableDarkMode,
disable as disableDarkMode,
} from "darkreader";
import { GuidanceRecords } from "./components/GuidanceRecords";

/// TODO Separate components to other files

Expand Down Expand Up @@ -256,6 +257,14 @@ const App: React.FC = () => {
/>
}
/>
<Route
path="/guidancerecords"
element={
<GuidanceRecords
sidebarbtn_onClick={sidebarbtn_onClick}
/>
}
/>
<Route
path="/profile"
element={
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { useEffect, useState } from 'react';
import { Form, Button, Stack } from 'react-bootstrap';
import Select from 'react-select'

import LoadingOverlay from 'react-loading-overlay-ts';
import { customFetch } from '../../../utils';
import { toast } from 'react-toastify';

const DEFAULT_FORM_VALUES = {
userId: '',
content: '',
}

const DEFAULT_SELECT_VALUE = { value: 0, label: "Please pick a user" }

export const GuidanceRecordsForm = () => {
const [usersList, setUsersList] = useState([])

const [usersListOptions, setUsersListOptions] = useState([])
const [selectedUser, setSelectedUser] = useState({ ...DEFAULT_SELECT_VALUE })

const [formData, setFormData] = useState({ ...DEFAULT_FORM_VALUES });
const [formErrors, setFormErrors] = useState({ ...DEFAULT_FORM_VALUES });

const [isLoading, setIsLoading] = useState(true)

const resetToDefault = () => {
setFormData({ ...DEFAULT_FORM_VALUES })
setFormErrors({ ...DEFAULT_FORM_VALUES })
setSelectedUser({ ...DEFAULT_SELECT_VALUE })
}

const fetchAll = () => {
setIsLoading(true)
customFetch(`${global.server_backend_url}/backend/guidancerecords/users`)
.then((response) => {
if (response.ok) return response.json();
else throw response;
})
.then((data) => {
setUsersList(data)
setIsLoading(false)
return data;
})
}

const validateForm = () => {
let isValid = true;
const newFormErrors = { ...formErrors };
console.log(selectedUser.value)

if (!selectedUser.value) {
newFormErrors.userId = 'Select user first'
isValid = false;
} else newFormErrors.userId = '';


if (formData.content.trim() === '') {
newFormErrors.content = 'Content is required';
isValid = false;
} else newFormErrors.content = '';

setFormErrors(newFormErrors);
return isValid;

}

const handleUserSelectionChange = (e) => {
setSelectedUser({ value: e.value, label: e.label })
}

const handleChange = (e) => {
const { name, value } = e.target;
setFormData({
...formData,
[name]: value,
});
};

const handleSubmit = (e) => {
e.preventDefault();

if (validateForm()) {

const formatted = {
content: formData.content,
userId: selectedUser.value.id
}

customFetch(`${global.server_backend_url}/backend/guidancerecords/record`, {
method: 'POST',
body: JSON.stringify(formatted)
}).then((response) => {
fetchAll()
resetToDefault()
if (response.ok) {
return response.json();
} else throw response;
}).then((data) => {
toast(`Successfully added guidance records for ${data.user.lname}`)
}).catch((err) => {
console.log(err)
}).finally(() => {
fetchAll()
})
}
}

useEffect(() => {
fetchAll()
}, [])

useEffect(() => {
setUsersListOptions([...usersList.map((user) => {
return { value: user, label: `[${user.type}] ${user.lname}, ${user.fname} ${user.mname ? user.mname[0] + "." : ""}` }
})])
console.log(usersList)
}, [usersList])

return (
<LoadingOverlay active={isLoading} spinner text="Waiting for update">
<Form onSubmit={handleSubmit}>
<Select className='fs-5' options={usersListOptions} value={selectedUser} onChange={handleUserSelectionChange} />
<div className="text-danger mb-3">{formErrors.userId}</div>
<Form.Group controlId="content" className="mb-3">
<Form.Label>Guidance Record Contents</Form.Label>
<Form.Control
type="text"
as={'textarea'}
rows={5}
name="content"
value={formData.content}
onChange={handleChange}
required
/>
<div className="text-danger">{formErrors.content}</div>
</Form.Group>
<Stack direction="horizontal" className="gap-3 justify-content-between">
<Button className="mt-2" variant="primary" type="submit">
Create Guidance Record
</Button>
<Button variant="danger" className={`${formData.id ? '' : 'invisible'}`}>
Delete
</Button>
</Stack>
</Form>
</LoadingOverlay>
)
}
Loading

0 comments on commit f28d687

Please sign in to comment.