diff --git a/frontend/src/components/Header/Chat.tsx b/frontend/src/components/Header/Chat.tsx
index c6315068..a6258865 100644
--- a/frontend/src/components/Header/Chat.tsx
+++ b/frontend/src/components/Header/Chat.tsx
@@ -310,9 +310,9 @@ const Chat: React.FC = ({ showChat, setShowChat }) => {
Hi there, I'm {CHATBOT_NAME}!
- You can ask me questions about your uploaded documents.
- I'll search through them to provide accurate, cited
- answers.
+ You can ask me questions about bipolar medications.
+ I'll search through our database of verified medical
+ journal articles to provide accurate, cited answers.
Learn more about my sources.
diff --git a/frontend/src/components/Header/FeatureMenuDropDown.tsx b/frontend/src/components/Header/FeatureMenuDropDown.tsx
index b1bbf03e..36d72792 100644
--- a/frontend/src/components/Header/FeatureMenuDropDown.tsx
+++ b/frontend/src/components/Header/FeatureMenuDropDown.tsx
@@ -4,13 +4,13 @@ export const FeatureMenuDropDown = () => {
const location = useLocation();
const currentPath = location.pathname;
return (
-
-
+
+
Manage files
-
+
Manage and chat with files
@@ -19,7 +19,7 @@ export const FeatureMenuDropDown = () => {
+
diff --git a/frontend/src/pages/Activate/Activate.tsx b/frontend/src/pages/Activate/Activate.tsx
new file mode 100644
index 00000000..391ec04b
--- /dev/null
+++ b/frontend/src/pages/Activate/Activate.tsx
@@ -0,0 +1,76 @@
+import { useEffect, useState } from "react";
+import { useParams, Link } from "react-router-dom";
+import { useDispatch } from "react-redux";
+import { verify, AppDispatch } from "../../services/actions/auth";
+import Layout from "../Layout/Layout";
+import Spinner from "../../components/LoadingSpinner/LoadingSpinner";
+
+const Activate = () => {
+ const { uid, token } = useParams<{ uid: string; token: string }>();
+ const dispatch = useDispatch
();
+ const [status, setStatus] = useState<"loading" | "success" | "error">("loading");
+
+ useEffect(() => {
+ if (!uid || !token) {
+ setStatus("error");
+ return;
+ }
+
+ (async () => {
+ try {
+ await dispatch(verify(uid, token));
+ setStatus("success");
+ } catch {
+ setStatus("error");
+ }
+ })();
+ }, [dispatch, uid, token]);
+
+ if (status === "loading") {
+ return (
+
+
+
+ );
+ }
+
+ if (status === "error") {
+ return (
+
+
+
+
+ Activation failed
+
+
+ This activation link is invalid or has already been used. Please register again or request a new activation email.
+
+
+ Back to register
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+ Email verified
+
+
+ Your account has been activated. You can now log in.
+
+
+ Continue to log in
+
+
+
+
+ );
+};
+
+export default Activate;
diff --git a/frontend/src/pages/DocumentManager/UploadFile.tsx b/frontend/src/pages/DocumentManager/UploadFile.tsx
index 35c4b84f..32b727e8 100644
--- a/frontend/src/pages/DocumentManager/UploadFile.tsx
+++ b/frontend/src/pages/DocumentManager/UploadFile.tsx
@@ -1,5 +1,5 @@
import React, { useState, useRef } from "react";
-import axios from "axios";
+import { adminApi } from "../../api/apiClient";
import TypingAnimation from "../../components/Header/components/TypingAnimation.tsx";
import Layout from "../Layout/Layout.tsx";
@@ -22,16 +22,9 @@ const UploadFile: React.FC = () => {
formData.append("file", file);
try {
- const baseUrl = import.meta.env.VITE_API_BASE_URL;
- const response = await axios.post(
- `${baseUrl}/v1/api/uploadFile`,
+ const response = await adminApi.post(
+ `/api/v1/api/uploadFile`,
formData,
- {
- headers: {
- "Content-Type": "multipart/form-data",
- Authorization: `JWT ${localStorage.getItem("access")}`, // Assuming JWT is used for auth
- },
- }
);
console.log("File uploaded successfully", response.data);
} catch (error) {
diff --git a/frontend/src/pages/DrugSummary/PDFViewer.tsx b/frontend/src/pages/DrugSummary/PDFViewer.tsx
index 39ddfbfc..e4aae111 100644
--- a/frontend/src/pages/DrugSummary/PDFViewer.tsx
+++ b/frontend/src/pages/DrugSummary/PDFViewer.tsx
@@ -10,6 +10,7 @@ import {
import { Document, Page, pdfjs } from "react-pdf";
import { useLocation, useNavigate } from "react-router-dom";
import axios from "axios";
+import { endpoints } from "../../api/endpoints";
import "react-pdf/dist/esm/Page/AnnotationLayer.css";
import "react-pdf/dist/esm/Page/TextLayer.css";
import ZoomMenu from "./ZoomMenu";
@@ -50,11 +51,10 @@ const PDFViewer = () => {
const params = new URLSearchParams(location.search);
const guid = params.get("guid");
const pageParam = params.get("page");
- const baseURL = import.meta.env.VITE_API_BASE_URL as string | undefined;
const pdfUrl = useMemo(() => {
- return guid && baseURL ? `${baseURL}/v1/api/uploadFile/${guid}` : null;
- }, [guid, baseURL]);
+ return guid ? endpoints.uploadFile(guid) : null;
+ }, [guid]);
useEffect(() => setUiScalePct(Math.round(scale * 100)), [scale]);
diff --git a/frontend/src/pages/Files/FileRow.tsx b/frontend/src/pages/Files/FileRow.tsx
index 19665855..57ed66bf 100644
--- a/frontend/src/pages/Files/FileRow.tsx
+++ b/frontend/src/pages/Files/FileRow.tsx
@@ -1,5 +1,6 @@
import React, { useState } from "react";
import { Link } from "react-router-dom";
+import { endpoints } from "../../api/endpoints";
interface File {
id: number;
@@ -42,8 +43,7 @@ const FileRow: React.FC = ({
const handleSave = async () => {
setLoading(true);
try {
- const baseUrl = import.meta.env.VITE_API_BASE_URL as string;
- await fetch(`${baseUrl}/v1/api/editmetadata/${file.guid}`, {
+ await fetch(endpoints.editMetadata(file.guid), {
method: "PATCH",
headers: {
"Content-Type": "application/json",
diff --git a/frontend/src/pages/Files/ListOfFiles.tsx b/frontend/src/pages/Files/ListOfFiles.tsx
index b53874bf..37bd459a 100644
--- a/frontend/src/pages/Files/ListOfFiles.tsx
+++ b/frontend/src/pages/Files/ListOfFiles.tsx
@@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react";
-import { api } from "../../api/apiClient";
+import { publicApi } from "../../api/apiClient";
import Layout from "../Layout/Layout";
import FileRow from "./FileRow";
import Table from "../../components/Table/Table";
@@ -30,14 +30,12 @@ const ListOfFiles: React.FC<{ showTable?: boolean }> = ({
const [downloading, setDownloading] = useState(null);
const [opening, setOpening] = useState(null);
- const baseUrl = import.meta.env.VITE_API_BASE_URL;
-
useEffect(() => {
const fetchFiles = async () => {
try {
- const url = `${baseUrl}/v1/api/uploadFile`;
+ const url = `/api/v1/api/uploadFile`;
- const { data } = await api.get(url);
+ const { data } = await publicApi.get(url);
if (Array.isArray(data)) {
setFiles(data);
@@ -50,7 +48,7 @@ const ListOfFiles: React.FC<{ showTable?: boolean }> = ({
};
fetchFiles();
- }, [baseUrl]);
+ }, []);
const updateFileName = (guid: string, updatedFile: Partial) => {
setFiles((prevFiles) =>
@@ -63,7 +61,7 @@ const ListOfFiles: React.FC<{ showTable?: boolean }> = ({
const handleDownload = async (guid: string, fileName: string) => {
try {
setDownloading(guid);
- const { data } = await api.get(`/v1/api/uploadFile/${guid}`, { responseType: 'blob' });
+ const { data } = await publicApi.get(`/api/v1/api/uploadFile/${guid}`, { responseType: 'blob' });
const url = window.URL.createObjectURL(new Blob([data]));
const link = document.createElement("a");
@@ -84,7 +82,7 @@ const ListOfFiles: React.FC<{ showTable?: boolean }> = ({
const handleOpen = async (guid: string) => {
try {
setOpening(guid);
- const { data } = await api.get(`/v1/api/uploadFile/${guid}`, { responseType: 'arraybuffer' });
+ const { data } = await publicApi.get(`/api/v1/api/uploadFile/${guid}`, { responseType: 'arraybuffer' });
const file = new Blob([data], { type: 'application/pdf' });
const fileURL = window.URL.createObjectURL(file);
diff --git a/frontend/src/pages/Layout/Layout.tsx b/frontend/src/pages/Layout/Layout.tsx
index 3c12358b..02274b78 100644
--- a/frontend/src/pages/Layout/Layout.tsx
+++ b/frontend/src/pages/Layout/Layout.tsx
@@ -1,48 +1,22 @@
// Layout.tsx
-import {ReactNode, useState, useEffect} from "react";
+import {ReactNode} from "react";
import Header from "../../components/Header/Header";
import Footer from "../../components/Footer/Footer";
-import LoginMenuDropDown from "../../components/Header/LoginMenuDropDown";
import {connect} from "react-redux";
import {useAuth} from "./authHooks.ts";
import {RootState} from "../../services/actions/types";
-import {useLocation} from "react-router-dom";
interface LayoutProps {
children: ReactNode;
}
interface LoginFormProps {
- isAuthenticated: boolean;
+ isAuthenticated: boolean | null;
}
export const Layout = ({
- children,
- isAuthenticated,
- }: LayoutProps & LoginFormProps): JSX.Element => {
- const [showLoginMenu, setShowLoginMenu] = useState(false);
- const location = useLocation();
-
-
- useEffect(() => {
- if (!isAuthenticated) {
- if (
- location.pathname === "/login" ||
- location.pathname === "/resetpassword" ||
- location.pathname.includes("password") ||
- location.pathname.includes("reset")
- ) {
- setShowLoginMenu(false);
- } else {
- setShowLoginMenu(true);
- }
- }
- }, [isAuthenticated, location.pathname]);
-
- const handleLoginMenu = () => {
- setShowLoginMenu(!showLoginMenu);
- };
-
+ children
+}: LayoutProps & LoginFormProps): JSX.Element => {
useAuth();
return (
@@ -50,12 +24,6 @@ export const Layout = ({
- {!isAuthenticated && showLoginMenu && (
-
- )}
{children}
diff --git a/frontend/src/pages/Layout/Layout_V2_Header.tsx b/frontend/src/pages/Layout/Layout_V2_Header.tsx
index b510c62d..c896b7b1 100644
--- a/frontend/src/pages/Layout/Layout_V2_Header.tsx
+++ b/frontend/src/pages/Layout/Layout_V2_Header.tsx
@@ -1,38 +1,17 @@
-import { useState, useEffect } from "react";
import { Link, useLocation } from "react-router-dom";
-import LoginMenuDropDown from "../../components/Header/LoginMenuDropDown.tsx";
import { useAuth } from "./authHooks.ts";
import { useGlobalContext } from "../../../src/contexts/GlobalContext.tsx";
interface LoginFormProps {
- isAuthenticated: boolean;
+ isAuthenticated: boolean | null;
}
-const Header: React.FC = ({ isAuthenticated }) => {
- const [showLoginMenu, setShowLoginMenu] = useState(false);
+const Header: React.FC = () => {
const location = useLocation();
const { setShowMetaPanel } = useGlobalContext();
const isOnDrugSummaryPage = location.pathname.includes("/drugsummary");
- useEffect(() => {
- // only show the login menu on non‑auth pages
- if (!isAuthenticated) {
- const path = location.pathname;
- const isAuthPage =
- path === "/login" ||
- path === "/resetpassword" ||
- path.includes("password") ||
- path.includes("reset");
-
- setShowLoginMenu(!isAuthPage);
- }
- }, [isAuthenticated, location.pathname]);
-
- const handleLoginMenu = () => {
- setShowLoginMenu((prev) => !prev);
- };
-
useAuth();
return (
@@ -65,14 +44,6 @@ const Header: React.FC = ({ isAuthenticated }) => {
)}
- {!isAuthenticated && showLoginMenu && (
-
-
-
- )}
);
};
diff --git a/frontend/src/pages/Layout/Layout_V2_Main.tsx b/frontend/src/pages/Layout/Layout_V2_Main.tsx
index 132482b6..2ebad75c 100644
--- a/frontend/src/pages/Layout/Layout_V2_Main.tsx
+++ b/frontend/src/pages/Layout/Layout_V2_Main.tsx
@@ -7,7 +7,7 @@ import Sidebar from "./Layout_V2_Sidebar";
interface LayoutProps {
children: ReactNode;
- isAuthenticated: boolean;
+ isAuthenticated: boolean | null;
}
const Layout: React.FC
= ({ children, isAuthenticated }) => {
diff --git a/frontend/src/pages/Layout/Layout_V2_Sidebar.tsx b/frontend/src/pages/Layout/Layout_V2_Sidebar.tsx
index 19163290..b947c2d6 100644
--- a/frontend/src/pages/Layout/Layout_V2_Sidebar.tsx
+++ b/frontend/src/pages/Layout/Layout_V2_Sidebar.tsx
@@ -24,12 +24,7 @@ const Sidebar: React.FC = () => {
useEffect(() => {
const fetchFiles = async () => {
try {
- const baseUrl = import.meta.env.VITE_API_BASE_URL;
- const response = await axios.get(`${baseUrl}/v1/api/uploadFile`, {
- headers: {
- Authorization: `JWT ${localStorage.getItem("access")}`,
- },
- });
+ const response = await axios.get(`/api/v1/api/uploadFile`);
if (Array.isArray(response.data)) {
setFiles(response.data);
}
diff --git a/frontend/src/pages/ListMeds/useMedications.tsx b/frontend/src/pages/ListMeds/useMedications.tsx
index e15cc758..d78702db 100644
--- a/frontend/src/pages/ListMeds/useMedications.tsx
+++ b/frontend/src/pages/ListMeds/useMedications.tsx
@@ -1,5 +1,5 @@
import { useEffect, useState } from "react";
-import { api } from "../../api/apiClient";
+import { publicApi } from "../../api/apiClient";
export interface MedData {
name: string;
@@ -11,14 +11,12 @@ export function useMedications() {
const [medications, setMedications] = useState([]);
const [errors, setErrors] = useState([]);
- const baseUrl = import.meta.env.VITE_API_BASE_URL;
-
useEffect(() => {
const fetchMedications = async () => {
try {
- const url = `${baseUrl}/v1/api/get_full_list_med`;
+ const url = `/api/v1/api/get_full_list_med`;
- const { data } = await api.get(url);
+ const { data } = await publicApi.get(url);
data.sort((a: MedData, b: MedData) => {
const nameA = a.name.toUpperCase();
@@ -44,7 +42,7 @@ export function useMedications() {
};
fetchMedications();
- }, [baseUrl]);
+ }, []);
console.log(medications);
diff --git a/frontend/src/pages/Login/LoginForm.tsx b/frontend/src/pages/Login/LoginForm.tsx
index d4579ead..1d27aac5 100644
--- a/frontend/src/pages/Login/LoginForm.tsx
+++ b/frontend/src/pages/Login/LoginForm.tsx
@@ -1,6 +1,5 @@
import { useFormik } from "formik";
-// import { Link, useNavigate } from "react-router-dom";
-import { useNavigate } from "react-router-dom";
+import { Link, useNavigate } from "react-router-dom";
import { login, AppDispatch } from "../../services/actions/auth";
import { connect, useDispatch } from "react-redux";
import { RootState } from "../../services/actions/types";
@@ -9,7 +8,7 @@ import ErrorMessage from "../../components/ErrorMessage";
import LoadingSpinner from "../../components/LoadingSpinner/LoadingSpinner";
interface LoginFormProps {
- isAuthenticated: boolean;
+ isAuthenticated: boolean | null;
loginError?: string | null; // Align this with the mapped state
}
@@ -59,10 +58,9 @@ function LoginForm({ isAuthenticated, loginError }: LoginFormProps) {
onSubmit={handleSubmit}
className="mb-4 rounded-md bg-white px-3 pb-12 pt-6 shadow-md ring-1 md:px-12"
>
-
- {/* {errorMessage &&
{errorMessage}
} */}
+
- Welcome
+ Log in
@@ -100,27 +98,21 @@ function LoginForm({ isAuthenticated, loginError }: LoginFormProps) {
- {/*
-
- Forgot Password?
-
- */}
Sign In
+
+
+ Don't have an account? Sign up
+
+
+ Forgot password?
+
+
- { loading && }
-
- {/*
- Don't have an account?{" "}
-
- {" "}
- Register here
-
- .
-
*/}
+ { loading && }
>
);
}
diff --git a/frontend/src/pages/Login/ResetPassword.tsx b/frontend/src/pages/Login/ResetPassword.tsx
index ba57f601..34ffc44b 100644
--- a/frontend/src/pages/Login/ResetPassword.tsx
+++ b/frontend/src/pages/Login/ResetPassword.tsx
@@ -1,19 +1,23 @@
import { useFormik } from "formik";
-import { useNavigate } from "react-router-dom";
+import { useNavigate, Link } from "react-router-dom";
import { reset_password, AppDispatch } from "../../services/actions/auth";
import { connect, useDispatch } from "react-redux";
import { RootState } from "../../services/actions/types";
import { useEffect, useState } from "react";
+import axios from "axios";
+import { AUTH_ENDPOINTS } from "../../api/endpoints";
import Layout from "../Layout/Layout";
interface ResetPasswordProps {
- isAuthenticated: boolean;
+ isAuthenticated: boolean | null;
}
function ResetPassword(props: ResetPasswordProps) {
const { isAuthenticated } = props;
const dispatch = useDispatch();
const [requestSent, setRequestSent] = useState(false);
+ const [submittedEmail, setSubmittedEmail] = useState("");
+ const [resendStatus, setResendStatus] = useState<"idle" | "sent" | "error">("idle");
const navigate = useNavigate();
@@ -29,49 +33,86 @@ function ResetPassword(props: ResetPasswordProps) {
},
onSubmit: (values) => {
dispatch(reset_password(values.email));
+ setSubmittedEmail(values.email);
setRequestSent(true);
},
});
+ const handleResend = async () => {
+ try {
+ await axios.post(AUTH_ENDPOINTS.RESET_PASSWORD, { email: submittedEmail });
+ setResendStatus("sent");
+ } catch {
+ setResendStatus("error");
+ }
+ };
+
if (requestSent) {
- navigate("/");
- }
- return (
- <>
+ return (
-