El7a2ny is a pair of websites developed during the ACL course in the GUC in 2023. One of the websites in the online pharmacy, which acts as a portal for buying medicines.
El7a2ny online pharmacy aims to provide a convenient solution to people who wish to purchase over the counter and perscription medicines from the comfort of their homes. It also assists the pharmacists to easily add and edit medicines as they please.
Project builds successfully and is fully functional. However, project is currently in development and improvements could be made.
The code style is enforced using eslint and prettier. The code style is enforced using pre-commit hooks and pre-commit github action.
Admin
Add admin View pharmacist joing requests View joined pharmacists View patients View medicines Monthly report
- React
- Nodejs
- MongoDB
- Mongoose
- Express
- axios
- bcryptjs
- ejs
- git
- jsonwebtoken
- nodemailer
- nodemon
- stripe
- boostrap
- alot of react sub libraries can be found in package.json in frontend folder
- redux
Aplus-Cant-Lose-Pharmacy
-
- config/
- controllers/
- files/
- 1699695378808_Screenshot 2023-07-09 150827.png
- 1699695378810_Mid 2022.pdf
- 1699695378839_GUC_314_61_36537_2023-10-10T10_23_53 (1).pdf
- 1699816462731_Screenshot 2023-07-09 213117.png
- 1699816462732_GUC_314_61_36040_2023-10-02T10_40_27 (1).pdf
- 1699816462747_medHist_1699787695138_Screenshot 2023-07-09 150827.png
- 1699816787920_medHist_1699787695138_Screenshot 2023-07-09 150827.png
- 1699816787921_contract (1).png
- 1699824570990_Fayda_National_ID_Card_-_Front.jpg
- 1699824571006_Analysis_Assignment_1.pdf
- 1699824571006_photoid-front.jpg
- 1699825442591_Fayda_National_ID.jpg
- 1699825442591_photoid-front.jpg
- 1699825442602_photoid-front.jpg
- 1700045406814_degree.pdf
- 1700045406834_Fayda_National_ID.jpg
- 1700045406841_photoid-front.jpg
- 1700409676427_Fayda_National_ID.jpg
- 1700409676451_photoid-front.jpg
- 1700409676452_Rectangle-3.jpg
- medicines/
- middleware/
- Models/
- public/
- Routes/
- services/
- .env
- file-1699821554629.png
- file-1699826662289.jpg
- file-1699826804571.jpg
- file-1700045631723.jpg
- package-lock.json
- package.json
- server.js
-
- public/
- src/
- animations/
- components/
- config/
- Context/
- data/
- features/
- Pages/
- AdminDirect.js
- App.css
- App.js
- background.jpg
- background.png
- Consts.js
- Error.js
- hi.png
- HomeDirect.js
- index.css
- index.js
- PatientDirect.js
- PharmacistDirect.js
- store.js
- package-lock.json
- package.json
- README.md
- Prescription gating for certain medicines
- Inventory notification via mail
- Password reset via mail
- Credit card payment via stripe
- Tokenized sessions for logins
- Redux States
Please Install nodejs first before using the program.
npm install -g npm
#to check node version
node -v
#to check npm version
npm -v
Then go cd into backend folder and do npm install
cd backend
npm i
Wait for it to finish the repeat the same for the frontend folder
cd frontend
npm install
You are now ready to use the program !
First run the backend.
cd backend
nodemon server
When MongoDB is connected, run the frontend.
cd frontend
npm start
For Guest:
A guest can:
-Login
-Register as patient
-Register as pharmacist
-Forgot password
For Patient:
A patient can:
-Order over the counter medicines
-Order prescribed medicines
-View and pay for cart
-View cash in wallet
-View past and current orders
-Cancel current orders
-Change password
-Chat with a pharmacist
For Pharmacist:
A pharmacist can:
-View and edit medicines
-Archive or unarchive medicines
-Add medicines
-View cash in wallet
-Upload scholarly documents
-View medicines that are out of stock
-Change password
-Chat with a doctor
-View monthly reports
For Admin:
An admin can:
-Create admin accounts
-View and delete other accounts
-View medicines
-View monthly reports
-View past and current orders
-Change password
import { styled, alpha } from "@mui/material/styles";
import AppBar from "@mui/material/AppBar";
import Box from "@mui/material/Box";
import Toolbar from "@mui/material/Toolbar";
import IconButton from "@mui/material/IconButton";
import Typography from "@mui/material/Typography";
import InputBase from "@mui/material/InputBase";
import HomeIcon from "@mui/icons-material/Home";
import SearchIcon from "@mui/icons-material/Search";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import Paper from "@mui/material/Paper";
import { useContext, useEffect, useState } from "react";
import { TextField } from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import Fab from "@mui/material/Fab";
import AddIcon from "@mui/icons-material/Add";
import EditIcon from "@mui/icons-material/Edit";
import FavoriteIcon from "@mui/icons-material/Favorite";
import NavigationIcon from "@mui/icons-material/Navigation";
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import * as React from "react";
import Button from "@mui/material/Button";
import { SnackbarContext } from "../../App";
import Dialog from "@mui/material/Dialog";
import DialogTitle from "@mui/material/DialogTitle";
import DialogContent from "@mui/material/DialogContent";
import DialogActions from "@mui/material/DialogActions";
import CloseIcon from "@mui/icons-material/Close";
import download from "downloadjs"; // Import download function
import axios from "axios";
import { API_URL } from "../../Consts.js";
import AddShoppingCartIcon from "@mui/icons-material/AddShoppingCart";
import { viewMedicine } from "../../features/adminSlice";
import Popover from "@mui/material/Popover";
import List from "@mui/material/List";
import ListItem from "@mui/material/ListItem";
import ListItemText from "@mui/material/ListItemText";
import InfoIcon from "@mui/icons-material/Info";
import _debounce from "lodash.debounce";
import {
addMedicineToCart,
viewMedicineOTC,
viewPrescriptionMedicines,
getMedicinesByActiveElement,
} from "../../features/patientSlice";
import { AutoFixNormal } from "@mui/icons-material";
const SearchIconWrapper = styled("div")(({ theme }) => ({
padding: theme.spacing(0, 2),
height: "100%",
position: "absolute",
pointerEvents: "none",
display: "flex",
alignItems: "center",
justifyContent: "center",
}));
const handleDownload = async (mId) => {
try {
const result = await axios.get(`${API_URL}/pharmacist/downloadm/${mId}`, {
responseType: "blob",
});
const filename = "medicine"; // Set the filename as needed
download(result.data, filename);
} catch (error) {
console.error("Error while downloading file. Try again later.", error);
}
};
const StyledInputBase = styled(InputBase)(({ theme }) => ({
color: "inherit",
"& .MuiInputBase-input": {
padding: theme.spacing(1, 1, 1, 0),
// vertical padding + font size from searchIcon
paddingLeft: `calc(1em + ${theme.spacing(4)})`,
transition: theme.transitions.create("width"),
width: "100%",
[theme.breakpoints.up("sm")]: {
width: "12ch",
"&:focus": {
width: "20ch",
},
},
},
}));
export default function Medicine() {
const [nameFilter, setNameFilter] = useState("");
const [useFilter, setUseFilter] = useState("");
const dispatch = useDispatch();
const snackbarMessage = useContext(SnackbarContext);
const pid = useSelector((state) => state.user.id);
useEffect(() => {
dispatch(viewMedicineOTC());
dispatch(viewPrescriptionMedicines({ pid: pid }));
}, [dispatch, pid]);
return (
<Box sx={{ flexGrow: 1 }}>
<AppBar position="static">
<Toolbar>
<IconButton
size="large"
edge="start"
color="inherit"
aria-label="open drawer"
sx={{ mr: 2 }}
>
</IconButton>
<Typography
variant="h6"
noWrap
component="div"
sx={{ flexGrow: 1, display: { xs: "none", sm: "block" } }}
>
Medicine List
</Typography>
<TextField
value={nameFilter}
onChange={(e) => {
setNameFilter(e.target.value);
}}
sx={{
height: "80%",
borderRadius: "15px",
backgroundColor: "white",
color: "white !important",
}}
label="Name..."
variant="filled"
/>
<TextField
value={useFilter}
onChange={(e) => {
setUseFilter(e.target.value);
}}
sx={{
height: "80%",
borderRadius: "15px",
backgroundColor: "white",
color: "white !important",
}}
label="Use..."
variant="filled"
/>
</Toolbar>
</AppBar>
<BasicTable nameFilter={nameFilter} useFilter={useFilter} />
<BasicTable2 nameFilter={nameFilter} useFilter={useFilter} />
</Box>
);
}
function createData(name, price, use, activeelements, amount, imagelink) {
return { name, price, use, activeelements, amount, imagelink };
}
function BasicTable({ nameFilter, useFilter }) {
const dispatch = useDispatch();
const pid = useSelector((state) => state.user.id);
const [popoverAnchor, setPopoverAnchor] = useState(null);
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const [popoverAnchors, setPopoverAnchors] = useState({});
const handleButtonClick = (event, row) => {
setPopoverAnchor(event.currentTarget);
setIsPopoverOpen(true);
// Dispatch the action to get medicines by active element
dispatch(getMedicinesByActiveElement({ medId: row._id }));
};
const handlePopoverClose = () => {
setPopoverAnchor(null);
setIsPopoverOpen(false);
};
const { alternatives } = useSelector((state) => state.patient);
useEffect(() => {
dispatch(viewMedicineOTC());
dispatch(viewPrescriptionMedicines({ pid: pid }));
}, [dispatch, pid]);
const medicineList = useSelector((state) => state.patient.otcMeds);
const snackbarMessage = useContext(SnackbarContext);
const [editRow, setEditRow] = useState({});
const [isOpen, setIsOpen] = useState(false);
const [idx, setIdx] = useState(-1);
const [id, setId] = useState("1");
const handleEditClick = (row, index) => {
setIsOpen(true);
setEditRow(row);
setId(row._id);
console.log(id);
setIdx(index);
};
useEffect(() => {
// This will log the updated id value
}, [medicineList]);
const HandleAdd = async (id, pid) => {
try {
console.log(pid);
const response = await dispatch(
addMedicineToCart({ userId: pid, medicineId: id })
);
if (response.error) {
snackbarMessage(`Error: ${response.error.message}`, "error");
} else {
snackbarMessage("Added successfully", "success");
}
} catch (error) {
snackbarMessage(`Error: ${error.message}`, "error");
}
};
const tableContainerStyle = {
maxWidth: "80%", // Adjust the maximum width as needed
margin: "0 auto", // Center-align the table horizontally
marginTop: "40px",
boxShadow: "5px 5px 5px 5px #8585854a",
};
return (
<TableContainer component={Paper} style={tableContainerStyle}>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>
<TableCell sx={{ fontWeight: "bold", fontSize: "20px" }}>
Over the counter medicines
</TableCell>
</TableCell>
</TableRow>
<TableRow>
<TableCell sx={{ fontWeight: "bold", fontSize: "20px" }}>
Name
</TableCell>
<TableCell
align="right"
sx={{ fontWeight: "bold", fontSize: "20px" }}
>
Price
</TableCell>
<TableCell
align="right"
sx={{ fontWeight: "bold", fontSize: "20px" }}
>
Use
</TableCell>
<TableCell
align="right"
sx={{ fontWeight: "bold", fontSize: "20px" }}
>
Active Elements
</TableCell>
<TableCell
align="right"
sx={{ fontWeight: "bold", fontSize: "20px" }}
>
Amount
</TableCell>
<TableCell
align="right"
sx={{ width: "20%", fontWeight: "bold", fontSize: "20px" }}
>
Image
</TableCell>
<TableCell
align="right"
sx={{ fontWeight: "bold", fontSize: "20px" }}
>
Add to cart
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{medicineList
.filter((row) => {
return (
nameFilter === "" ||
row.name.toLowerCase().includes(nameFilter.toLowerCase())
);
})
.filter((row) => {
return (
useFilter === "" ||
row.use.toLowerCase().includes(useFilter.toLowerCase())
);
})
.map((row, index) => (
<TableRow
key={index}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell component="th" scope="row">
<div
sx={{ height: "50%" }}
>
{row.name}
<IconButton
color="primary"
aria-label="find alternatives"
sx={{ fontSize: 30 }}
onClick={(e) => handleButtonClick(e, row)}
>
<InfoIcon />
</IconButton>
{/* Display alternatives in a Popover */}
<Popover
open={Boolean(popoverAnchor)}
anchorEl={popoverAnchor}
onClose={handlePopoverClose}
anchorOrigin={{
vertical: "center",
horizontal: "right", // Adjust the horizontal position
}}
transformOrigin={{
vertical: "center",
horizontal: "left",
}}
>
{alternatives && alternatives.length > 0 ? (
<List>
<ListItem>Alternatives:</ListItem>
{alternatives.map((alternative, index) => (
<ListItem key={index}>
<ListItemText primary={alternative.name} />
</ListItem>
))}
</List>
) : (
<Typography sx={{ p: 2 }}>
There are no alternatives
</Typography>
)}
</Popover>
</div>
</TableCell>
<TableCell align="right" sx={{ fontSize: "18px" }}>
{row.price}
</TableCell>
<TableCell align="right" sx={{ fontSize: "18px" }}>
{row.use}
</TableCell>
<TableCell align="right" sx={{ fontSize: "18px" }}>
{row.activeElement}
</TableCell>
<TableCell align="right" sx={{ fontSize: "18px" }}>
{row.amount}
</TableCell>
<TableCell align="right" sx={{}}>
{/* Display the image directly */}
<img
src={`/public/${row.imgurl}`}
alt="medicine"
style={{
width: "70%",
height: "70%",
}}
/>
</TableCell>
<TableCell align="right">
<div
style={{
maraginLeft: "0px",
}}
>
<IconButton
color="primary"
aria-label="add to shopping cart"
onClick={() => HandleAdd(row._id, pid)}
>
<AddShoppingCartIcon />
</IconButton>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
function BasicTable2({ nameFilter, useFilter }) {
const dispatch = useDispatch();
const pid = useSelector((state) => state.user.id);
useEffect(() => {
dispatch(viewMedicine());
}, [dispatch]);
const medicineList = useSelector((state) => state.patient.prescriptionMeds);
const snackbarMessage = useContext(SnackbarContext);
const [editRow, setEditRow] = useState({});
const [isOpen, setIsOpen] = useState(false);
const [idx, setIdx] = useState(-1);
const [id, setId] = useState("1");
const handleEditClick = (row, index) => {
setIsOpen(true);
setEditRow(row);
setId(row._id);
console.log(id);
setIdx(index);
};
useEffect(() => {
// This will log the updated id value
}, [medicineList]);
const HandleAdd = async (id, pid) => {
try {
console.log(pid);
const response = await dispatch(
addMedicineToCart({ userId: pid, medicineId: id })
);
if (response.error) {
snackbarMessage(`Error: ${response.error.message}`, "error");
} else {
snackbarMessage("Added successfully", "success");
}
} catch (error) {
snackbarMessage(`Error: ${error.message}`, "error");
}
};
const tableContainerStyle = {
maxWidth: "80%", // Adjust the maximum width as needed
margin: "0 auto", // Center-align the table horizontally
marginTop: "40px",
boxShadow: "5px 5px 5px 5px #8585854a",
};
if (medicineList.length > 0) {
return (
<TableContainer component={Paper} style={tableContainerStyle}>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>
<TableCell sx={{ fontWeight: "bold", fontSize: "20px" }}>
Prescription medicines
</TableCell>
</TableCell>
</TableRow>
<TableRow>
<TableCell sx={{ fontWeight: "bold", fontSize: "20px" }}>
Name
</TableCell>
<TableCell
align="right"
sx={{ fontWeight: "bold", fontSize: "20px" }}
>
Price
</TableCell>
<TableCell
align="right"
sx={{ fontWeight: "bold", fontSize: "20px" }}
>
Use
</TableCell>
<TableCell
align="right"
sx={{ fontWeight: "bold", fontSize: "20px" }}
>
Active Elements
</TableCell>
<TableCell
align="right"
sx={{ fontWeight: "bold", fontSize: "20px" }}
>
Amount
</TableCell>
<TableCell
align="right"
sx={{ width: "20%", fontWeight: "bold", fontSize: "20px" }}
>
Image
</TableCell>
<TableCell
align="right"
sx={{ fontWeight: "bold", fontSize: "20px" }}
>
Add to cart
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{medicineList
.filter((row) => {
return (
nameFilter === "" ||
row.name.toLowerCase().includes(nameFilter.toLowerCase())
);
})
.filter((row) => {
return (
useFilter === "" ||
row.use.toLowerCase().includes(useFilter.toLowerCase())
);
})
.map((row, index) => (
<TableRow
key={index}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell component="th" scope="row">
{row.name}
</TableCell>
<TableCell align="right" sx={{ fontSize: "18px" }}>
{row.price}
</TableCell>
<TableCell align="right" sx={{ fontSize: "18px" }}>
{row.use}
</TableCell>
<TableCell align="right" sx={{ fontSize: "18px" }}>
{row.activeElement}
</TableCell>
<TableCell align="right" sx={{ fontSize: "18px" }}>
{row.amount}
</TableCell>
<TableCell align="right" sx={{}}>
{/* Display the image directly */}
<img
src={`/public/${row.imgurl}`}
alt="medicine"
style={{
width: "70%",
height: "70%",
}}
/>
</TableCell>
<TableCell align="right">
<div
style={{
maraginLeft: "0px",
}}
>
<IconButton
color="primary"
aria-label="add to shopping cart"
onClick={() => HandleAdd(row._id, pid)}
>
<AddShoppingCartIcon />
</IconButton>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
}
import { styled, alpha } from "@mui/material/styles";
import AppBar from "@mui/material/AppBar";
import Box from "@mui/material/Box";
import Toolbar from "@mui/material/Toolbar";
import IconButton from "@mui/material/IconButton";
import Typography from "@mui/material/Typography";
import InputBase from "@mui/material/InputBase";
import HomeIcon from "@mui/icons-material/Home";
import SearchIcon from "@mui/icons-material/Search";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import Paper from "@mui/material/Paper";
import { useContext, useEffect, useState } from "react";
import { TextField } from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import Fab from "@mui/material/Fab";
import { getOrdersInMonth } from "../../features/pharmacistSlice";
import AddIcon from "@mui/icons-material/Add";
import EditIcon from "@mui/icons-material/Edit";
import FavoriteIcon from "@mui/icons-material/Favorite";
import NavigationIcon from "@mui/icons-material/Navigation";
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import * as React from "react";
import Button from "@mui/material/Button";
import { SnackbarContext } from "../../App";
import Dialog from "@mui/material/Dialog";
import DialogTitle from "@mui/material/DialogTitle";
import DialogContent from "@mui/material/DialogContent";
import DialogActions from "@mui/material/DialogActions";
import CloseIcon from "@mui/icons-material/Close";
import download from "downloadjs"; // Import download function
import axios from "axios";
import { API_URL } from "../../Consts.js";
import MenuItem from "@mui/material/MenuItem";
import Select from "@mui/material/Select";
import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns";
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
import MobileDateRangePicker from "@mui/lab/MobileDateRangePicker";
import {
deleteMedicine,
updateMedicineDetails,
editMedicine,
archiveMedicine,
} from "../../features/pharmacistSlice";
import { AutoFixNormal } from "@mui/icons-material";
const SearchIconWrapper = styled("div")(({ theme }) => ({
padding: theme.spacing(0, 2),
height: "100%",
position: "absolute",
pointerEvents: "none",
display: "flex",
alignItems: "center",
justifyContent: "center",
}));
const StyledInputBase = styled(InputBase)(({ theme }) => ({
color: "inherit",
"& .MuiInputBase-input": {
padding: theme.spacing(1, 1, 1, 0),
// vertical padding + font size from searchIcon
paddingLeft: `calc(1em + ${theme.spacing(4)})`,
transition: theme.transitions.create("width"),
width: "100%",
[theme.breakpoints.up("sm")]: {
width: "12ch",
"&:focus": {
width: "20ch",
},
},
},
}));
export default function MonthlyReport() {
const [nameFilter, setNameFilter] = useState("");
const [useFilter, setUseFilter] = useState("");
const [selectedMonth, setSelectedMonth] = useState("");
const [selectedYear, setSelectedYear] = useState("");
const [startDate, setStartDate] = useState(""); // Add start date state
const [endDate, setEndDate] = useState(""); // Add end date state
const dispatch = useDispatch();
const { report } = useSelector((state) => state.pharmacist);
const handleStartDateChange = (event) => {
setStartDate(event.target.value);
};
const handleEndDateChange = (event) => {
setEndDate(event.target.value);
};
const generateDaysArray = (start, end) => {
const days = [];
for (let i = start; i <= end; i++) {
days.push(i);
}
return days;
};
const defaultStartDate = 1;
const defaultEndDate = 31;
useEffect(() => {
// Dispatch getOrdersInMonth here
dispatch(getOrdersInMonth({ month: selectedMonth, year: selectedYear }));
console.log(report);
}, [dispatch, selectedMonth, selectedYear, report]);
return (
<Box sx={{ flexGrow: 1 }}>
<AppBar position="static">
<Toolbar>
<IconButton
size="large"
edge="start"
color="inherit"
aria-label="open drawer"
sx={{
mr: 2,
width: "25%", // Set the width to 25%
}}
>
</IconButton>
<Typography
variant="h6"
noWrap
component="div"
sx={{ flexGrow: 1, display: { xs: "none", sm: "block" } }}
>
Sales Report
</Typography>
<TextField
value={nameFilter}
onChange={(e) => {
setNameFilter(e.target.value);
}}
sx={{
height: "80%",
width: "15%", // Adjust the width as needed
borderRadius: "15px",
backgroundColor: "white",
color: "white !important",
}}
label="Name filter"
variant="filled"
/>
<TextField
id="start-date"
label="Start Date"
select
value={startDate || ""}
onChange={handleStartDateChange}
variant="outlined"
sx={{ width: "10%",color:"white" }} // Adjust the width as needed
>
{generateDaysArray(defaultStartDate, defaultEndDate).map((day) => (
<MenuItem key={day} value={day}>
{day}
</MenuItem>
))}
</TextField>
<TextField
id="end-date"
label="End Date"
select
value={endDate || ""}
onChange={handleEndDateChange}
variant="outlined"
sx={{ width: "10%" }} // Adjust the width as needed
>
{generateDaysArray(
startDate || defaultStartDate,
defaultEndDate
).map((day) => (
<MenuItem key={day} value={day}>
{day}
</MenuItem>
))}
</TextField>
<Select
value={selectedMonth === "" ? "" : parseInt(selectedMonth, 10)}
onChange={(e) => setSelectedMonth(e.target.value)}
variant="outlined"
displayEmpty
inputProps={{ "aria-label": "Select Month" }}
sx={{ marginRight: "16px", minWidth: "100px" }}
>
<MenuItem value="" disabled>
Month
</MenuItem>
{Array.from({ length: 12 }, (_, index) => index + 1).map(
(month) => (
<MenuItem key={month} value={month}>
{new Date(0, month - 1).toLocaleString("en-US", {
month: "long",
})}
</MenuItem>
)
)}
</Select>
<Select
value={selectedYear}
onChange={(e) => setSelectedYear(e.target.value)}
variant="outlined"
displayEmpty
inputProps={{ "aria-label": "Select Year" }}
sx={{ minWidth: "80px" }}
>
<MenuItem value="" disabled>
Year
</MenuItem>
{Array.from(
{ length: new Date().getFullYear() - 2014 },
(_, index) => 2015 + index
).map((year) => (
<MenuItem key={year} value={year}>
{year}
</MenuItem>
))}
</Select>
</Toolbar>
</AppBar>
<BasicTable
nameFilter={nameFilter}
startDate={startDate}
endDate={endDate}
month={selectedMonth}
year={selectedYear}
/>
</Box>
);
}
function BasicTable({ nameFilter, startDate, endDate, month, year }) {
const snackbarMessage = useContext(SnackbarContext);
const dispatch = useDispatch();
let rows = useSelector((state) => state.pharmacist.report);
const [editRow, setEditRow] = useState({});
const [edited, setEdited] = useState(false);
const [isOpen, setIsOpen] = useState(false);
const [idx, setIdx] = useState(-1);
const [id, setId] = useState("1");
useEffect(() => {}, [dispatch]);
const [imgUrl, setImgUrl] = useState(""); // Add this line
const handleFileChange = (event) => {
const file = event.target.files[0];
const reader = new FileReader();
reader.onloadend = () => {
const base64data = reader.result;
console.log("Base64 Image Data:", base64data);
setImgUrl(base64data);
};
if (file) {
reader.readAsDataURL(file);
}
};
const uploadButtonStyle = {
backgroundColor: "#4CAF50",
color: "white",
padding: "10px 15px",
borderRadius: "5px",
cursor: "pointer",
marginTop: "10px",
width: "20%", // Add margin-top for spacing
};
const handleSubmit = (event) => {
event.preventDefault();
const formData = new FormData();
formData.append("file", event.target.elements.imgFile.files[0]);
axios
.post(`${API_URL}/pharmacist/upload2`, formData)
.then((uploadResponse) => {
// Renamed to uploadResponse
const imgText = uploadResponse.data;
const sampleData = {
activeElement: event.target.elements.activeElement.value,
price: event.target.elements.price.value,
use: event.target.elements.use.value,
name: event.target.elements.name.value,
amount: event.target.elements.amount.value,
imgurl: imgText,
id: id, // Use the text received from the backend
};
console.log(sampleData);
dispatch(editMedicine({ idx: idx, newData: sampleData }));
const response = dispatch(updateMedicineDetails(sampleData));
response.then((responseData) => {
console.log(responseData);
if (responseData.payload === undefined) {
snackbarMessage(`error: ${responseData} has occurred`, "error");
} else {
snackbarMessage("You have successfully edited", "success");
}
});
setIsOpen(false);
});
};
const fileInputStyle = {
display: "none",
};
const tableContainerStyle = {
maxWidth: "80%", // Adjust the maximum width as needed
margin: "0 auto", // Center-align the table horizontally
marginTop: "40px",
boxShadow: "5px 5px 5px 5px #8585854a",
};
const [imageViewerOpen, setImageViewerOpen] = useState(false);
const [selectedImage, setSelectedImage] = useState(null);
const handleImageViewerOpen = (imgUrl) => {
setSelectedImage(imgUrl);
setImageViewerOpen(true);
};
if (!month || !year) {
} else if (rows.length === 0) {
return <div>No orders for this month</div>;
} else {
return (
<>
<TableContainer component={Paper} style={tableContainerStyle}>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell sx={{ fontWeight: "bold", fontSize: "20px" }}>
Order report
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows
.filter((row) => {
const orderDate = new Date(row.orderDate); // Convert orderDate to Date object
const startDateFull = new Date(
`${year}-${month}-${startDate}`
);
const endDateFull = new Date(
`${year}-${month}-${Number(endDate) + 1}`
);
return (
(nameFilter === "" ||
row.cart.some((item) =>
item.name
.toLowerCase()
.includes(nameFilter.toLowerCase())
)) &&
(!startDate || orderDate >= new Date(startDateFull)) &&
(!endDate || orderDate <= new Date(endDateFull))
);
})
.map((row, index) => (
<React.Fragment key={index}>
<TableRow>
<TableCell
colSpan={4}
sx={{ fontSize: "20px", fontWeight: "bold" }}
>
Order Date: {new Date(row.orderDate).toLocaleString()}
</TableCell>
</TableRow>
<TableRow>
<TableCell sx={{ fontWeight: "bold", fontSize: "20px" }}>
Name
</TableCell>
<TableCell
align="right"
sx={{ fontWeight: "bold", fontSize: "20px" }}
>
Price
</TableCell>
<TableCell
align="right"
sx={{ fontWeight: "bold", fontSize: "20px" }}
>
Amount
</TableCell>
<TableCell
align="right"
sx={{ fontWeight: "bold", fontSize: "20px" }}
>
Total price
</TableCell>
</TableRow>
{row.cart.map((cartEntry, cartIndex) => (
<TableRow key={`${index}-${cartIndex}`}>
<TableCell sx={{ fontSize: "18px" }}>
{cartEntry.name}
</TableCell>
<TableCell align="right" sx={{ fontSize: "18px" }}>
{cartEntry.price}
</TableCell>
<TableCell align="right" sx={{ fontSize: "18px" }}>
{cartEntry.amount}
</TableCell>
<TableCell align="right" sx={{ fontSize: "18px" }}>
{cartEntry.amount * cartEntry.price}
</TableCell>
</TableRow>
))}
</React.Fragment>
))}
</TableBody>
</Table>
</TableContainer>
</>
);
}
}
import React, { useEffect, useState, useContext } from "react";
import { SnackbarContext } from "../../App";
import { API_URL } from "../../Consts.js";
import download from "downloadjs"; // Import download function
import axios from "axios";
import {
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
Button,
} from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import { viewPendPh, acceptPh, rejectPh } from "../../features/adminSlice";
const handleDownload = async (drId) => {
try {
const response = await axios.get(
`${API_URL}/pharmacist/downloadf/${drId}`,
{
responseType: "blob",
}
);
// Extract filename from the Content-Disposition header
const contentDisposition = response.headers["content-disposition"];
const filenameMatch = contentDisposition.match(/filename="(.+)"/);
const filename = filenameMatch ? filenameMatch[1] : "files.zip";
// Download the file using the downloadjs library
download(response.data, filename, response.headers["content-type"]);
} catch (error) {
console.error("Error downloading files:", error);
// Handle error, e.g., show an error message to the user
}
};
const JoinRequests = () => {
const snackbarMessage = useContext(SnackbarContext);
const dispatch = useDispatch();
useEffect(() => {
dispatch(viewPendPh());
}, [dispatch]);
const dummyData = useSelector((state) => state.admin.phpending);
const handleDelete = (requestId) => {};
const handleApprove = (id) => {
const responseData = dispatch(acceptPh(id));
if (responseData === undefined) {
snackbarMessage(`error: error in sending an email`, "error");
} else {
snackbarMessage("Email sent to accepted Pahrmacist", "success");
}
};
const handleReject = (id) => {
const responseData = dispatch(rejectPh(id));
if (responseData === undefined) {
snackbarMessage(`error: error in sending an email`, "error");
} else {
snackbarMessage("Email sent to rejected Pahrmacist", "success");
}
};
return (
<div>
<h1>Join Requests</h1>
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell>Name</TableCell>
<TableCell>Email</TableCell>
<TableCell>Username</TableCell>
<TableCell>Gender</TableCell>
<TableCell>Status</TableCell>
<TableCell>Rate</TableCell>
<TableCell>affilation</TableCell>
<TableCell>Background</TableCell>
<TableCell>Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{dummyData.map((request) => (
<TableRow key={request._id}>
<TableCell>{request.name}</TableCell>
<TableCell>{request.email}</TableCell>
<TableCell>{request.username}</TableCell>
<TableCell>{request.gender}</TableCell>
<TableCell>{request.status}</TableCell>
<TableCell>{request.rate}</TableCell>
<TableCell>{request.affilation}</TableCell>
<TableCell>{request.background}</TableCell>
<TableCell>
{" "}
<Button
sx={{
backgroundColor: "#1776d1",
color: "black",
marginRight: "10px",
marginTop: "10px",
width: "100px",
}}
onClick={() => handleDownload(request._id)}
>
Download
</Button>
</TableCell>
<TableCell>
<Button
onClick={() => handleApprove(request._id)}
variant="contained"
color="primary"
>
Approve
</Button>
<Button
onClick={() => handleReject(request._id)}
variant="contained"
color="secondary"
>
Reject
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</div>
);
};
export default JoinRequests;
import React, { useState, useContext } from "react";
import { useDispatch,useSelector } from "react-redux";
import { loginGuest } from "../../features/userSlice";
import "./login.css";
import { useEffect } from "react";
import { SnackbarContext } from "../../App";
import { NavLink, useNavigate } from "react-router-dom";
import IconButton from "@mui/material/IconButton";
import InputAdornment from "@mui/material/InputAdornment";
import Visibility from "@mui/icons-material/Visibility";
import VisibilityOff from "@mui/icons-material/VisibilityOff";
function App() {
const navigate = useNavigate();
const snackbarMessage = useContext(SnackbarContext);
const dispatch = useDispatch();
const [isSubmitted, setIsSubmitted] = useState(false);
const [showPassword, setShowPassword] = useState(false);
const user = useSelector((state) => state.user);
useEffect(() => {
if (user.logged) {
snackbarMessage("You have successfully logged in", "success");
setIsSubmitted(true);
navigate("/Home");
} else if (user.error) {
snackbarMessage("Error: user not found", "error");
}
}, [user]);
const handleSubmit = (event) => {
event.preventDefault();
const guest = {
username: event.target.elements.username.value,
password: event.target.elements.password.value,
};
const response = dispatch(loginGuest(guest));
};
const toggleShowPassword = () => {
setShowPassword(!showPassword);
};
const linkStyle = {
textDecoration: "none",
color: "#0073e6",
borderBottom: "1px solid transparent",
transition: "border-bottom 0.3s",
};
const hoverStyle = {
borderBottom: "1px solid #0073e6",
};
return (
<div className="app">
<div className="login-form">
<div className="title">Sign In</div>
{isSubmitted ? (
<div>User is successfully logged in</div>
) : (
<div className="form">
<form onSubmit={handleSubmit}>
<div className="input-container">
<label htmlFor="username">Username </label>
<input type="text" id="username" name="username" required />
</div>
<div className="input-container">
<label htmlFor="password">Password </label>
<div className="password-input">
<input
type={showPassword ? "text" : "password"}
id="password"
name="password"
required
/><span>
<InputAdornment position="end">
<IconButton sx={{}}onClick={toggleShowPassword}>
{showPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment></span>
</div>
</div>
<div className="options">
<div>
<span>Don't have an account? </span>
<NavLink
to="/"
style={linkStyle}
activeStyle={hoverStyle}
>
Register
</NavLink>
</div>
<div>
<span>Forgot password? </span>
<NavLink
to="/ResetPassword"
style={linkStyle}
activeStyle={hoverStyle}
>
Reset password
</NavLink>
</div>
</div>
<div className="button-container">
<input type="submit" />
</div>
</form>
</div>
)}
</div>
</div>
);
}
export default App;
import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useContext } from "react";
import {
viewCart,
getAddresses,
addAddress,
getWallet,
payForCart,
} from "../../features/patientSlice";
import { SnackbarContext } from "../../App";
import { NavLink, useNavigate } from "react-router-dom";
import { cancelOrder } from "../../features/patientSlice";
import {
Box,
Typography,
Button,Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
TextField,
FormControl,
InputLabel,
Select,
MenuItem,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
} from "@mui/material";
import { Link } from "react-router-dom";
const ViewOrder = () => {
const snackbarMessage = useContext(SnackbarContext);
const order = useSelector((state) => state.patient.orderDetails);
const role = useSelector((state) => state.user.role);
const dispatch = useDispatch();
const navigate = useNavigate();
console.log(order);
const handleCancelOrder = async () => {
try {
// Dispatch an action to cancel the order on the server
await dispatch(cancelOrder({ oid: order.orderId }));
snackbarMessage("Order canceled successfully", "success");
// Navigate to the home page or any other desired destination
navigate("/Home");
} catch (error) {
snackbarMessage("Error cancelling order", "error");
}
};
const tableContainerStyle = {
maxWidth: "80%", // Adjust the maximum width as needed
margin: "0 auto", // Center-align the table horizontally
marginTop: "40px",
boxShadow: "5px 5px 5px 5px #8585854a",
};
// Add logic to handle payment confirmation
// You can dispatch actions, make API calls, etc.
return (
role==="patient"?
<Box>
<div style={{ position: "absolute", top: 0, left: 0, padding: "10px" }}>
<NavLink exact to="/Home">
<Button variant="outlined" color="primary">
Return
</Button>
</NavLink>
</div>
<Typography variant="h4" gutterBottom style={{ marginTop: "50px" }}>
Order Summary
</Typography>
<Typography variant="h6" gutterBottom>
Grand Total: {order.totalPrice}
</Typography>
<Typography variant="h6" gutterBottom>
Date: {order.orderDate}
</Typography>
<Typography variant="h6" gutterBottom>
Delivery location: {order.address}
</Typography>
<Typography variant="h6" gutterBottom>
Status: {order.status}
</Typography>
<Typography variant="h6" gutterBottom>
Payment type: {order.payment}
</Typography>
<TableContainer component={Paper} style={tableContainerStyle}>
{/* Dialog component and form go here */}
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Name</TableCell>
<TableCell align="right">Active Elements</TableCell>
<TableCell align="right">Price</TableCell>
<TableCell align="right">Amount</TableCell>
<TableCell align="right">Total Price</TableCell>
</TableRow>
</TableHead>
<TableBody>
{order.cart.map((row, index) => (
<TableRow
key={index}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell component="th" scope="row">
{row.name}
</TableCell>
<TableCell align="right">{row.activeElement}</TableCell>
<TableCell align="right">{row.price}</TableCell>
<TableCell align="right">{row.cartAmount}</TableCell>
<TableCell align="right">{row.price * row.cartAmount}</TableCell>
</TableRow>
))}
{/* Add the Grand Total row here */}
<TableRow>
<TableCell align="center">
<Button
variant="contained"
color="primary"
onClick={handleCancelOrder}
>
Cancel order
</Button>
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
</Box>:<>
<Link to="/Login" sx={{ left: "100%" }}>
<Typography
variant="h6"
noWrap
component="div"
sx={{
flexGrow: 1,
display: { xs: "none", sm: "flex" },
fontSize: "20px",
maragin: "auto",
}}
>
Login
</Typography>
</Link>
</>
);
};
export default ViewOrder;
{
username,
password
}
Logs in
{
username,
password
}
Creates a new patient account. Validates input fields, checks for existing username and email, and creates a patient along with a user record.
{
medicineId
}
Adds a medicine to the patient's cart. Validates user and medicine, checks if the medicine is already in the cart, and updates the cart accordingly.
{
medicineId
}
Removes a medicine from the patient's cart. Validates user and medicine, and updates the cart accordingly.
{
medicineId
}
Decreases the count of a medicine in the patient's cart. Validates user and medicine, updates the cart, and removes the item if the count reaches 0.
{
userId
}
Views the contents of the patient's cart. Retrieves medicine details for each item in the cart and calculates the grand total.
{
location
}
Adds a new address to the patient's addresses array.
{
userId
}
Gets the addresses associated with the patient.
{
userId
}
Gets the wallet balance of the patient.
{
paymentType,
address
}
Processes the payment for the items in the patient's cart. Updates medicine quantities, creates a new order, and clears the patient's cart.
{
userId
}
Gets the undelivered orders for the patient.
{
userId
}
Gets the delivered orders for the patient.
{
orderId
}
Gets the details of a specific order, including medicine details in the cart.
{
orderId
}
Cancels a specific order. Updates medicine quantities, refunds the payment if needed, and deletes the order.
{
address,
amount
}
Creates a new Stripe checkout session for processing payment.
{
// No specific parameters
}
Gets unarchived Over the Counter medicines.
{
patientId
}
Gets unarchived prescription medicines for a specific patient.
{
medicineId
}
Gets other unarchived Over the Counter medicines with the same active element as the specified medicine.
{
name,
email,
username,
dBirth,
gender,
rate,
affilation,
background,
docs,
password
}
Creates a new pharmacist and user records.
{
activeElement,
price,
use,
name,
amount,
imgurl,
type
}
Adds a new medicine to the system.
{
}
Gets the names of all pharmacists in the system.
{
}
Gets the names of all doctors in the system.
{
name,
activeElement,
price,
use,
amount,
imgurl,
type
}
Updates details of a specific medicine.
{
month,
year
}
Gets orders with medicine information for a specific month.
{
phId: pharmacist_id
}
Gets the wallet information for a specific pharmacist.
{
}
Gets all medicines with zero amount in stock.
{
}
Gets all admins in the system.
{
username,
password,
email
}
Creates a new admin user in the system.
{
}
Gets all pending pharmacists in the system.
{
}
Gets all accepted pharmacists in the system.
{
}
Gets all patients in the system.
{
id: patient_id
}
Deletes a specific patient and their associated user account.
{
id: pharmacist_id
}
Deletes a specific pharmacist and their associated user account.
{
id: admin_id
}
Deletes a specific admin user.
{
id
}
Sends an acceptance email to a pharmacist with the specified ID.
{
id
}
Sends an rejection email to a pharmacist with the specified ID.
{
userId,
logId
}
Creates or fetches a one-to-one chat between users.
{
id: user_id
}
Fetches all chats for a user.
{
users,
name
}
Creates a new group chat.
{
chatId,
chatName
}
Renames a group chat.
{
chatId,
userId
}
Removes a user from a group.
{
chatId,
userId
}
Adds a user to a group or allows a user to leave a group.
{
chatId
}
Gets all messages for a specific chat.
{
content,
chatId,
logId
}
Creates a new message in a chat.
GET /api/patient/getPatientOrders/652abfb5bbd323bbbc0a4364
Parameter | Type | Description |
---|---|---|
patientId |
ObjectId |
Required. Id of patient |
Response
{
"orders": [
{
"_id": "6553bd4b19799f4076ed06da",
"orderDate": "2023-11-14T18:32:43.847Z",
"address": "Beety",
"totalPrice": 23
},
{
"_id": "6553d332670233a6b5f13852",
"orderDate": "2023-11-14T20:06:10.375Z",
"address": "Beety",
"totalPrice": 34
},
{
"_id": "6553df7d16c5eb7e9da6d95c",
"orderDate": "2023-11-14T20:58:37.570Z",
"address": "heliopolis",
"totalPrice": 23
},
{
"_id": "6553e6301c1e135f352d46c0",
"orderDate": "2023-11-14T21:27:12.499Z",
"address": "Henaho",
"totalPrice": 150
}
]
}
POST addMedicineToCart/652abfb5bbd323bbbc0a4364
Body:
{
"medicineId": "65514be645d7b1d96259f6db"
}
Parameter | Type | Description |
---|---|---|
patientId |
ObjectId |
Required. Id of patient |
medicineId |
ObjectId |
Required. Id of med to be added |
Response
{
"message": "Medicine added to cart successfully",
"patient": {
"emergencyContact": {
"fullName": "Emergency Contact Name",
"mobile": 9876543210,
"relation": "spouse"
},
"_id": "652abfb5bbd323bbbc0a4364",
"name": "John kaas",
"email": "sohailahakeem20@gmail.com",
"username": "johndoe1234",
"dBirth": "1990-01-15T00:00:00.000Z",
"gender": "male",
"mobile": 1234567890,
"family": [
{
"fullName": "John Doe",
"NID": 1234567890,
"age": 30,
"gender": "male",
"relation": "spouse",
"_id": "652ade520528b0fd04805feb"
},
{
"fullName": "Jolia Doe",
"NID": 1234567890,
"age": 30,
"gender": "female",
"relation": "child",
"_id": "652ade7d0528b0fd04805ff1"
},
{
"fullName": "John Doe",
"NID": 1234567890,
"age": 30,
"gender": "male",
"relation": "child",
"_id": "652afdb51f86662ac38cebd9"
},
{
"fullName": "nardy",
"NID": 132245678909867,
"age": 33,
"gender": "male",
"relation": "child",
"_id": "652afdc51f86662ac38cebe2"
},
{
"fullName": "mona",
"NID": 234,
"age": 4567,
"gender": "male",
"relation": "spouse",
"_id": "652b54c76a7924f9f0f77698"
},
{
"fullName": "mona",
"NID": 234,
"age": 4567,
"gender": "male",
"relation": "spouse",
"_id": "652b54c96a7924f9f0f776a3"
},
{
"fullName": "mona",
"NID": 234,
"age": 4567,
"gender": "male",
"relation": "child",
"_id": "652b54cc6a7924f9f0f776af"
},
{
"fullName": "mona",
"NID": 234,
"age": 4567,
"gender": "male",
"relation": "child",
"_id": "652b54cd6a7924f9f0f776bc"
},
{
"fullName": "dfvberfsbv",
"NID": 4567890,
"age": 20,
"gender": "male",
"relation": "child",
"_id": "652b5592d7e59982899b0677"
},
{
"fullName": "gdgrd",
"NID": 32423,
"age": 54,
"gender": "male",
"relation": "child",
"_id": "652b580cd7e59982899b0691"
},
{
"fullName": "gddergdg",
"NID": 3424235,
"age": 33,
"gender": "male",
"relation": "spouse",
"_id": "652b5f5bd7e59982899b077b"
},
{
"fullName": "gddergdg",
"NID": 3424235,
"age": 33,
"gender": "male",
"relation": "spouse",
"_id": "652b5f66d7e59982899b078c"
},
{
"fullName": "ahanyw",
"NID": 1234567,
"age": 20,
"gender": "female",
"relation": "child",
"_id": "652b5fc8d7e59982899b0852"
},
{
"fullName": "ahanyw",
"NID": 1234567,
"age": 20,
"gender": "female",
"relation": "child",
"_id": "652b6135d7e59982899b0865"
},
{
"fullName": "mona",
"NID": 1234567,
"age": 40,
"gender": "female",
"relation": "child",
"_id": "652b6223d7e59982899b08b0"
},
{
"fullName": "sokara",
"NID": 234567890,
"age": 1234,
"gender": "female",
"relation": "spouse",
"_id": "652b62f3d7e59982899b08ff"
},
{
"fullName": "sokara",
"NID": 234567890,
"age": 1234,
"gender": "female",
"relation": "spouse",
"_id": "652b62f6d7e59982899b0915"
},
{
"fullName": "dcfvgbh",
"NID": 123456,
"age": 20,
"gender": "male",
"relation": "spouse",
"_id": "652b669bd7e59982899b09de"
},
{
"fullName": "mona",
"NID": 12345678904352,
"age": 13,
"gender": "none",
"relation": "child",
"_id": "652d918d147b6dedfc5d5816"
},
{
"fullName": "grgrgr",
"NID": 12345678904352,
"age": 13,
"gender": "none",
"relation": "child",
"_id": "652d91d9147b6dedfc5d582f"
},
{
"fullName": "dfgfhgjkh",
"NID": 124152564616,
"age": 46,
"gender": "female",
"relation": "spouse",
"_id": "652d929c147b6dedfc5d5879"
},
{
"fullName": "sokar",
"NID": 3.5647890098765234e+23,
"age": 242,
"gender": "none",
"relation": "spouse",
"_id": "652d92bb147b6dedfc5d58c6"
},
{
"fullName": "hala",
"NID": 2145,
"age": 44,
"gender": "female",
"relation": "spouse",
"_id": "652e41053fdfdd12a1ba9bde"
},
{
"fullName": "hamosa",
"NID": 12345678912,
"age": 33,
"gender": "male",
"relation": "child",
"pid": "6547c7d29393a7db1188bc13",
"_id": "6553cd8e070c40358a9a9e83"
},
{
"fullName": "remote",
"NID": 12345678912,
"age": 22,
"gender": "male",
"relation": "child",
"pid": "652d9e3bf9638ca5e2694dbd",
"_id": "65549b349cac1e09eae3f679"
},
{
"fullName": "remote",
"NID": 12345678912,
"age": 22,
"gender": "male",
"relation": "child",
"pid": "652d9e3bf9638ca5e2694dbd",
"_id": "65549b349cac1e09eae3f69c"
},
{
"fullName": "remote",
"NID": 12345678912,
"age": 22,
"gender": "male",
"relation": "child",
"pid": "652d9e3bf9638ca5e2694dbd",
"_id": "65549b459cac1e09eae3f717"
},
{
"fullName": "one",
"NID": 12345678912,
"age": 33,
"gender": "male",
"relation": "spouse",
"pid": "655a2170d0a24345699c7690",
"_id": "657b5a4795551fc518ff5fcf"
},
{
"fullName": "hmar",
"NID": 123546,
"age": 13,
"gender": "male",
"relation": "spouse",
"_id": "657b5a7c95551fc518ff6046"
},
{
"fullName": "fr",
"NID": 12345678,
"age": 12,
"gender": "male",
"relation": "spouse",
"_id": "657b5aab95551fc518ff6160"
}
],
"doctors": [
{
"doctorID": "65591f061fd85ba47a9e48f9",
"_id": "652af9fc4cdab3677d1e7573"
}
],
"records": [
{
"_id": "6529cda4a1da2872d3dcd4f0",
"url": "bhjgkdj",
"desc": "ghfvshfj"
},
{
"_id": "6529cda4a1aa2872d3dcd4f0",
"url": "ejsfvfyesfvyesfjvyef",
"desc": "yufvhjdvfhs"
}
],
"__v": 216,
"cart": [
{
"medicineID": "65514be645d7b1d96259f6db",
"amount": 2,
"_id": "6554a47f12f6e6c1c49d69bf"
}
],
"addresses": [
{
"location": "Beety",
"_id": "654e85b20ac91ee12d737200"
},
{
"location": "Henaho",
"_id": "654f9925cad42bbe447a320a"
},
{
"location": "heliopolis",
"_id": "6553df5e16c5eb7e9da6d916"
},
{
"location": "guceh",
"_id": "6554a42812f6e6c1c49d6763"
}
],
"wallet": 999988567.99,
"healthRecords": [
{
"date": "2023-11-15T12:30:00.000Z",
"description": "Routine checkup",
"labResults": "Normal",
"medicalInformation": "No allergies",
"primaryDiagnosis": "Healthy",
"treatment": "None",
"_id": "654fe0a3fe5ab7e7ed4dc3e9"
},
{
"date": "2023-12-05T14:45:00.000Z",
"description": "Follow-up appointment",
"labResults": "Within normal range",
"medicalInformation": "No known allergies",
"primaryDiagnosis": "Stable condition",
"treatment": "Continuing medication",
"_id": "654fe143fe5ab7e7ed4dc42f"
},
{
"date": "2023-10-15T14:45:00.000Z",
"description": "Follow-up appointment",
"labResults": "Within normal range",
"medicalInformation": "No known allergies",
"primaryDiagnosis": "Stable condition",
"treatment": "Continuing medication",
"_id": "654fee21db99e318072802e8"
},
{
"date": "2023-11-30T00:00:00.000Z",
"description": "bbbbb",
"labResults": "bbbbb",
"medicalInformation": "bbbbb",
"primaryDiagnosis": "bbbbb",
"treatment": "bbbbb",
"_id": "654fee7dd3680d12080b02d9"
},
{
"_id": "65549fce93d90c7a644c4600"
}
],
"hPackage": "6547d1fa0b08dd03e8e7de39",
"SubDate": "2023-11-14T22:00:00.000Z",
"hPStatus": "Subscribed",
"medHist": [
{
"title": "heart",
"description": "break",
"file_path": "medHist\\1702688980571_Screenshot 2023-08-14 192816.png",
"file_mimetype": "image/png",
"_id": "657cf8d5445ff17a05da6692"
}
],
"notifications": [
{
"message": "APPOINTEMNT CANCELED WITH DOCTOR John Doe1",
"type": "AppointmentCanceled",
"_id": "657dc54aaac6ec25c63fafc5",
"timestamp": "2023-12-16T15:42:02.408Z"
},
{
"message": "APPOINTEMNT RESCHEULED WITH DOCTOR John Doe1",
"type": "AppointmentRescheduled",
"_id": "657dc6a3aac6ec25c6403775",
"timestamp": "2023-12-16T15:47:47.412Z"
},
{
"message": "APPOINTEMNT RESCHEULED WITH DOCTOR John Doe1",
"type": "AppointmentRescheduled",
"_id": "657dc6baaac6ec25c64038af",
"timestamp": "2023-12-16T15:48:10.417Z"
},
{
"message": "APPOINTEMNT RESCHEULED WITH DOCTOR John Doe1",
"type": "AppointmentRescheduled",
"_id": "657dc784aac6ec25c64039f3",
"timestamp": "2023-12-16T15:51:32.861Z"
},
{
"message": "APPOINTEMNT CANCELED WITH DOCTOR John Doe1",
"type": "AppointmentCanceled",
"_id": "657dc8eaaac6ec25c640630e",
"timestamp": "2023-12-16T15:57:30.862Z"
},
{
"message": "APPOINTEMNT CANCELED WITH DOCTOR John Doe1",
"type": "AppointmentCanceled",
"_id": "657dc912aac6ec25c640646b",
"timestamp": "2023-12-16T15:58:10.470Z"
},
{
"message": "APPOINTEMNT CANCELED WITH DOCTOR John Doe1",
"type": "AppointmentCanceled",
"_id": "657dc91eaac6ec25c64065cc",
"timestamp": "2023-12-16T15:58:22.660Z"
},
{
"message": "APPOINTEMNT RESCHEULED WITH DOCTOR John Doe1",
"type": "AppointmentRescheduled",
"_id": "657dc960aac6ec25c640ae9b",
"timestamp": "2023-12-16T15:59:28.716Z"
},
{
"message": "APPOINTEMNT CANCELED WITH DOCTOR John Doe1",
"type": "AppointmentCanceled",
"_id": "657dcd7d5b832d3b9939004c",
"timestamp": "2023-12-16T16:17:01.818Z"
},
{
"message": "APPOINTEMNT CANCELED WITH DOCTOR John Doe1",
"type": "AppointmentCanceled",
"_id": "657dd2352b74acebc2fb8858",
"timestamp": "2023-12-16T16:37:09.293Z"
},
{
"message": "APPOINTEMNT CANCELED WITH DOCTOR John Doe1",
"type": "AppointmentCanceled",
"_id": "657dd2512b74acebc2fb89e1",
"timestamp": "2023-12-16T16:37:37.086Z"
},
{
"message": "APPOINTEMNT CANCELED WITH DOCTOR John Doe1",
"type": "AppointmentCanceled",
"_id": "657dd2572b74acebc2fb8b74",
"timestamp": "2023-12-16T16:37:43.774Z"
},
{
"message": "APPOINTEMNT CANCELED WITH DOCTOR John Doe1",
"type": "AppointmentCanceled",
"_id": "657dd4352b74acebc2fb8da0",
"timestamp": "2023-12-16T16:45:41.459Z"
},
{
"message": "APPOINTEMNT RESCHEULED WITH DOCTOR John Doe1",
"type": "AppointmentRescheduled",
"_id": "657e2fe39cb4286da0a5e358",
"timestamp": "2023-12-16T23:16:51.415Z"
},
{
"message": "APPOINTEMNT RESCHEULED WITH DOCTOR John Doe1",
"type": "AppointmentRescheduled",
"_id": "657e30049cb4286da0a5e3a4",
"timestamp": "2023-12-16T23:17:24.174Z"
},
{
"message": "APPOINTEMNT RESCHEULED WITH DOCTOR John Doe1",
"type": "AppointmentRescheduled",
"_id": "657e30749cb4286da0a5e3f2",
"timestamp": "2023-12-16T23:19:16.487Z"
},
{
"message": "APPOINTEMNT RESCHEULED WITH DOCTOR John Doe1",
"type": "AppointmentRescheduled",
"_id": "657e30819cb4286da0a5e442",
"timestamp": "2023-12-16T23:19:29.415Z"
},
{
"message": "APPOINTEMNT RESCHEULED WITH DOCTOR John Doe1",
"type": "AppointmentRescheduled",
"_id": "657e309f9fdf6b1b45c75fa6",
"timestamp": "2023-12-16T23:19:59.723Z"
},
{
"message": "APPOINTEMNT RESCHEULED WITH DOCTOR John Doe1",
"type": "AppointmentRescheduled",
"_id": "657e30ab9fdf6b1b45c75ffa",
"timestamp": "2023-12-16T23:20:11.804Z"
},
{
"message": "APPOINTEMNT RESCHEULED WITH DOCTOR John Doe1",
"type": "AppointmentRescheduled",
"_id": "657e30d09fdf6b1b45c76050",
"timestamp": "2023-12-16T23:20:48.289Z"
},
{
"message": "APPOINTEMNT RESCHEULED WITH DOCTOR John Doe1",
"type": "AppointmentRescheduled",
"_id": "657e310b51af0e2beeb4721c",
"timestamp": "2023-12-16T23:21:47.091Z"
}
]
}
}
GET /api/pharmacist/pharmacistGetWallet/655a3137acbb95f4558bc8f2
Parameter | Type | Description |
---|---|---|
pId |
ObjectId |
Required. Id of pharmacist |
Response
{
"wallet": 500
}
These 2 channels on YouTube have playlists for : Node, Express, React, Mongo and MERN stack in beginner level.
https://www.youtube.com/channel/UC29ju8bIPH5as8OGnQzwJyA
https://www.youtube.com/channel/UCW5YeuERMmlnqo4oq8vwUpg
Also here are more resources:
Stripe API:
Node.js
https://www.youtube.com/playlist?list=PLZlA0Gpn_vH_uZs4vJMIhcinABSTUH2bY
Express.js
https://www.youtube.com/watch?v=fgTGADljAeg
ES6:
https://www.youtube.com/playlist?list=PLZlA0Gpn_vH-0FlQnruw2rd1HuiYJHHkm
React introduction:
https://www.youtube.com/playlist?list=PLZlA0Gpn_vH_NT5zPVp18nGe_W9LqBDQK
React Hooks -- functional components
https://www.youtube.com/playlist?list=PLZlA0Gpn_vH8EtggFGERCwMY5u5hOjf-h
JWT authentication:
https://www.youtube.com/watch?v=mbsmsi7l3r4
https://www.youtube.com/watch?v=-RCnNyD0L-s
https://dev.to/salarc123/mern-stack-authentication-tutorial-part-1-the-backend-1c57
To be a good developer:
https://www.youtube.com/playlist?list=PLZlA0Gpn_vH_ma_XO-GLSpL9L06ii4mAp
Tests:
https://www.youtube.com/playlist?list=PLZlA0Gpn_vH_63f0HH-dUtkininO7GO6f
To run this project, you will need to add the following environment variables to your .env file
EMAIL_FROM
PORT
MONGO_URI
Password
TOKEN_SECRET
STRIPE_PRIVATE_KEY
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
- @Shark23p4
- @AbdelrahmanAmr20
- @ahany21
- @Ahmed-Elgamel
- @Mazen Mostafa
- @MohamedFayzalla
- @Nardy Michelle
- @Seif555
- @youssefbayoumi
- @Sohailaaa
If you have any feedback, please reach out to us at yelsharkawy42@gmail.com
The Stripe CLI is licensed under the Apache License 2.0 and verified on GitHub as being owned by Stripe.
This project is used by the following companies:
- GUC
This website was created by Team APlusCantLose. Thanks to our product manager Hadwa!