El7a2ny online pharmacy

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.

Motivation

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.

Badges

MongoDB React Express.js JWT Bootstrap Redux NodeJS Git GitHub Stack Overflow

Build Status⚒️

BuildStatus

Project builds successfully and is fully functional. However, project is currently in development and improvements could be made.

Coding Style 📜

The code style is enforced using eslint and prettier. The code style is enforced using pre-commit hooks and pre-commit github action.

Screenshots 📁

HomePage

alt text Register dialog alt text

Register

As patient alt text As pharmacist alt text

Login

alt text Forgot password alt text

Patient

View medicine alt text Change password dialog alt text View cart alt text Checkout alt text View orders alt text Order details alt text Chat alt text

Pharmacist

View medicine alt text Monthly report alt text Chat alt text

Admin

Add admin alt text View pharmacist joing requests alt text View joined pharmacists alt text View patients alt text View medicines alt text Monthly report alt text

Tech/FrameWorks Used 📺

  1. React
  2. Nodejs
  3. MongoDB
  4. Mongoose
  5. Express
  6. axios
  7. bcryptjs
  8. ejs
  9. git
  10. jsonwebtoken
  11. nodemailer
  12. nodemon
  13. stripe
  14. boostrap
  15. alot of react sub libraries can be found in package.json in frontend folder
  16. redux

File Structure

Aplus-Cant-Lose-Pharmacy

Extra features

  1. Prescription gating for certain medicines
  2. Inventory notification via mail
  3. Password reset via mail
  4. Credit card payment via stripe
  5. Tokenized sessions for logins
  6. Redux States

Installation

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 !

Usage

First run the backend.

cd backend

nodemon server

When MongoDB is connected, run the frontend.

cd frontend

npm start

Functions

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

Usage/Examples

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;

API reference

POST /api/login

Params

{
    username,
    password
}

Description

Logs in

POST /api/addPatient

Params

{
    username,
    password
}

Description

Creates a new patient account. Validates input fields, checks for existing username and email, and creates a patient along with a user record.

POST /api/addMedicineToCart/:userId

Params

{
    medicineId
}

Description

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.

DELETE /api/removeMedicineFromCart/:userId

Params

{
    medicineId
}

Description

Removes a medicine from the patient's cart. Validates user and medicine, and updates the cart accordingly.

POST /api/decreaseMedicine/:userId

Params

{
    medicineId
}

Description

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.

GET /api/viewCart/:userId

Params

{
    userId
}

Description

Views the contents of the patient's cart. Retrieves medicine details for each item in the cart and calculates the grand total.

POST /api/addAddress/:userId

Params

{
    location
}

Description

Adds a new address to the patient's addresses array.

GET /api/getAddresses/:userId

Params

{
    userId
}

Description

Gets the addresses associated with the patient.

GET /api/getWallet/:userId

Params

{
    userId
}

Description

Gets the wallet balance of the patient.

POST /api/payForCart/:userId

Params

{
    paymentType,
    address
}

Description

Processes the payment for the items in the patient's cart. Updates medicine quantities, creates a new order, and clears the patient's cart.

GET /api/getPatientOrders/:userId

Params

{
    userId
}

Description

Gets the undelivered orders for the patient.

GET /api/getPastPatientOrders/:userId

Params

{
    userId
}

Description

Gets the delivered orders for the patient.

GET /api/getOrderDetailsById/:orderId

Params

{
    orderId
}

Description

Gets the details of a specific order, including medicine details in the cart.

DELETE /api/cancelOrder/:orderId

Params

{
    orderId
}

Description

Cancels a specific order. Updates medicine quantities, refunds the payment if needed, and deletes the order.

POST /api/createCartCheckoutSession/:userId

Params

{
    address,
    amount
}

Description

Creates a new Stripe checkout session for processing payment.

GET /api/viewMedicineOTC

Params

{
    // No specific parameters
}

Description

Gets unarchived Over the Counter medicines.

GET /api/viewPrescriptionMedicines/:patientId

Params

{
    patientId
}

Description

