https://gszb.github.io/Iwosan-Gonczy/
A modern, responsive medical dashboard built with React 19 and Vite. The application provides healthcare professionals with an at-a-glance overview of diagnostics, patient statistics, health indices, and upcoming appointments.
npm install
npm run devOpen http://localhost:5173 in your browser.
The app is split into a collapsible sidebar and a scrollable main content area. The sidebar includes navigation links, an expand/collapse toggle, and an emergency hotlines section. On mobile devices (≤1024px) the sidebar transforms into an off-canvas drawer activated via a hamburger menu in the header, complete with a dismissible overlay.
The dropdown was implemented on the user's profile picture, and it includes links to the user's profile, settings, and logout.
The sidebar is a collapsible navigation menu that includes links to the dashboard, diagnostics, patients, appointments, and settings. It also includes an emergency hotlines section with links to emergency services. The collapsing was implemented not on a usual way, since the lack of business description from the design source. The user has to click on the expand arrow button, which highlights the button and the sidebar remains open. On toggling, the highlighting disappears and the sidebar collapses, which you can expand again by hovering on the sidebar.
The main content area renders:
- Welcome section with the logged-in user's name and a light/dark theme toggle
- Stat cards displaying key metrics via interactive Chart.js charts:
- Diagnostics breakdown (Doughnut chart)
- Patient demographics (Doughnut + horizontal bar chart)
- Health index gauge (semi-circle Doughnut)
- Surgery statistics (semi-circle Doughnut)
- Lab results (vertical Bar chart)
- Heartbeat monitor (Line chart simulating an EKG pattern) (Since the charts on the design were very specific, I used Chart.js to create similar looking charts, the possibilities to customize the charts are endless)
- Appointments accordion listing upcoming patient appointments with expandable detail panels (I decided to implement the accordion in this section, since it was the most obvious UI pattern for this section)
Light and dark themes are managed via Redux Toolkit and persisted to localStorage. Toggling the theme dispatches a Redux action that updates a data-theme attribute on <body>, which drives all CSS variable swaps.
src/
├── assets/ # SVG icons and images (centralized via index.js)
├── components/
│ ├── AppointmentsAccordion/ # Expandable appointment list
│ ├── DashboardOverview/ # Main dashboard grid with chart cards
│ ├── Header/ # Search bar, notifications, profile dropdown
│ ├── Layout/ # Sidebar + main content wrapper
│ ├── Sidebar/ # Navigation sidebar with collapse/expand
│ └── StatCard/ # Reusable metric card container
├── store/
│ ├── store.js # Redux store configuration
│ └── themeSlice.js # Theme state (light/dark) with localStorage
├── styles/
│ └── _variables.scss # CSS custom properties for both themes
├── App.jsx # Root component with theme toggle and sections
├── App.scss # Welcome section and theme toggle styles
├── index.css # Global resets and cross-browser normalization
└── main.jsx # Entry point with Redux Provider
Vite provides near-instant HMR and fast builds via native ESM. React 19 gives us the latest hooks and rendering optimizations.
Chosen for theme state management. While a simple theme toggle could use React Context, Redux Toolkit was adopted because:
- It provides
createSlicewhich eliminates boilerplate for reducers and actions - The store is easily extensible if additional global state is needed (e.g., user auth, notification counts)
- It integrates directly with
localStoragefor theme persistence
Chosen for data visualization. Chart.js is lightweight (~60KB gzipped), supports Doughnut, Bar, and Line charts out of the box, and renders on <canvas> for good performance. The react-chartjs-2 wrapper provides declarative React components that map directly to Chart.js configurations, avoiding manual DOM manipulation.
All the charts are just mocked charts based on examples found on the internet. The data is not real, the implementation is not unique, since it is just a test project.
SCSS is used for component-level styling following BEM methodology. It provides nesting for media queries and pseudo-selectors, which keeps responsive styles co-located with their base rules. All selectors are kept flat at the BEM block/element/modifier level to avoid deep nesting.
- All interactive elements are focusable and operable via keyboard
- Visible
:focus-visibleoutlines (2px solid accent color) on every button, link, and input - The profile dropdown implements roving tabindex — Arrow Up/Down navigates between menu items, Escape closes the menu and returns focus to the trigger
- Accordion panels toggle on Enter/Space via native
<button>elements
| Component | Attributes |
|---|---|
| Sidebar | <nav> landmark, aria-label on navigation, role="list" / role="listitem" on link groups |
| Header dropdown | aria-haspopup, aria-expanded, aria-controls on trigger; role="menu", role="menuitem", role="none" on list structure |
| Accordion | aria-expanded, aria-controls on each toggle button; role="region", aria-labelledby on content panels |
| Theme toggle | role="switch", aria-checked, dynamic aria-label reflecting current action |
| Search input | aria-label for screen reader context |
| Mobile menu | aria-label on hamburger and close buttons |
A single <h1> exists on the page (welcome title). All section headings use <h2>. The appointments widget title also uses <h2> with an id linked via aria-labelledby.
All text/background combinations meet the 4.5:1 minimum contrast ratio:
| Variable | Light | Dark | Ratio |
|---|---|---|---|
--text-primary on --bg-primary |
#212b36 on #ffffff |
#ffffff on #161c24 |
13.5:1 / 16.0:1 |
--text-secondary on --bg-primary |
#637381 on #ffffff |
#a3b1bf on #161c24 |
5.4:1 / 5.2:1 |
--accent-color on --bg-primary |
#0052cc on #ffffff |
#0052cc on #161c24 |
4.6:1 / 4.7:1 |
-webkit-font-smoothingand-moz-osx-font-smoothingfor consistent text rendering-webkit-text-size-adjustand-ms-text-size-adjustto prevent mobile text resizing-webkit-tap-highlight-color: transparenton buttons-webkit-appearance: noneon search inputs to remove native styling- Firefox scrollbar support via
scrollbar-widthandscrollbar-color -webkit-overflow-scrolling: touchfor iOS momentum scrolling- Vendor-prefixed transitions and transforms for sidebar animations
| Breakpoint | Behavior |
|---|---|
| > 1200px | Full layout: sidebar + 3-column card grid + appointments sidebar |
| ≤ 1200px | Appointments sidebar drops below the card grid |
| ≤ 1024px | Sidebar becomes off-canvas drawer, hamburger menu appears, cards become 2-column |
| ≤ 768px | Cards stack to single column, welcome section stacks vertically |