diff --git a/frontend/src/assets/images/LogRectangle.png b/frontend/src/assets/images/LogRectangle.png new file mode 100644 index 00000000..d670dd3f Binary files /dev/null and b/frontend/src/assets/images/LogRectangle.png differ diff --git a/frontend/src/assets/images/ShopLogBooks.png b/frontend/src/assets/images/ShopLogBooks.png new file mode 100644 index 00000000..361ad31c Binary files /dev/null and b/frontend/src/assets/images/ShopLogBooks.png differ diff --git a/frontend/src/assets/images/adult-cardiac-book.png b/frontend/src/assets/images/adult-cardiac-book.png new file mode 100644 index 00000000..406866a5 Binary files /dev/null and b/frontend/src/assets/images/adult-cardiac-book.png differ diff --git a/frontend/src/assets/images/congenital-cardiac-book.png b/frontend/src/assets/images/congenital-cardiac-book.png new file mode 100644 index 00000000..c34aa252 Binary files /dev/null and b/frontend/src/assets/images/congenital-cardiac-book.png differ diff --git a/frontend/src/assets/images/general-surgery-book.png b/frontend/src/assets/images/general-surgery-book.png new file mode 100644 index 00000000..1a640387 Binary files /dev/null and b/frontend/src/assets/images/general-surgery-book.png differ diff --git a/frontend/src/assets/images/logbooks.png b/frontend/src/assets/images/logbooks.png new file mode 100644 index 00000000..31c6b15f Binary files /dev/null and b/frontend/src/assets/images/logbooks.png differ diff --git a/frontend/src/assets/images/logo.png b/frontend/src/assets/images/logo.png new file mode 100644 index 00000000..2f030bcb Binary files /dev/null and b/frontend/src/assets/images/logo.png differ diff --git a/frontend/src/assets/images/obstetrics-book.png b/frontend/src/assets/images/obstetrics-book.png new file mode 100644 index 00000000..7fe2b88a Binary files /dev/null and b/frontend/src/assets/images/obstetrics-book.png differ diff --git a/frontend/src/assets/images/ophthalmology-book.png b/frontend/src/assets/images/ophthalmology-book.png new file mode 100644 index 00000000..04777f67 Binary files /dev/null and b/frontend/src/assets/images/ophthalmology-book.png differ diff --git a/frontend/src/components/Buttons/CLButtons.jsx b/frontend/src/components/Buttons/CLButtons.jsx index 036bdd4e..311ece25 100644 --- a/frontend/src/components/Buttons/CLButtons.jsx +++ b/frontend/src/components/Buttons/CLButtons.jsx @@ -1,8 +1,12 @@ -import "./CLButtons.css" +import "./CLButtons.css"; -const DEFAULT_HEIGHT = "54px" -const DEFAULT_WIDTH = "fit-content" -const DEFAULT_ON_CLICK = () => {} +const DEFAULT_PRIMARY_CLASSNAME = "primary-variant-button"; +const DEFAULT_SECONDARY_CLASSNAME = "secondary-variant-button"; + +const DEFAULT_HEIGHT = "54px"; +const DEFAULT_WIDTH = "fit-content"; +const DEFAULT_TYPE = "button"; +const DEFAULT_ON_CLICK = () => {}; /** * PRIMARY variant of the clinical logging button. @@ -11,24 +15,30 @@ const DEFAULT_ON_CLICK = () => {} */ export const CLButtonPrimary = ({ children, + className, height = DEFAULT_HEIGHT, width = DEFAULT_WIDTH, - onClick = DEFAULT_ON_CLICK + type = DEFAULT_TYPE, + onClick = DEFAULT_ON_CLICK, }) => { + const updatedClassName = className + ? DEFAULT_PRIMARY_CLASSNAME + " " + className + : DEFAULT_PRIMARY_CLASSNAME; return ( - - ) -} + ); +}; /** * SECONDARY variant of the clinical logging button. @@ -37,21 +47,27 @@ export const CLButtonPrimary = ({ */ export const CLButtonSecondary = ({ children, + className, height = DEFAULT_HEIGHT, width = DEFAULT_WIDTH, - onClick = DEFAULT_ON_CLICK + type = DEFAULT_TYPE, + onClick = DEFAULT_ON_CLICK, }) => { - + const updatedClassName = className + ? DEFAULT_SECONDARY_CLASSNAME + " " + className + : DEFAULT_SECONDARY_CLASSNAME; + return ( - - ) -} \ No newline at end of file + ); +}; diff --git a/frontend/src/components/ContentHeader/ContentHeader.css b/frontend/src/components/ContentHeader/ContentHeader.css new file mode 100644 index 00000000..76a5492a --- /dev/null +++ b/frontend/src/components/ContentHeader/ContentHeader.css @@ -0,0 +1,142 @@ +.content-header { + position: absolute; + left: 230px; + top: 18%; + display: flex; + width: 80%; + justify-content: space-between; + align-items: center; + padding-bottom: 4px; + border-bottom: 1px solid #9AB0E1; +} + +.content-header h2 { + font-size: 24px; + font-weight: 600; + color: #1E1E1E; +} + +.content-header[data-page="upload-photo"] h2 { + border-bottom: 2px solid #2B4B96; +} + +.button-group { + display: flex; + gap: 12px; /* Space between buttons */ + align-items: center; +} + +.actions-button { + background: white; + color: #000000; /* SECONDARY_COLOR */ + border-radius: 20px; /* DEFAULT_BORDER_RADIUS */ + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); + height: 40px; + padding: 0 16px; + display: flex; + align-items: center; + gap: 8px; + cursor: pointer; + font-size: 15px; + font-weight: 600; +} + +.actions-button:hover { + background: #f0f5ff; +} + +.add-button { + background: #244B94; /* PRIMARY_BACKGROUND_COLOR */ + color: #F7FAFF; /* PRIMARY_COLOR */ + border-radius: 20px; /* DEFAULT_BORDER_RADIUS */ + width: 120px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + gap: 6px; + cursor: pointer; + font-size: 15px; + font-weight: 600; +} + +.add-book-button:hover { + opacity: 0.9; +} + +.plus-icon { + width: 18px; + height: 18px; +} + +.down-icon { + width: 18px; + height: 18px; +} + +.back-button { + background: none; + border: none; + padding: 0.5rem; + cursor: pointer; + color: #2B4B96; +} + +.preview-button { + background: none; + border: none; + padding: 0.5rem; + cursor: pointer; + color: #333333; + font-weight: 400; + font-size: 1.3rem; + display: flex; + align-items: center; + gap: 10px; + flex-direction: row-reverse; +} + +.preview-icon { + width: 30px; + height: 30px; + stroke-width: 2; + color: #244B94; +} + +.upload-back-icon { + width: 30px; + height: 30px; + stroke-width: 3; + color: #2B4B96; + transition: transform 0.2s ease; +} + +.upload-back-icon:hover { + transform: translateX(-5px); +} + +.title-wrapper { + display: flex; + justify-content: center; +} + +.upload-photo-title { + color: #1E1E1E; + font-size: 1.8rem; + font-weight: 600; + margin: 0; + padding-bottom: 0.5rem; + width: fit-content; + border-bottom: 2px solid #ECEBED; +} + +.upload-photo-header { + position: absolute; + left: 230px; + top: 18%; + display: flex; + width: 80%; + justify-content: space-between; + align-items: center; + padding-bottom: 4px; +} \ No newline at end of file diff --git a/frontend/src/components/ContentHeader/ContentHeader.jsx b/frontend/src/components/ContentHeader/ContentHeader.jsx new file mode 100644 index 00000000..a546eb0a --- /dev/null +++ b/frontend/src/components/ContentHeader/ContentHeader.jsx @@ -0,0 +1,63 @@ +import { useLocation, useNavigate } from "react-router-dom"; +import { + PlusIcon, + ChevronDownIcon, + ChevronDoubleLeftIcon, + ChevronLeftIcon, +} from "@heroicons/react/24/outline"; +import "./ContentHeader.css"; + +export default function ContentHeader({ onPreviewClick }) { + const location = useLocation(); + const navigate = useNavigate(); + const isHistory = location.pathname === "/history"; + const isUploadPhoto = location.pathname === "/upload-photo"; + + if (isUploadPhoto) { + return ( +
+ +
+

Upload Photos

+
+ +
+ ); + } + + return ( +
+

{isHistory ? "Log History" : "Logbooks"}

+
+ {}} + /> + {}} /> +
+
+ ); +} + +function AddButton({ variant = "logbook", onClick = () => {} }) { + return ( + + ); +} + +function ActionsButton({ onClick = () => {} }) { + return ( + + ); +} diff --git a/frontend/src/components/Home/Header/WelcomeSection.css b/frontend/src/components/Home/Header/WelcomeSection.css new file mode 100644 index 00000000..e7d52a79 --- /dev/null +++ b/frontend/src/components/Home/Header/WelcomeSection.css @@ -0,0 +1,19 @@ +.welcome-section { + position: absolute; + left: 230px; + top: 12%; + display: flex; + align-items: center; +} + +.welcome-text { + font-weight: 300; + font-size: 25px; + color: #1e1e1e; +} + +.user-name { + font-weight: bold; + font-size: 25px; + color: #1e1e1e; +} diff --git a/frontend/src/components/Home/Header/WelcomeSection.jsx b/frontend/src/components/Home/Header/WelcomeSection.jsx new file mode 100644 index 00000000..55bf8608 --- /dev/null +++ b/frontend/src/components/Home/Header/WelcomeSection.jsx @@ -0,0 +1,12 @@ +import "./WelcomeSection.css"; + +export default function WelcomeSection({ firstName }) { + return ( +
+

+ Welcome back, + {firstName} +

+
+ ); +} diff --git a/frontend/src/components/Home/LeftSection/BottomSection.css b/frontend/src/components/Home/LeftSection/BottomSection.css new file mode 100644 index 00000000..355ce7a2 --- /dev/null +++ b/frontend/src/components/Home/LeftSection/BottomSection.css @@ -0,0 +1,8 @@ +.bottom-section { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 30px; + margin-top: 30px; + width: 95%; + height: calc(500px - 280px - 24px); +} diff --git a/frontend/src/components/Home/LeftSection/BottomSection.jsx b/frontend/src/components/Home/LeftSection/BottomSection.jsx new file mode 100644 index 00000000..f96b659c --- /dev/null +++ b/frontend/src/components/Home/LeftSection/BottomSection.jsx @@ -0,0 +1,12 @@ +import ShopBooksCard from "./ShopBooksCard"; +import RecentActivityCard from "./RecentActivityCard"; +import "./BottomSection.css"; + +export default function BottomSection({ recentActivities }) { + return ( +
+ + +
+ ); +} diff --git a/frontend/src/components/Home/LeftSection/GetStartedCard.css b/frontend/src/components/Home/LeftSection/GetStartedCard.css new file mode 100644 index 00000000..a868c80e --- /dev/null +++ b/frontend/src/components/Home/LeftSection/GetStartedCard.css @@ -0,0 +1,31 @@ +.get-started-card { + background: white; + border-radius: 30px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); + width: 85%; + padding: 20px 30px; + height: 50%; +} + +.get-started-card h2 { + font-size: 20px; + font-weight: 600; + margin-top: 0; + color: #2f3c50; +} + +.get-started-card p { + color: #3c4049; + font-weight: 400; + text-align: left; + font-size: 14px; + margin-bottom: 16px; +} + +.button-stack { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + height: 55%; +} diff --git a/frontend/src/components/Home/LeftSection/GetStartedCard.jsx b/frontend/src/components/Home/LeftSection/GetStartedCard.jsx new file mode 100644 index 00000000..7fe2254a --- /dev/null +++ b/frontend/src/components/Home/LeftSection/GetStartedCard.jsx @@ -0,0 +1,27 @@ +import { CLButtonSecondary } from "../../Buttons/CLButtons"; +import { NewLogModal } from "../../NewLogModal/NewLogModal"; +import "./GetStartedCard.css"; + +export default function GetStartedCard({ + handleAddLogbook, + handleViewHistory, +}) { + return ( +
+

Get Started

+

+ Convert handwritten clinical logs to standardized excel templates with + just a click of a button! +

+
+ + + Add Logbook + + + View Log History + +
+
+ ); +} diff --git a/frontend/src/components/Home/LeftSection/MainContent.css b/frontend/src/components/Home/LeftSection/MainContent.css new file mode 100644 index 00000000..08628568 --- /dev/null +++ b/frontend/src/components/Home/LeftSection/MainContent.css @@ -0,0 +1,13 @@ +.dashboard-container { + padding: 16px; + margin-left: 65px; + max-width: 1400px; +} + +.content-grid { + display: grid; + grid-template-columns: 1fr 1.2fr; + gap: 24px; + margin-top: 5%; + height: auto; +} \ No newline at end of file diff --git a/frontend/src/components/Home/LeftSection/MainContent.jsx b/frontend/src/components/Home/LeftSection/MainContent.jsx new file mode 100644 index 00000000..4b416ef4 --- /dev/null +++ b/frontend/src/components/Home/LeftSection/MainContent.jsx @@ -0,0 +1,87 @@ +import { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { useAuth } from "../../../contexts/AuthContext"; +import WelcomeSection from "../Header/WelcomeSection"; +import GetStartedCard from "./GetStartedCard"; +import BottomSection from "./BottomSection"; +import LogbooksCard from "../RightSection/LogbooksCard"; +import "./MainContent.css"; + +export default function MainContent() { + const navigate = useNavigate(); + const [setSelectedLog] = useState(null); + const { session } = useAuth(); + + const handleAddLogbook = () => { + navigate("/newLog"); + }; + + const handleViewHistory = () => { + navigate("/history"); + }; + + const recentActivities = [ + { + id: 1, + action: "Added Log", + logName: "mylogexample", + time: "1d", + }, + { + id: 2, + action: "Added Log", + logName: "mylogexample", + time: "1d", + }, + { + id: 3, + action: "Added Log", + logName: "mylogexample", + time: "1d", + }, + // Add more items as needed + ]; + + const progressItems = [ + { + id: 1, + name: "Adult Cardiac 2025", + progress: 65, + }, + { + id: 2, + name: "Adult Cardiac 2025", + progress: 65, + }, + { + id: 3, + name: "Adult Cardiac 2025", + progress: 65, + }, + // Add more items as needed + ]; + + return ( +
+ + +
+
+ + + +
+ + +
+
+ ); +} diff --git a/frontend/src/components/Home/LeftSection/RecentActivityCard.css b/frontend/src/components/Home/LeftSection/RecentActivityCard.css new file mode 100644 index 00000000..692ecea8 --- /dev/null +++ b/frontend/src/components/Home/LeftSection/RecentActivityCard.css @@ -0,0 +1,84 @@ +.recent-activity-card { + background: white; + border-radius: 30px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); + padding: 20px; + height: 83%; + max-height: 300px; + overflow-y: auto; + transition: transform 0.2s ease; +} + +.recent-activity-card:hover { + transform: translateY(-2px); +} + +.activity-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.activity-header h3 { + font-size: 20px; + font-weight: 600; + margin-top: 0; + color: #2f3c50; +} + +.activity-header .chevron-icon { + width: 20px; + height: 20px; + color: black; + margin-top: -20px; +} + +.activity-list { + display: flex; + flex-direction: column; + margin-top: -10px; +} + +.activity-item { + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid #e0e0e0; +} + +.activity-info { + display: flex; + gap: 5px; + color: #666; + font-size: 12px; +} + +.activity-info h2 { + font-weight: 500; + font-size: 12px; + color: #2f3c50; + opacity: 0.8; +} + +.activity-info h3 { + font-weight: 700; + font-size: 12px; + color: #2e4a8f; + margin-top: 10px; +} + +.activity-time { + margin-left: 20px; +} + +.activity-time h3 { + font-weight: 400; + font-size: 12px; + color: #a1b4de; +} + +.time-icon { + color: #92a4cb; + width: 14px; + height: 14px; +} diff --git a/frontend/src/components/Home/LeftSection/RecentActivityCard.jsx b/frontend/src/components/Home/LeftSection/RecentActivityCard.jsx new file mode 100644 index 00000000..fe1fb452 --- /dev/null +++ b/frontend/src/components/Home/LeftSection/RecentActivityCard.jsx @@ -0,0 +1,29 @@ +import { ChevronRightIcon, ClockIcon } from "@heroicons/react/24/outline"; +import "./RecentActivityCard.css"; + +export default function RecentActivityCard({ recentActivities }) { + return ( + +
+
+

Recent Activity

+ +
+
+ {recentActivities.map((activity) => ( +
+
+

{activity.action}:

+

{activity.logName}

+
+
+

{activity.time}

+
+ +
+ ))} +
+
+
+ ); +} diff --git a/frontend/src/components/Home/LeftSection/ShopBooksCard.css b/frontend/src/components/Home/LeftSection/ShopBooksCard.css new file mode 100644 index 00000000..f22af55d --- /dev/null +++ b/frontend/src/components/Home/LeftSection/ShopBooksCard.css @@ -0,0 +1,58 @@ +.shop-books-card { + background: linear-gradient(232.43deg, #6c8bd3 9.4%, #244b94 90.17%); + border-radius: 30px; + padding: 20px; + color: white; + display: flex; + justify-content: space-between; + cursor: pointer; + height: 83%; + position: relative; + overflow: hidden; + transition: transform 0.2s ease; +} + +.shop-books-card:hover { + transform: translateY(-2px); +} + +.shop-books-card::after { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: radial-gradient( + circle at bottom, + rgba(255, 255, 255, 0) 30%, + rgba(255, 255, 255, 0.2) 100% + ); + pointer-events: none; +} + +.shop-books-card h3 { + font-size: 20px; + font-weight: 600; + margin-top: 0; + color: white; +} + +.shop-books-card .chevron-icon { + width: 20px; + height: 20px; + color: white; + margin-top: 4px; +} + +.shop-books-image { + position: absolute; + top: 43%; + left: 1px; + right: 1px; + width: 250px; + height: auto; + object-fit: contain; + z-index: 1; +} + \ No newline at end of file diff --git a/frontend/src/components/Home/LeftSection/ShopBooksCard.jsx b/frontend/src/components/Home/LeftSection/ShopBooksCard.jsx new file mode 100644 index 00000000..09411282 --- /dev/null +++ b/frontend/src/components/Home/LeftSection/ShopBooksCard.jsx @@ -0,0 +1,19 @@ +import { ChevronRightIcon } from "@heroicons/react/24/outline"; +import ShopLogBooks from "../../../assets/images/ShopLogBooks.png"; +import "./ShopBooksCard.css"; + +export default function ShopBooksCard() { + return ( + +
+

Shop Log Books

+ + Shop Log Books +
+
+ ); +} diff --git a/frontend/src/components/Home/RightSection/LogbooksCard.css b/frontend/src/components/Home/RightSection/LogbooksCard.css new file mode 100644 index 00000000..39c243dd --- /dev/null +++ b/frontend/src/components/Home/RightSection/LogbooksCard.css @@ -0,0 +1,52 @@ +.logbooks-card { + background: white; + border-radius: 30px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); + width: 85%; + padding: 24px; + height: 93%; +} + +.card-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-left: 30px; +} + +.card-header h2 { + font-size: 20px; + font-weight: 600; + margin-top: 0; + margin-bottom: 8px; + color: #2f3c50; +} + +.view-more-btn { + padding: 6px 12px; + border-radius: 20px; + border: 1px solid #9ab0e1; + background: #f7faff; + color: #4f607e; + font-size: 15px; + font-weight: bold; + cursor: pointer; + margin-right: 30px; +} + +.logbooks-image { + width: 100%; + max-width: 280px; + margin: 30px auto; + display: block; +} + +.progress-list { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + margin: 0 auto; + gap: 20px; + width: 80%; +} diff --git a/frontend/src/components/Home/RightSection/LogbooksCard.jsx b/frontend/src/components/Home/RightSection/LogbooksCard.jsx new file mode 100644 index 00000000..ec14fb99 --- /dev/null +++ b/frontend/src/components/Home/RightSection/LogbooksCard.jsx @@ -0,0 +1,26 @@ +import LogBooks from "../../../assets/images/logbooks.png"; +import ProgressItem from "./ProgressItem"; +import "./LogbooksCard.css"; + +export default function LogbooksCard({ progressItems, setSelectedLog }) { + return ( +
+
+

Log Books

+ +
+
+ Logbooks +
+ {progressItems.map((item) => ( + setSelectedLog(item.id)} + /> + ))} +
+
+
+ ); +} diff --git a/frontend/src/components/Home/RightSection/ProgressItem.css b/frontend/src/components/Home/RightSection/ProgressItem.css new file mode 100644 index 00000000..bd00ac29 --- /dev/null +++ b/frontend/src/components/Home/RightSection/ProgressItem.css @@ -0,0 +1,40 @@ +.progress-item { + width: 100%; + padding: 12px; + border-radius: 8px; + cursor: pointer; + transition: background-color 0.2s ease; + background-color: transparent; +} + +.progress-item:hover { + background-color: #e7edf8; +} + +.progress-item:hover .progress-bar { + background: #fbfcfe; +} + +.progress-item:hover .progress-info { + color: #244b94; +} + +.progress-info { + display: flex; + justify-content: space-between; + margin-bottom: 6px; + font-size: 15px; +} + +.progress-bar { + height: 8px; + background: #e7edf8; + border-radius: 4px; + overflow: hidden; +} + +.progress-fill { + height: 100%; + background: #244b94; + border-radius: 4px; +} diff --git a/frontend/src/components/Home/RightSection/ProgressItem.jsx b/frontend/src/components/Home/RightSection/ProgressItem.jsx new file mode 100644 index 00000000..1a67a04c --- /dev/null +++ b/frontend/src/components/Home/RightSection/ProgressItem.jsx @@ -0,0 +1,18 @@ +import "./ProgressItem.css"; + +export default function ProgressItem({ item, onClick }) { + return ( +
+
+ {item.name} + {item.progress}% +
+
+
+
+
+ ); +} diff --git a/frontend/src/components/LogHistory/LogTable.css b/frontend/src/components/LogHistory/LogTable.css index d45b08b8..86df3e5c 100644 --- a/frontend/src/components/LogHistory/LogTable.css +++ b/frontend/src/components/LogHistory/LogTable.css @@ -1,52 +1,76 @@ -.log-table { - width: 100%; /* Adjust width as needed */ +/* Logs Table */ +.logs-table { + width: 100%; border-collapse: collapse; - margin-top: 200px; -} - -.log-table th, .log-table td { - padding: 0.75em; + table-layout: fixed; + border-radius: 20px; + box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.1); + } + + .logs-table th { + background: #333A3F; + color: white; text-align: left; - border-bottom: 1px solid #444; -} - -.log-table th { - background-color: #333; -} - -.log-table .actions-header, -.log-table .actions-cell { - text-align: right; - padding-right: 1em; /* Add some padding to push it away from the edge */ -} - -.log-table tr:hover { - background-color: #2c2c2c; -} - -.action-button { - background: none; - border: none; - color: #e0e0e0; - cursor: pointer; - margin-left: -0.5em; -} - -.action-button:hover { - color: #646cff; -} - -.log-title-cell { - width: 30%; /* Adjust based on how much space you want for titles */ + padding: 13px 24px; + font-weight: 700; + font-size: 17px; + white-space: nowrap; + letter-spacing: 0.5px; + } + + .logs-table th:first-child { + border-top-left-radius: 20px; + } + + .logs-table th:last-child { + border-top-right-radius: 20px; + } + + .logs-table td { + color: #1E1E1E; + font-size: 15px; + padding: 8px 24px; + } + + .logs-table td:not(.checkbox-column) { text-align: left; -} - -.date-created-cell { - width: 25%; - text-align: center; -} - -.date-operation-cell { - width: 25%; - text-align: center; -} + padding: 16px 24px; + } + + /* Selected Row */ + .logs-table tr.selected { + background-color: #D3E4FF; + } + + /* Hover and Even Rows */ + .logs-table tr:not(.selected):nth-child(even), + .logs-table tr:not(.selected):hover { + background-color: #F2F5FF; + } + + /* Column Widths */ + .checkbox-column { + width: 64px; + } + + .log-title-column, + .type-column, + .date-column { + width: calc((100% - 64px) / 3); + } + + /* Title Column */ + .title-column { + color: #64B2F6 !important; + } + + /* Sort Icon */ + .sort-icon { + width: 20px; + height: 20px; + display: inline-block; + vertical-align: middle; + margin-left: 5px; + margin-bottom: 3px; + stroke-width: 2.5; + } \ No newline at end of file diff --git a/frontend/src/components/LogHistory/LogTable.jsx b/frontend/src/components/LogHistory/LogTable.jsx index 378315a4..9312a733 100644 --- a/frontend/src/components/LogHistory/LogTable.jsx +++ b/frontend/src/components/LogHistory/LogTable.jsx @@ -1,41 +1,51 @@ -import DownloadIcon from "@mui/icons-material/Download"; -import VisibilityIcon from "@mui/icons-material/Visibility"; -import DeleteIcon from "@mui/icons-material/Delete"; +import { ChevronUpDownIcon } from "@heroicons/react/24/outline"; import "./LogTable.css"; -const LogTable = ({ logs }) => ( -
- +export default function LogTable({ + currentLogs, + selectedLogs, + handleSelectAll, + handleSelectLog, + allSelected, +}) { + return ( +
- - - - + + + + - {logs.map((log) => ( - - - - - + + + + ))}
Log TitleDate of OperationDate CreatedActions + + + LOG TITLE + + TYPE + + DATE CREATED +
{log.title}{log.dateOfOperation}{log.date} - - - + {currentLogs.map((log) => ( +
+ handleSelectLog(log.id)} + /> {log.title}{log.type}{log.dateCreated}
-
-); - -export default LogTable; + ); +} diff --git a/frontend/src/components/LogHistory/Pagination.css b/frontend/src/components/LogHistory/Pagination.css index 36eb5f0d..13e0c6c4 100644 --- a/frontend/src/components/LogHistory/Pagination.css +++ b/frontend/src/components/LogHistory/Pagination.css @@ -1,35 +1,37 @@ .pagination { display: flex; - justify-content: space-between; + gap: 8px; align-items: center; - margin-top: 1em; - color: #1a1a1a; -} - -.pagination span { - font-size: 0.9em; -} - -.page-buttons { + } + + .pagination span { display: flex; - gap: 0.5em; -} - -.page-buttons button { - background: #e0e0e0; - color: #1a1a1a; - padding: 0.4em 0.6em; + align-items: center; + justify-content: center; cursor: pointer; - border-radius: 4px; -} - -.page-buttons button.active { - background-color: #646cff; - color: #fff; - font-weight: bold; -} - -.page-buttons button:disabled { - color: #777; - cursor: not-allowed; -} + padding: 4px 8px; + color: #64B2F6; + font-size: 14px; + min-width: 13px; + height: 20px; + } + + .pagination .current-page { + background-color: #244B94; + color: white !important; + font-weight: 500; + border-radius: 50%; + } + + .pagination span.next, + .pagination span.previous { + min-width: auto; + height: auto; + color: #244B94; + font-weight: 500; + } + + /* Hover state for pagination */ + .pagination span:hover:not(.current-page):not(.next):not(.previous) { + color: #244B94; + } \ No newline at end of file diff --git a/frontend/src/components/LogHistory/Pagination.jsx b/frontend/src/components/LogHistory/Pagination.jsx index 54e651ce..3ee20a4b 100644 --- a/frontend/src/components/LogHistory/Pagination.jsx +++ b/frontend/src/components/LogHistory/Pagination.jsx @@ -1,40 +1,33 @@ import "./Pagination.css"; -const Pagination = ({ currentPage, totalLogs, logsPerPage, onPageChange }) => { - const totalPages = Math.ceil(totalLogs / logsPerPage); - const startLog = (currentPage - 1) * logsPerPage + 1; - const endLog = Math.min(startLog + logsPerPage - 1, totalLogs); - +export default function Pagination({ + currentPage, + totalPages, + handleNextPage, + handlePreviousPage, + handlePageClick, +}) { return (
- - Showing {startLog} to {endLog} of {totalLogs} logs - -
- - {Array.from({ length: totalPages }, (_, index) => ( - - ))} - -
+ + )}
); -}; - -export default Pagination; +} diff --git a/frontend/src/components/LogHistory/SearchFilterSort.css b/frontend/src/components/LogHistory/SearchFilterSort.css deleted file mode 100644 index 6462ac53..00000000 --- a/frontend/src/components/LogHistory/SearchFilterSort.css +++ /dev/null @@ -1,68 +0,0 @@ -.search-filter-sort { - display: flex; - justify-content: space-between; - margin: 1em 0; - margin-top: 200px; - margin-bottom: -11em; -} - -/* Styling for search input and sort dropdown */ -.search-filter-sort input[type="text"], -.search-filter-sort select { - padding: 0.6rem; - background-color: #F7FAFF; - color: #666B6F; - border: 1px solid #444; - border-radius: 4px; -} - -/* Filter button to open/close dropdown */ -.filter-button { - padding: 0.6rem; - background-color: #e0e0e0; - color: #444; - border: none; - cursor: pointer; - border-radius: 4px; -} - -.filter-button:hover { - background-color: #555; -} - -/* Dropdown Menu Styling */ -.filter-dropdown { - position: fixed; - padding: 1em; - background-color: #2c2c2c; - border: 1px solid #444; - border-radius: 4px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); - display: flex; - flex-direction: column; - gap: 1em; - z-index: 1000; - font-weight: 600; - margin-left: 826px; - margin-top: 40px; -} - -/* Dropdown content styles */ -.filter-group { - display: flex; - flex-direction: column; -} - -.filter-group label { - font-size: 0.9em; - color: #e0e0e0; - margin-bottom: 0.25em; -} - -input[type="date"] { - padding: 0.5rem; - background-color: #2c2c2c; - color: #e0e0e0; - border: 1px solid #444; - border-radius: 4px; -} diff --git a/frontend/src/components/LogHistory/SearchFilterSort.jsx b/frontend/src/components/LogHistory/SearchFilterSort.jsx deleted file mode 100644 index db55c4ce..00000000 --- a/frontend/src/components/LogHistory/SearchFilterSort.jsx +++ /dev/null @@ -1,80 +0,0 @@ -import { useState } from "react"; -import "./SearchFilterSort.css"; - -const SearchFilterSort = ({ - search, - setSearch, - sort, - setSort, - startDate, - setStartDate, - endDate, - setEndDate, - patientName, - setPatientName, -}) => { - const [isDropdownOpen, setIsDropdownOpen] = useState(false); - - const toggleDropdown = () => { - setIsDropdownOpen((prev) => !prev); - }; - - return ( -
- setSearch(e.target.value)} - className="search-input" - /> - - {/* Sort By dropdown comes before Filters button */} - - - - - {/* Dropdown Menu for Advanced Filters */} - {isDropdownOpen && ( -
-
- - setStartDate(e.target.value)} - /> - to - setEndDate(e.target.value)} - /> -
- -
- - setPatientName(e.target.value)} - /> -
-
- )} -
- ); -}; - -export default SearchFilterSort; diff --git a/frontend/src/components/Logbooks/AddLogbookCard.css b/frontend/src/components/Logbooks/AddLogbookCard.css new file mode 100644 index 00000000..14f7eaa6 --- /dev/null +++ b/frontend/src/components/Logbooks/AddLogbookCard.css @@ -0,0 +1,37 @@ +.add-logbook-card { + border-radius: 30px; + max-width: 300px; + padding: 20px; + position: relative; + cursor: pointer; + transition: transform 0.2s; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + } + + .add-logbook-card:hover { + transform: translateY(-4px); + } + + /* Text Styles */ + .add-logbook-card span { + color: #1f2937; + font-size: 14px; + font-weight: 400; + } + + /* Icon Styles */ + .plus-circle-icon { + width: 40px; + height: 40px; + color: #244b94; + padding: 8px; + border: 4px solid #244b94; + border-radius: 50%; + stroke-width: 3px; + } + \ No newline at end of file diff --git a/frontend/src/components/Logbooks/AddLogbookCard.jsx b/frontend/src/components/Logbooks/AddLogbookCard.jsx new file mode 100644 index 00000000..38c46a6e --- /dev/null +++ b/frontend/src/components/Logbooks/AddLogbookCard.jsx @@ -0,0 +1,11 @@ +import { PlusIcon } from "@heroicons/react/24/outline"; +import "./AddLogbookCard.css"; + +export default function AddLogbookCard() { + return ( +
+ + Add Log Book +
+ ); +} diff --git a/frontend/src/components/Logbooks/LogbookCard.css b/frontend/src/components/Logbooks/LogbookCard.css new file mode 100644 index 00000000..179b904a --- /dev/null +++ b/frontend/src/components/Logbooks/LogbookCard.css @@ -0,0 +1,134 @@ +/* Base Card Styles */ +.logbook-card { + border-radius: 30px; + max-width: 300px; + padding: 20px; + position: relative; + cursor: pointer; + transition: transform 0.2s; + background: #ebf2fd; + backdrop-filter: blur(20px); + box-shadow: + 0px 16px 60px 0px #cdd8f040, + 0px 0px 60px 0px #ffffff80 inset; + height: 300px; + } + + .logbook-card:hover .book-cover { + transform: translateY(-10px); + } + + /* Logbook Card Variants */ + .logbook-card.congenital { + background: #fbf4ff; + } + + .logbook-card.ophthalmology { + background: #ebfbfe; + } + + .logbook-card.obstetrics { + background: #f0edf3; + } + + .logbook-card.general-surgery { + background: #e8fdff; + } + + /* Book Cover Styles */ + .book-cover { + position: relative; + height: 100%; + overflow: hidden; + transition: transform 0.2s; + } + + .book-cover-image { + width: 60%; + margin-top: 10%; + margin-left: 20%; + object-fit: cover; + clip-path: inset(0 0 30% 0); + } + + .book-cover-image.obstetrics { + width: 30%; + margin-top: 90%; + } + + .log-rectangle { + width: 170%; + height: 240%; + padding: 13px; + object-fit: cover; + position: absolute; + bottom: 0; + right: -57%; + } + + /* Details Container */ + .details-container { + position: relative; + height: 140px; + } + + .book-details { + position: relative; + padding: 10px; + bottom: 100%; + z-index: 2; + align-items: flex-start; + } + + /* Text Styles */ + .book-title { + font-size: 22px; + font-weight: bold; + color: #1e1e1e; + margin-bottom: 12px; + text-align: left; + } + + .type-label, + .storage-info { + font-size: 14px; + font-weight: 500; + text-align: left; + display: flex; + align-items: center; + gap: 4px; + } + + .type-label { + color: #333333; + } + + .storage-info { + margin: 8px 0; + } + + .type-value, + .storage-count { + font-size: 14px; + font-weight: 500; + } + + .type-value { + color: #244b94; + } + + .storage-count { + color: #5d9eff; + } + + .created-date { + font-size: 10px; + color: #656b6f; + margin-top: 8px; + text-align: left; + } + + .created-date strong { + font-weight: 600; + } + \ No newline at end of file diff --git a/frontend/src/components/Logbooks/LogbookCard.jsx b/frontend/src/components/Logbooks/LogbookCard.jsx new file mode 100644 index 00000000..c7c11897 --- /dev/null +++ b/frontend/src/components/Logbooks/LogbookCard.jsx @@ -0,0 +1,40 @@ +import { LogbookTypeInfo } from "./LogbookTypeInfo"; +import LogRectangle from "../../assets/images/LogRectangle.png"; +import "./LogbookCard.css"; + +export default function LogbookCard({ title, type, storage, created }) { + /** Retrieve type information from the mapping */ + const typeInfo = LogbookTypeInfo[type] || {}; + + /** Construct class name */ + const className = ["logbook-card", typeInfo.className] + .filter(Boolean) + .join(" "); + + /** Get the corresponding book image */ + const bookImage = typeInfo.image || LogRectangle; // Fallback to LogRectangle if image not found + + return ( +
+
+ {type} +
+
+ +
+

{title}

+
+ Type: {type} +
+
+ Storage: {storage}/100 logs + used +
+
+ Created {created} +
+
+
+
+ ); +} diff --git a/frontend/src/components/Logbooks/LogbookTypeInfo.jsx b/frontend/src/components/Logbooks/LogbookTypeInfo.jsx new file mode 100644 index 00000000..7a2f0416 --- /dev/null +++ b/frontend/src/components/Logbooks/LogbookTypeInfo.jsx @@ -0,0 +1,29 @@ +import AdultCardiac from "../../assets/images/adult-cardiac-book.png"; +import CongenitalCardiac from "../../assets/images/congenital-cardiac-book.png"; +import Obstetrics from "../../assets/images/obstetrics-book.png"; +import GeneralSurgery from "../../assets/images/general-surgery-book.png"; +import Ophthalmology from "../../assets/images/ophthalmology-book.png"; + +/** Mapping of logbook types to their class names and images */ +export const LogbookTypeInfo = { + "Cardiac Surgery - Adult": { + className: "", + image: AdultCardiac, + }, + "Cardiac Surgery - Congenital": { + className: "congenital", + image: CongenitalCardiac, + }, + Ophthalmology: { + className: "ophthalmology", + image: Ophthalmology, + }, + "Obstetrics/Gynecology": { + className: "obstetrics", + image: Obstetrics, + }, + "General Surgery": { + className: "general-surgery", + image: GeneralSurgery, + }, +}; diff --git a/frontend/src/components/Navbar/Navbar.jsx b/frontend/src/components/Navbar/Navbar.jsx index 09e0276d..1e89d002 100644 --- a/frontend/src/components/Navbar/Navbar.jsx +++ b/frontend/src/components/Navbar/Navbar.jsx @@ -1,11 +1,11 @@ -import { NavLink } from 'react-router-dom'; +import { NavLink } from "react-router-dom"; import { useAuth } from "../../contexts/AuthContext"; import { HomeIcon, BookOpenIcon, ClockIcon, - ArrowLeftStartOnRectangleIcon -} from '@heroicons/react/24/outline'; + ArrowLeftStartOnRectangleIcon, +} from "@heroicons/react/24/outline"; import "./Navbar.css"; export const Navbar = () => { @@ -22,28 +22,28 @@ export const Navbar = () => { return ( ); -} +}; diff --git a/frontend/src/components/NewLogModal/NewLogModal.css b/frontend/src/components/NewLogModal/NewLogModal.css new file mode 100644 index 00000000..b0ebfdda --- /dev/null +++ b/frontend/src/components/NewLogModal/NewLogModal.css @@ -0,0 +1,81 @@ +.modal-content { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 2rem; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + height: 380px; + width: 640px; + background-color: #FBFCFE; + border-radius: 20px; + box-shadow: 24; + } + + .close-modal-button { + position: absolute; + top: 40px; + left: 40px; + height: 40px; + width: 40px; + display: flex; + justify-content: center; + align-items: center; + background-color: transparent; + color: #244B94; + border-radius: 40px; + } + + .close-modal-button:hover { + background-color: rgb(219, 219, 219); + } + + .modal-description { + font-size: 24px; + font-weight: bold; + margin: 1rem; + } + + .new-log-modal-divider { + width: 60%; + } + + .new-log-modal-buttons-container { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 2rem; + gap: 20px; + } + + .upload-photo-button { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + color: #F7FAFF; + gap: 12px; + } + +.create-manually-button { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + color: #4f607e; + gap: 12px; +} + +.close-x-icon { + height: 34px; + width: 34px; + color: inherit; +} + +.modal-icon { + height: 40px; + width: 40px; +} diff --git a/frontend/src/components/NewLogModal/NewLogModal.jsx b/frontend/src/components/NewLogModal/NewLogModal.jsx new file mode 100644 index 00000000..a164cec1 --- /dev/null +++ b/frontend/src/components/NewLogModal/NewLogModal.jsx @@ -0,0 +1,65 @@ +import { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { CLButtonPrimary, CLButtonSecondary } from "../Buttons/CLButtons"; +import { + PhotoIcon, + PencilSquareIcon, + XMarkIcon, +} from "@heroicons/react/24/outline"; +import Box from "@mui/material/Box"; +import Modal from "@mui/material/Modal"; +import Divider from "@mui/material/Divider"; +import "./NewLogModal.css"; + +export const NewLogModal = () => { + const [open, setOpen] = useState(false); + const handleOpen = () => setOpen(true); + const handleClose = () => setOpen(false); + + const navigate = useNavigate(); + + const handleUploadPhoto = () => { + navigate("/upload-photo"); + }; + + const handleCreateManually = () => { + navigate("/manualEntry"); + }; + + return ( +
+ + Create New Log + + + + +

+ How would you like to create a new log? +

+ +
+ + +

Upload Photo

+
+ + +

Create Manually

+
+
+
+
+
+ ); +}; diff --git a/frontend/src/components/UploadPhoto/PreviewSection.css b/frontend/src/components/UploadPhoto/PreviewSection.css index 0ae639e5..4ee4c76b 100644 --- a/frontend/src/components/UploadPhoto/PreviewSection.css +++ b/frontend/src/components/UploadPhoto/PreviewSection.css @@ -1,70 +1,170 @@ .preview-section { - flex-basis: 36%; - max-width: 31vw; - transition: right 0.5s ease; - padding: 20px; + position: fixed; + top: 15%; + right: 2%; + width: 400px; + height: 75%; + border-radius: 10px; + background: #FAF9F9; + box-shadow: 8px 0 30px rgba(0, 0, 0, 0.15); + padding: 2rem; + z-index: 1000; display: flex; flex-direction: column; - box-sizing: border-box; - background-color: #333; - overflow-y: auto; -} - -.edit-mode-toggle { - margin-bottom: 20px; -} - -.edit-button { - padding: 10px 20px; - margin-left: 330px; - border: 1px solid transparent; - cursor: pointer; - border-radius: 5px; - font-weight: 600; - transition: border-color 0.25s; -} - -.edit-button:hover { - border-color: #646cff; -} - -.edit-button:focus, -.edit-button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -.remove-button { + } + + .preview-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; + padding-bottom: 0.5rem; + border-bottom: 2px solid #ECEBED; + } + + .preview-title { + display: flex; + align-items: center; + gap: 0.3rem; + color: #333333; font-weight: 600; - margin-top: 10px; - padding: 10px 10px; + background: none; + } + + .preview-section-icon { + color: #2B4B96; + width: 20px; + height: 20px; + } + + /* Edit Button */ + .edit-button { + display: flex; + align-items: center; + gap: 0.3rem; + border: 1px solid #9AB0E1; + border-radius: 15px; + background: none; + color: #333333; + cursor: pointer; + transition: color 0.2s ease; + } + + .edit-button.active { + color: #2B4B96; + } + + .edit-icon { + width: 16px; + height: 16px; + } + + /* Delete Button */ + .delete-button { + position: absolute; + top: 1rem; + right: 1.5rem; + width: 24px; + height: 24px; + border-radius: 50%; + background: rgba(0, 0, 0, 0.5); + border: none; color: white; - border: 1px solid transparent; + font-size: 18px; + line-height: 1; cursor: pointer; - border-radius: 5px; - transition: border-color 0.25s; -} - -.remove-button:hover { - border-color: #646cff; -} - -.remove-button:focus, -.remove-button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -.preview-item { display: flex; align-items: center; - margin-top: -10px; - padding: 10px; - border-radius: 5px; - height: 90vh; -} - -.preview-list { + justify-content: center; + transition: background-color 0.2s ease; + } + + .delete-button:hover { + background: rgba(0, 0, 0, 0.7); + } + + /* Preview List */ + .preview-list { + flex-grow: 1; + overflow-y: auto; + overflow-x: hidden; + border-radius: 20px; display: flex; flex-direction: column; - gap: 15px; - overflow-y: auto; -} \ No newline at end of file + gap: 0.5rem; + padding: 0.5rem; + margin-bottom: 0.5rem; + } + + .preview-list::-webkit-scrollbar { + width: 6px; + } + + .preview-list::-webkit-scrollbar-track { + background: #F3F4F6; + border-radius: 3px; + } + + .preview-list::-webkit-scrollbar-thumb { + background: #D1D5DB; + border-radius: 3px; + } + + .preview-list::-webkit-scrollbar-thumb:hover { + background: #9CA3AF; + } + + /* Preview Item */ + .preview-item { + width: 100%; + padding: 0.5rem; + position: relative; + } + + .preview-image { + width: calc(100% - 8px); + border-radius: 12px; + object-fit: contain; + max-height: 500px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + } + + /* Preview Footer */ + .preview-footer { + margin-top: auto; + display: flex; + justify-content: flex-end; + } + + .preview-footer .transcribe-button { + margin-bottom: -1rem; + border-radius: 20px; + padding: 10px 14px; + font-size: 14px; + } + + /* Transcribe Button */ + .transcribe-button { + background-color: #244B94; + color: white; + border: none; + border-radius: 15px; + padding: 12px 20px; + font-size: 16px; + font-weight: 600; + cursor: pointer; + transition: background-color 0.2s ease; + } + + .transcribe-button:hover { + background-color: #1e3a7b; + } + + .transcribe-button:active { + background-color: #152c5f; + } + + .transcribe-button:focus { + outline: none; + box-shadow: 0 0 0 2px rgba(43, 75, 150, 0.4); + } \ No newline at end of file diff --git a/frontend/src/components/UploadPhoto/PreviewSection.jsx b/frontend/src/components/UploadPhoto/PreviewSection.jsx index f240b871..f86634e5 100644 --- a/frontend/src/components/UploadPhoto/PreviewSection.jsx +++ b/frontend/src/components/UploadPhoto/PreviewSection.jsx @@ -1,43 +1,79 @@ -import EditIcon from "@mui/icons-material/Edit"; +import { useState } from "react"; +import { + ChevronDoubleRightIcon, + PencilSquareIcon, +} from "@heroicons/react/24/solid"; import "./PreviewSection.css"; export default function PreviewSection({ - imageFiles, - isEditMode, - toggleEditMode, - handleRemoveImage, + files, + setFiles, + handlePreviewClick, + handleTranscribe, }) { + const [isEditing, setIsEditing] = useState(false); + + /** Toggle edit mode */ + const handleEdit = () => { + setIsEditing((prev) => !prev); + }; + + /** Handle image deletion */ + const handleDeleteImage = (timestampToDelete) => { + setFiles((prevFiles) => { + // Find the file to delete and revoke its URL + const fileToDelete = prevFiles.find( + (file) => file.timestamp === timestampToDelete + ); + if (fileToDelete?.preview) { + URL.revokeObjectURL(fileToDelete.preview); + } + // Filter out the deleted file + return prevFiles.filter((file) => file.timestamp !== timestampToDelete); + }); + }; + return (
-
- +
-

Preview

+
- {imageFiles.length === 0 &&

No photo uploaded yet.

} - {imageFiles.map((image, index) => ( -
-
-

{image.name}

- {image.name} - {isEditMode && ( - - )} -
+ {files.map((fileData, index) => ( +
+ {`Preview + {isEditing && ( + + )}
))}
+ +
+ +
); } diff --git a/frontend/src/components/UploadPhoto/UploadArea.css b/frontend/src/components/UploadPhoto/UploadArea.css index 071fe921..79c0c81d 100644 --- a/frontend/src/components/UploadPhoto/UploadArea.css +++ b/frontend/src/components/UploadPhoto/UploadArea.css @@ -1,15 +1,86 @@ .upload-area { - border: 2px dashed #646cff; - background-color: #1d1d1d; - display: flex; - align-items: center; - justify-content: center; - text-align: center; - margin-bottom: 20px; - height: 450px; - width: 80%; - border-radius: 8px; - margin-left: auto; - margin-right: auto; - cursor: pointer; -} \ No newline at end of file + width: 45%; + margin-left: 23%; + aspect-ratio: 3/2; + border: 2px dashed rgba(69, 72, 76, 0.3); + border-radius: 30px; + display: flex; + align-items: center; + justify-content: center; + position: relative; + } + + .upload-area.drag-active { + border-color: #2B4B96; + background-color: rgba(43, 75, 150, 0.05); + } + + .file-input { + opacity: 0; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + cursor: pointer; + } + + /* Upload Label */ + .upload-label { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + pointer-events: none; + } + + .upload-icon { + width: 80px; + height: 80px; + background: #333333; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 0 20px rgba(30, 55, 101, 1); + position: relative; + } + + .icon-image { + width: 40px; + height: 40px; + color: white; + } + + .plus-indicator { + position: absolute; + bottom: 2px; + right: -5px; + width: 25px; + height: 25px; + background: #6FB2F6; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: 20px; + } + + /* Upload Text */ + .upload-text { + text-align: center; + } + + .upload-text h3 { + margin: 0; + font-size: 1.25rem; + color: #000000; + font-weight: 400; + } + + .upload-text p { + margin-top: 0; + color: #6B7280; + font-size: 0.875rem; + } \ No newline at end of file diff --git a/frontend/src/components/UploadPhoto/UploadArea.jsx b/frontend/src/components/UploadPhoto/UploadArea.jsx index 82e87057..b398a69a 100644 --- a/frontend/src/components/UploadPhoto/UploadArea.jsx +++ b/frontend/src/components/UploadPhoto/UploadArea.jsx @@ -1,26 +1,75 @@ -import AddPhotoAlternateIcon from "@mui/icons-material/AddPhotoAlternate"; +import { useState } from "react"; +import { PhotoIcon } from "@heroicons/react/24/solid"; import "./UploadArea.css"; -export default function UploadArea({ handleFileUpload }) { +export default function UploadArea({ ALLOWED_FILE_TYPES, handleFiles }) { + const [dragActive, setDragActive] = useState(false); + + /** Function to filter valid files */ + const filterValidFiles = (fileList) => + Array.from(fileList).filter((file) => + ALLOWED_FILE_TYPES.includes(file.type) + ); + + /** Handle drag events */ + const handleDrag = (e) => { + e.preventDefault(); + e.stopPropagation(); + + switch (e.type) { + case "dragenter": + case "dragover": + setDragActive(true); + break; + case "dragleave": + setDragActive(false); + break; + default: + break; + } + }; + + /** Handle drop event */ + const handleDrop = (e) => { + e.preventDefault(); + e.stopPropagation(); + setDragActive(false); + + const validFiles = filterValidFiles(e.dataTransfer.files); + handleFiles(validFiles); + }; + + /** Handle file input change */ + const handleFileInput = (e) => { + const validFiles = filterValidFiles(e.target.files); + handleFiles(validFiles); + }; + return ( -