Gets unarchived prescription medicines for a specific patient.

GET /api/getMedicinesByActiveElement/:medicineId

Params

{
    medicineId
}

Description

Gets other unarchived Over the Counter medicines with the same active element as the specified medicine.

POST /api/addPharmacist

Params

{
  name,
  email,
  username,
  dBirth,
  gender,
  rate,
  affilation,
  background,
  docs,
  password
}

Description

Creates a new pharmacist and user records.

POST /api/addMedicine

Params

{
  activeElement,
  price,
  use,
  name,
  amount,
  imgurl,
  type
}

Description

Adds a new medicine to the system.

GET /api/getAllPharmacistNames

Params

{

}

Description

Gets the names of all pharmacists in the system.

GET /api/getAllDoctorsNames

Params

{

}

Description

Gets the names of all doctors in the system.

PUT /api/updateMedicineDetails/:id

Params

{
  name,
  activeElement,
  price,
  use,
  amount,
  imgurl,
  type
}

Description

Updates details of a specific medicine.

GET /api/getOrdersInMonth

Params

{
  month,
  year
}

Description

Gets orders with medicine information for a specific month.

GET /api/pharmacistGetWallet/:phId

Params

{
  phId: pharmacist_id
}

Description

Gets the wallet information for a specific pharmacist.

GET /api/getMedicinesWithZeroAmount

Params

{

}

Description

Gets all medicines with zero amount in stock.

GET /api/getAdmins

Params

{

}

Description

Gets all admins in the system.

POST /api/createAdmin

Params

{
  username,
  password,
  email
}

Description

Creates a new admin user in the system.

GET /api/viewPendingPharmacists

Params

{
  
}

Description

Gets all pending pharmacists in the system.

GET /api/viewAcceptedPharmacists

Params

{
  
}

Description

Gets all accepted pharmacists in the system.

GET /api/viewPatients

Params

{
  
}

Description

Gets all patients in the system.

DELETE /api/deletePatient/:id

Params

{
    id: patient_id
}

Description

Deletes a specific patient and their associated user account.

DELETE /api/deletePharmacist/:id

Params

{
    id: pharmacist_id
}

Description

Deletes a specific pharmacist and their associated user account.

DELETE /api/deleteAdmin/:id

Params

{
    id: admin_id
}

Description

Deletes a specific admin user.

POST /api/sendAcceptEmail

Params

{
    id
}

Description

Sends an acceptance email to a pharmacist with the specified ID.

POST /api/sendRejectEmail

Params

{
    id
}

Description

Sends an rejection email to a pharmacist with the specified ID.

POST /api/chat/accessChat

Params

{
    userId,
    logId
}

Description

Creates or fetches a one-to-one chat between users.

GET /api/chat/fetchChats/:id

Params

{
  id: user_id
}

Description

Fetches all chats for a user.

POST /api/chat/createGroupChat

Params

{
  users,
  name
}

Description

Creates a new group chat.

PUT /api/chat/renameGroup

Params

{
  chatId,
  chatName
}

Description

Renames a group chat.

PUT /api/chat/removeFromGroup

Params

{
  chatId,
  userId
}

Description

Removes a user from a group.

PUT /api/chat/addToGroup

Params

{
  chatId,
  userId
}

Description

Adds a user to a group or allows a user to leave a group.

GET /api/message/allMessages/:chatId

Params

{
  chatId
}

Description

Gets all messages for a specific chat.

POST /api/message/sendMessage

Params

{
  content,
  chatId,
  logId
}

Description

Creates a new message in a chat.

Tests

  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
}

Useful Links

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:

https://youtu.be/1r-F3FIONl8

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

https://youtu.be/hQAHSlTtcmY

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

Environment Variables

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

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Authors

Feedback

If you have any feedback, please reach out to us at yelsharkawy42@gmail.com

License

The Stripe CLI is licensed under the Apache License 2.0 and verified on GitHub as being owned by Stripe.

Used By

This project is used by the following companies:

  • GUC

Credits

This website was created by Team APlusCantLose. Thanks to our product manager Hadwa!