course-schedule #2

Merged
jare2473 merged 5 commits from course-schedule into main 2025-09-05 10:50:24 +02:00
33 changed files with 3282 additions and 396 deletions

View File

@ -2,14 +2,17 @@ import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import AppRoutes from './AppRoutes'; // move the routing and loading logic here
import { SettingsProvider } from './context/SettingsContext';
import { ThemeProvider } from './context/ThemeContext';
function App() {
return (
<SettingsProvider>
<Router basename={import.meta.env.BASE_URL}>
<AppRoutes />
</Router>
</SettingsProvider>
<ThemeProvider>
<SettingsProvider>
<Router basename={import.meta.env.BASE_URL}>
<AppRoutes />
</Router>
</SettingsProvider>
</ThemeProvider>
);
}

View File

@ -6,6 +6,8 @@ import Layout from './Layout';
import { RoomBooking } from './pages/RoomBooking';
import { NewBooking } from './pages/NewBooking';
import { BookingSettings } from './pages/BookingSettings';
import { CourseSchedule } from './pages/CourseSchedule';
import { CourseScheduleView } from './pages/CourseScheduleView';
import { TestSession } from './pages/TestSession';
import FullScreenLoader from './components/FullScreenLoader';
@ -105,7 +107,7 @@ const AppRoutes = () => {
return (
<>
{/* Pass loading as isVisible to FullScreenLoader */}
{/*<FullScreenLoader isVisible={loading} />*/}
{<FullScreenLoader isVisible={loading} />}
<Routes>
@ -115,6 +117,8 @@ const AppRoutes = () => {
<Route path="/" element={<Layout />}>
<Route index element={<RoomBooking bookings={bookings} showSuccessBanner={showSuccessBanner} lastCreatedBooking={lastCreatedBooking} onDismissBanner={() => setShowSuccessBanner(false)} onBookingUpdate={updateBooking} onBookingDelete={deleteBooking} showDeleteBanner={showDeleteBanner} lastDeletedBooking={lastDeletedBooking} onDismissDeleteBanner={() => setShowDeleteBanner(false)} />} />
<Route path="new-booking" element={<NewBooking addBooking={addBooking} />} />
<Route path="course-schedule" element={<CourseSchedule />} />
<Route path="course-schedule/:courseId" element={<CourseScheduleView />} />
<Route path="booking-settings" element={<BookingSettings />} />
</Route>
</Routes>

View File

@ -1,17 +1,17 @@
.card {
border: 1px solid #E5E5E5;
padding: 1.25rem;
border: var(--border-width-thin) solid var(--border-light);
padding: var(--card-padding);
width: 100%;
border-radius: 0.5rem;
background: #fff;
transition: all 0.2s ease;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
border-radius: var(--border-radius-md);
background: var(--bg-primary);
transition: var(--transition-medium);
box-shadow: var(--shadow-lg);
}
.card:hover {
cursor: pointer;
border-color: #007AFF;
box-shadow: 0 4px 16px rgba(0, 122, 255, 0.12);
border-color: var(--color-primary);
box-shadow: var(--shadow-xl);
transform: translateY(-2px);
}
@ -28,55 +28,55 @@
.date {
text-transform: uppercase;
font-size: 0.8rem;
font-weight: 600;
color: #999;
font-size: var(--font-size-sm);
font-weight: var(--font-weight-semibold);
color: var(--text-muted);
letter-spacing: 1px;
margin-bottom: 0.5rem;
margin-bottom: var(--spacing-sm);
display: block;
}
.titleRow {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 0.5rem;
gap: var(--spacing-md);
margin-bottom: var(--spacing-sm);
}
.title {
margin: 0;
font-size: 1.25rem;
font-weight: 700;
color: #333;
font-size: var(--font-size-3xl);
font-weight: var(--font-weight-bold);
color: var(--text-primary);
line-height: 1.3;
}
.room {
font-weight: 600;
font-size: 0.875rem;
padding: 0.375rem 0.75rem;
border-radius: 1rem;
font-weight: var(--font-weight-semibold);
font-size: var(--font-size-base);
padding: var(--spacing-xs) var(--spacing-md);
border-radius: var(--border-radius-lg);
white-space: nowrap;
}
.room-green {
background: #D4EDDA;
color: #155724;
background: var(--room-green-bg);
color: var(--room-green-text);
}
.room-red {
background: #F8D7DA;
color: #721C24;
background: var(--room-red-bg);
color: var(--room-red-text);
}
.room-blue {
background: #D1ECF1;
color: #0C5460;
background: var(--room-blue-bg);
color: var(--room-blue-text);
}
.room-yellow {
background: #FFF3CD;
color: #856404;
background: var(--room-yellow-bg);
color: var(--room-yellow-text);
}
.timeSection {
@ -91,28 +91,28 @@
.startTime {
font-size: 1.6rem;
font-weight: 400;
color: #333;
font-weight: var(--font-weight-normal);
color: var(--text-primary);
line-height: 1;
}
.endTime {
font-size: 1.6rem;
font-weight: 400;
color: #acacac;
font-weight: var(--font-weight-normal);
color: var(--text-tertiary);
line-height: 1;
}
.participants {
margin: 0;
font-size: 0.9rem;
color: #999;
font-size: var(--font-size-md);
color: var(--text-muted);
}
/* Expanded card styles */
.expanded {
border-color: #007AFF;
box-shadow: 0 4px 16px rgba(0, 122, 255, 0.12);
border-color: var(--color-primary);
box-shadow: var(--shadow-xl);
}
.expanded:hover {

View File

@ -15,7 +15,7 @@
.dialog {
overflow: hidden;
background: rgba(255, 255, 255, 0.95) !important;
background: var(--modal-dialog-bg) !important;
padding: 0;
border-radius: 0.4rem;
}
@ -32,7 +32,7 @@
margin: 0;
font-size: 1.25rem;
font-weight: 600;
color: #1f2937;
color: var(--text-primary);
flex: 1;
}
@ -46,7 +46,7 @@
align-items: center;
justify-content: center;
border-radius: 0.25rem;
color: #6b7280;
color: var(--text-secondary);
cursor: pointer;
transition: all 0.2s ease;
flex-shrink: 0;
@ -54,12 +54,12 @@
}
.closeButton:hover {
background-color: #f3f4f6;
color: #374151;
background-color: var(--modal-close-hover-bg);
color: var(--text-primary);
}
.closeButton:focus {
outline: 2px solid #2563eb;
outline: 2px solid var(--color-primary);
outline-offset: -1px;
}
@ -75,7 +75,7 @@
.sectionWithTitle label {
font-size: 0.8rem;
color: #717171;
color: var(--text-tertiary);
font-weight: 500;
margin-bottom: 0.25rem;
text-transform: uppercase;
@ -85,16 +85,16 @@
.sectionWithTitle p {
margin: 0;
font-size: 1rem;
color: #1f2937;
color: var(--text-primary);
font-weight: 500;
}
.timeDisplay {
margin: 1rem 0;
padding: 1rem;
background-color: #f8f9fa;
background-color: var(--modal-display-bg);
border-radius: 8px;
border: 1px solid #e9ecef;
border: 1px solid var(--modal-display-border);
width: fit-content;
margin-left: 0;
}
@ -114,7 +114,7 @@
.startTime label, .endTime label {
font-size: 0.75rem;
color: #6c757d;
color: var(--text-secondary);
font-weight: 500;
margin-bottom: 0.25rem;
text-transform: uppercase;
@ -124,20 +124,20 @@
.timeValue {
font-size: 1.5rem;
font-weight: 600;
color: #212529;
color: var(--text-primary);
}
.timeSeparator {
font-size: 1.5rem;
font-weight: 400;
color: #6c757d;
color: var(--text-secondary);
margin: 0 0.5rem;
padding-top: 1.3rem;
}
.roomText {
font-weight: 600 !important;
color: #059669 !important;
color: var(--modal-save-bg) !important;
}
.modalFooter {
@ -152,74 +152,74 @@
.cancelButton {
flex: 2;
background-color: white;
background-color: var(--modal-cancel-bg);
height: 4rem;
color: #374151;
color: var(--modal-cancel-text);
font-weight: 600;
border: 2px solid #d1d5db;
border: 2px solid var(--modal-cancel-border);
border-radius: 0.5rem;
transition: all 0.2s ease;
cursor: pointer;
}
.cancelButton:hover {
background-color: #f9fafb;
border-color: #9ca3af;
background-color: var(--modal-cancel-hover-bg);
border-color: var(--modal-cancel-hover-border);
}
.cancelButton:active {
background-color: #e5e7eb;
background-color: var(--modal-cancel-active-bg);
transform: translateY(1px);
}
.saveButton {
flex: 3;
background-color: #059669;
color: white;
background-color: var(--modal-save-bg);
color: var(--modal-save-text);
height: 4rem;
font-weight: 600;
font-size: 1.1rem;
border: 2px solid #047857;
border: 2px solid var(--modal-save-border);
border-radius: 0.5rem;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgba(5, 150, 105, 0.2);
box-shadow: var(--modal-save-shadow);
cursor: pointer;
}
.saveButton:hover {
background-color: #047857;
box-shadow: 0 4px 8px rgba(5, 150, 105, 0.3);
background-color: var(--modal-save-hover-bg);
box-shadow: var(--modal-save-hover-shadow);
}
.saveButton:active {
background-color: #065f46;
background-color: var(--modal-save-active-bg);
transform: translateY(1px);
box-shadow: 0 1px 2px rgba(5, 150, 105, 0.2);
box-shadow: var(--modal-save-active-shadow);
}
.saveButton[data-focused],
.cancelButton[data-focused] {
outline: 2px solid #2563EB;
outline: 2px solid var(--color-primary);
outline-offset: -1px;
}
.disabledButton {
background-color: #f8f9fa !important;
color: #adb5bd !important;
border: 2px dashed #dee2e6 !important;
background-color: var(--button-disabled-bg) !important;
color: var(--button-disabled-text) !important;
border: 2px dashed var(--button-disabled-border) !important;
opacity: 0.6 !important;
box-shadow: none !important;
cursor: default !important;
}
.disabledButton:hover {
background-color: #f8f9fa !important;
background-color: var(--button-disabled-bg) !important;
transform: none !important;
box-shadow: none !important;
}
.disabledButton:active {
background-color: #f8f9fa !important;
background-color: var(--button-disabled-bg) !important;
transform: none !important;
}

View File

@ -1,6 +1,6 @@
.elementHeading {
margin: 0;
color: #8E8E8E;
color: var(--text-tertiary);
font-size: 0.8rem;
font-style: normal;
font-weight: 520;

View File

@ -9,7 +9,7 @@
.modalFooter {
height: fit-content;
width: 100%;
color: blue;
color: var(--color-primary);
display: flex;
align-items: center;
gap: 1rem;
@ -21,58 +21,58 @@
.cancelButton {
flex: 2;
background-color: white;
background-color: var(--modal-cancel-bg);
height: 4rem;
color: #374151;
color: var(--modal-cancel-text);
font-weight: 600;
border: 2px solid #d1d5db;
border: 2px solid var(--modal-cancel-border);
border-radius: 0.5rem;
transition: all 0.2s ease;
}
@media (hover: hover) {
.cancelButton:hover {
background-color: #f9fafb;
border-color: #9ca3af;
background-color: var(--modal-cancel-hover-bg);
border-color: var(--modal-cancel-hover-border);
cursor: pointer;
}
}
.saveButton {
flex: 3;
background-color: #059669;
color: white;
background-color: var(--modal-save-bg);
color: var(--modal-save-text);
height: 4rem;
font-weight: 600;
font-size: 1.1rem;
border: 2px solid #047857;
border: 2px solid var(--modal-save-border);
border-radius: 0.5rem;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgba(5, 150, 105, 0.2);
box-shadow: var(--modal-save-shadow);
}
@media (hover: hover) {
.saveButton:hover {
background-color: #047857;
box-shadow: 0 4px 8px rgba(5, 150, 105, 0.3);
background-color: var(--modal-save-hover-bg);
box-shadow: var(--modal-save-hover-shadow);
cursor: pointer;
}
}
.saveButton:active {
background-color: #065f46;
background-color: var(--modal-save-active-bg);
transform: translateY(1px);
box-shadow: 0 1px 2px rgba(5, 150, 105, 0.2);
box-shadow: var(--modal-save-active-shadow);
}
.saveButton[data-focused],
.cancelButton[data-focused] {
outline: 2px solid #2563EB;
outline: 2px solid var(--color-primary);
outline-offset: -1px;
}
.cancelButton:active {
background-color: #e5e7eb;
background-color: var(--modal-cancel-active-bg);
transform: translateY(1px);
}
@ -90,7 +90,7 @@
.sectionWithTitle label {
font-size: 0.8rem;
color: #717171;
color: var(--text-tertiary);
}
.sectionWithTitle p {
@ -98,15 +98,12 @@
}
.modalContainer {
background-color: white;
background-color: var(--modal-bg);
width: 85%;
max-width: 400px;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, 0.18) !important;
box-shadow: 0 32px 64px rgba(0, 0, 0, 0.12),
0 16px 32px rgba(0, 0, 0, 0.08),
0 8px 16px rgba(0, 0, 0, 0.04),
inset 0 1px 0 rgba(255, 255, 255, 0.15) !important;
border: 1px solid var(--modal-border) !important;
box-shadow: var(--modal-shadow) !important;
backdrop-filter: blur(20px) saturate(140%) !important;
-webkit-backdrop-filter: blur(20px) saturate(140%) !important;
}
@ -134,18 +131,18 @@
max-height: calc(100vh - 4rem) !important;
max-width: 90vw !important;
overflow-y: auto !important;
background: white !important;
background: var(--modal-bg) !important;
border-radius: 0.5rem !important;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25) !important;
box-shadow: var(--modal-shadow) !important;
}
/* New time display styles */
.timeDisplay {
margin: 1rem 0;
padding: 1rem;
background-color: #f8f9fa;
background-color: var(--modal-display-bg);
border-radius: 8px;
border: 1px solid #e9ecef;
border: 1px solid var(--modal-display-border);
min-width: 196px;
width: fit-content;
}
@ -165,7 +162,7 @@
.startTime label, .endTime label {
font-size: 0.75rem;
color: #6c757d;
color: var(--text-secondary);
font-weight: 500;
margin-bottom: 0.25rem;
text-transform: uppercase;
@ -175,11 +172,11 @@
.timeValue {
font-size: 1.5rem;
font-weight: 600;
color: #212529;
color: var(--text-primary);
}
.timeValue.placeholder {
color: #adb5bd;
color: var(--text-muted);
font-style: italic;
font-size: 1rem;
}
@ -187,23 +184,23 @@
.timeSeparator {
font-size: 1.5rem;
font-weight: 400;
color: #6c757d;
color: var(--text-secondary);
margin: 0 0.5rem;
padding-top: 1.3rem;
}
/* Disabled button styles */
.disabledButton {
background-color: #f8f9fa !important;
color: #adb5bd !important;
border: 2px dashed #dee2e6 !important;
background-color: var(--button-disabled-bg) !important;
color: var(--button-disabled-text) !important;
border: 2px dashed var(--button-disabled-border) !important;
opacity: 0.6 !important;
box-shadow: none;
}
@media (hover: hover) {
.disabledButton:hover {
background-color: #f8f9fa !important;
background-color: var(--button-disabled-bg) !important;
transform: none !important;
box-shadow: none;
cursor: default;
@ -211,6 +208,6 @@
}
.disabledButton:active {
background-color: #f8f9fa !important;
background-color: var(--button-disabled-bg) !important;
transform: none !important;
}

View File

@ -1,10 +1,11 @@
.textInput {
width: 100%;
margin-bottom: 10px;
border: 1px solid #D2D9E0;
border: 1px solid var(--input-border);
border-radius: 0.5rem;
font-size: 16px;
background-color: #FAFBFC;
background-color: var(--input-bg);
color: var(--input-text);
padding: 1rem;
width: 100%;
max-width: 600px;
@ -12,12 +13,12 @@
}
.textInput::placeholder {
color: #adadad;
color: var(--input-placeholder);
}
.elementHeading {
margin: 0;
color: #8E8E8E;
color: var(--text-tertiary);
font-size: 0.8rem;
font-style: normal;
font-weight: 520;
@ -29,7 +30,7 @@
/* Compact styles */
.compactElementHeading {
font-size: 0.75rem;
color: #717171;
color: var(--text-tertiary);
font-weight: 500;
margin-bottom: 0.4rem;
margin-top: 0;
@ -40,10 +41,11 @@
.compactTextInput {
width: 100%;
padding: 0.5rem 1rem;
border: 1px solid #ccc;
border: 1px solid var(--input-border);
border-radius: 0.375rem;
font-size: 16px;
background-color: white;
background-color: var(--input-bg);
color: var(--input-text);
font-family: inherit;
transition: border-color 0.2s ease;
box-sizing: border-box;
@ -51,7 +53,7 @@
}
.compactTextInput:focus {
outline: 2px solid #007AFF;
outline: 2px solid var(--color-primary);
outline-offset: 2px;
border-color: #007AFF;
border-color: var(--color-primary);
}

View File

@ -1,6 +1,5 @@
.bookingsListContainer {
/*border-top: 1px solid gray;*/
padding-bottom: 2rem;
padding-bottom: var(--spacing-3xl);
display: flex;
flex-direction: column;
}
@ -8,45 +7,45 @@
.bookingsContainer {
display: flex;
flex-direction: column;
gap: 0.5rem;
gap: var(--spacing-sm);
}
.message {
font-style: italic;
color: #838383;
color: var(--text-secondary);
margin: 0;
}
.heading {
margin: 0;
margin-bottom: 0.5rem;
margin-bottom: var(--spacing-sm);
}
.showMoreButton {
background: #ffffff;
background: var(--button-secondary-bg);
border: none;
border-radius: 0.75rem;
padding: 1rem;
font-size: 1rem;
font-weight: 600;
color: rgb(0, 0, 0);
border-radius: var(--border-radius-lg);
padding: var(--spacing-lg);
font-size: var(--font-size-xl);
font-weight: var(--font-weight-semibold);
color: var(--button-secondary-text);
cursor: pointer;
transition: all 0.2s ease;
margin-top: 1rem;
transition: var(--transition-medium);
margin-top: var(--spacing-lg);
width: 100%;
max-width: 100%;
box-shadow: 0 2px 8px rgba(143, 143, 143, 0.2);
box-shadow: var(--button-secondary-shadow);
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
gap: var(--spacing-sm);
box-sizing: border-box;
}
.showMoreButton:hover {
background: #f2f6ff;
background: var(--button-secondary-hover-bg);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(192, 192, 192, 0.3);
box-shadow: var(--button-secondary-hover-shadow);
}
.showMoreButton:active {

View File

@ -10,10 +10,10 @@
appearance: none;
-webkit-appearance: none;
padding: 0.5rem 2.5rem 0.5rem 1rem; /* More room on right for chevron */
border: 1px solid #ccc;
border: 1px solid var(--input-border);
border-radius: 0.375rem;
background-color: white;
color: #333;
background-color: var(--input-bg);
color: var(--input-text);
cursor: pointer;
font-size: 1rem;
width: 100%;
@ -27,7 +27,7 @@
top: 50%;
right: 1rem;
transform: translateY(-50%);
color: #888;
color: var(--dropdown-chevron-color);
font-size: 0.8rem;
z-index: 1;
}

View File

@ -1,6 +1,7 @@
import React, { useState } from 'react';
import styles from './Header.module.css'; // Import the CSS Module
import { Link } from 'react-router-dom';
import { ThemeToggle } from './ThemeToggle';
const Header = () => {
const [menuOpen, setMenuOpen] = useState(false);
@ -23,14 +24,18 @@ const Header = () => {
</Link>
Studentportalen
</div>
<div className={styles.menuIcon} onClick={toggleMenu}>
{/* Simple menu icon, you can replace it with an icon from a library */}
<div className={styles.right}>
<ThemeToggle />
<div className={styles.menuIcon} onClick={toggleMenu}>
{/* Simple menu icon, you can replace it with an icon from a library */}
</div>
</div>
{menuOpen && (
<div className={styles.menu}>
{/* Menu items */}
<Link onClick={handleClick} to="/">Lokalbokning</Link>
<Link onClick={handleClick} to="/course-schedule">Schema</Link>
<Link onClick={handleClick} to="/booking-settings">Booking Settings</Link>
</div>
)}

View File

@ -2,45 +2,68 @@
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
background-color: #f8f8f8;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
position: fixed; /* Make the header sticky */
top: 0; /* Stick to the top */
width: 100%; /* Full width */
z-index: 1000; /* Ensure it stays on top of other content */
padding: var(--spacing-sm) var(--spacing-xl);
background-color: var(--bg-secondary);
box-shadow: var(--shadow-md);
position: fixed;
top: 0;
width: 100%;
z-index: var(--z-header);
}
.left {
display: flex;
align-items: center;
gap: var(--spacing-lg);
font-weight: var(--font-weight-semibold);
color: var(--text-primary);
}
.right {
display: flex;
align-items: center;
gap: var(--spacing-lg);
}
.logo img {
height: 40px; /* Adjust the height as needed */
transition: filter 0.2s ease;
}
.menuIcon {
font-size: 24px;
cursor: pointer;
color: var(--text-primary);
padding: var(--spacing-xs);
border-radius: var(--border-radius-md);
transition: var(--transition-fast);
}
.menuIcon:hover {
background-color: var(--bg-muted);
}
.menu {
position: absolute;
top: 60px; /* Position below the header */
right: 20px;
background-color: white;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
padding: 10px;
border-radius: 5px;
top: var(--header-height);
right: var(--spacing-xl);
background-color: var(--bg-primary);
box-shadow: var(--shadow-lg);
padding: var(--spacing-sm);
border-radius: var(--border-radius-md);
display: flex;
flex-direction: column;
gap: 10px;
gap: var(--spacing-sm);
}
.menu a {
text-decoration: none;
color: #333;
padding: 5px 10px;
color: var(--text-primary);
padding: var(--spacing-xs) var(--spacing-sm);
}
.menu a:hover {
background-color: #f0f0f0;
background-color: var(--bg-muted);
}
/* Responsive design for larger screens */
@ -70,5 +93,10 @@ body {
flex-direction: row;
align-items: center;
gap: 1rem;
color: #002E5F;
color: var(--header-brand-color);
}
/* Dark mode logo filter */
:global([data-theme="dark"]) .logo img {
filter: brightness(0) saturate(100%) invert(1);
}

View File

@ -1,18 +1,18 @@
/* Base banner styles */
.banner {
border-radius: 0.75rem;
padding: 1rem 1.25rem;
margin-bottom: 1.5rem;
border-radius: var(--border-radius-lg);
padding: var(--spacing-lg) var(--spacing-xl);
margin-bottom: var(--spacing-2xl);
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
box-shadow: var(--shadow-lg);
}
.bannerContent {
display: flex;
align-items: center;
gap: 1rem;
gap: var(--spacing-lg);
}
.icon {
@ -22,114 +22,114 @@
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 1rem;
font-weight: var(--font-weight-bold);
font-size: var(--font-size-xl);
flex-shrink: 0;
}
.text {
display: flex;
flex-direction: column;
gap: 0.25rem;
gap: var(--spacing-xs);
}
.titleRow {
display: flex;
align-items: center;
gap: 0.5rem;
gap: var(--spacing-sm);
}
.title {
font-weight: 700;
font-size: 1.1rem;
font-weight: var(--font-weight-bold);
font-size: var(--font-size-2xl);
}
.details {
font-weight: 500;
font-size: 0.9rem;
font-weight: var(--font-weight-medium);
font-size: var(--font-size-md);
}
.bookingTitle {
font-weight: 400;
font-weight: var(--font-weight-normal);
}
.closeButton {
background: none;
border: none;
color: #6C757D;
color: var(--notification-close-button);
font-size: 1.5rem;
font-weight: 300;
font-weight: var(--font-weight-light);
cursor: pointer;
padding: 0.25rem 0.5rem;
border-radius: 0.375rem;
transition: all 0.2s ease;
padding: var(--spacing-xs) var(--spacing-sm);
border-radius: var(--border-radius-md);
transition: var(--transition-medium);
line-height: 1;
flex-shrink: 0;
width: fit-content;
}
.closeButton:hover {
background: rgba(108, 117, 125, 0.1);
color: #495057;
background: var(--notification-close-button-bg-hover);
color: var(--notification-close-button-hover);
}
/* Success variant styles */
.success {
background: #E8F5E8;
border: 1px solid #4CAF50;
background: var(--notification-success-bg);
border: var(--border-width-thin) solid var(--notification-success-border);
}
.successIcon {
background: #4CAF50;
color: white;
background: var(--notification-success-icon-bg);
color: var(--notification-success-icon-text);
}
.successTitle {
color: #2E7D32;
color: var(--notification-success-title);
}
.successDetails {
color: #388E3C;
color: var(--notification-success-details);
}
/* Delete variant styles */
.delete {
background: #FFF4F4;
border: 1px solid #F87171;
background: var(--notification-error-bg);
border: var(--border-width-thin) solid var(--notification-error-border);
}
.deleteIcon {
background: #EF4444;
color: white;
background: var(--notification-error-icon-bg);
color: var(--notification-error-icon-text);
}
.deleteTitle {
color: #DC2626;
color: var(--notification-error-title);
}
.deleteDetails {
color: #EF4444;
color: var(--notification-error-details);
}
/* Development variant styles */
.development {
background: #FFF8E1;
border: 1px solid #FFB74D;
background: var(--notification-warning-bg);
border: var(--border-width-thin) solid var(--notification-warning-border);
}
.developmentIcon {
font-size: 1.5rem;
color: #FF9800;
color: var(--notification-warning-icon);
}
/* Test label styles */
.testLabel {
background: #FF9800;
color: white;
font-size: 0.7rem;
font-weight: 700;
background: var(--notification-warning-icon);
color: var(--color-white);
font-size: var(--font-size-xs);
font-weight: var(--font-weight-bold);
padding: 0.2rem 0.4rem;
border-radius: 0.25rem;
border-radius: var(--border-radius-sm);
text-transform: uppercase;
letter-spacing: 0.5px;
}
@ -143,15 +143,15 @@
position: absolute;
top: 100%;
right: 0;
margin-top: 0.5rem;
background: #333;
color: white;
padding: 0.75rem 1rem;
border-radius: 0.375rem;
font-size: 0.875rem;
margin-top: var(--spacing-sm);
background: var(--tooltip-bg);
color: var(--tooltip-text);
padding: var(--spacing-md) var(--spacing-lg);
border-radius: var(--border-radius-md);
font-size: var(--font-size-base);
white-space: nowrap;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 10;
box-shadow: var(--shadow-xl);
z-index: var(--z-dropdown);
animation: fadeIn 0.2s ease-out;
}
@ -159,10 +159,10 @@
content: '';
position: absolute;
bottom: 100%;
right: 1rem;
right: var(--spacing-lg);
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid #333;
border-bottom: 6px solid var(--tooltip-bg);
}
@keyframes fadeIn {

View File

@ -4,7 +4,7 @@
.elementHeading {
margin: 0;
color: #8E8E8E;
color: var(--text-tertiary);
font-size: 0.8rem;
font-style: normal;
font-weight: 520;
@ -24,15 +24,15 @@
.participantChip {
display: flex;
align-items: center;
background-color: #F0F8FF;
border: 1px solid #D1E7FF;
background-color: var(--chip-bg);
border: 1px solid var(--chip-border);
border-radius: 1.25rem;
padding: 0.375rem 0.75rem;
font-size: 0.875rem;
color: #2563EB;
color: var(--chip-text);
gap: 0.5rem;
transition: all 0.2s ease;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: var(--chip-shadow);
}
@ -48,38 +48,38 @@
}
.clickableChip:hover {
background-color: #E0F2FE;
border-color: #BAE6FD;
background-color: var(--chip-hover-bg);
border-color: var(--chip-hover-border);
}
.clickableChip:focus {
outline: 2px solid #2563EB;
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
.clickableChip:active {
background-color: #BFDBFE;
background-color: var(--chip-active-bg);
transform: scale(0.98);
}
.removeIcon {
color: #2563EB;
color: var(--chip-text);
font-size: 0.875rem;
font-weight: bold;
margin-left: 0.25rem;
}
.defaultUserChip {
background-color: #F3F4F6;
border-color: #D1D5DB;
color: #374151;
background-color: var(--chip-default-bg);
border-color: var(--chip-default-border);
color: var(--chip-default-text);
}
.defaultUserChip:hover {
background-color: #F3F4F6;
border-color: #D1D5DB;
color: #374151;
background-color: var(--chip-default-bg);
border-color: var(--chip-default-border);
color: var(--chip-default-text);
cursor: default;
}
@ -93,23 +93,24 @@
.searchInput {
width: 100%;
margin-bottom: 10px;
border: 1px solid #D2D9E0;
border: 1px solid var(--input-border);
border-radius: 0.5rem;
font-size: 16px;
background-color: #FAFBFC;
background-color: var(--input-bg);
color: var(--input-text);
padding: 1rem;
font-family: inherit;
box-sizing: border-box;
}
.searchInput::placeholder {
color: #adadad;
color: var(--input-placeholder);
}
.searchInput:focus {
outline: 2px solid #2563EB;
outline: 2px solid var(--color-primary);
outline-offset: -1px;
border-color: #2563EB;
border-color: var(--color-primary);
}
.dropdown {
@ -117,10 +118,10 @@
top: 100%;
left: 0;
right: 0;
background: white;
border: 1px solid #D2D9E0;
background: var(--dropdown-bg);
border: 1px solid var(--dropdown-border);
border-radius: 0.5rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.08);
box-shadow: var(--dropdown-shadow);
z-index: 1000;
max-height: 300px;
overflow-y: auto;
@ -132,12 +133,12 @@
}
.section:not(:last-child) {
border-bottom: 1px solid #F1F3F4;
border-bottom: 1px solid var(--dropdown-divider);
}
.sectionHeader {
font-weight: 600;
color: #5F6368;
color: var(--text-tertiary);
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.5px;
@ -150,8 +151,9 @@
cursor: pointer;
transition: background-color 0.2s;
border: none;
border-bottom: 1px solid #F1F3F4;
border-bottom: 1px solid var(--dropdown-divider);
background: none;
color: var(--text-primary);
width: 100%;
text-align: left;
font-family: inherit;
@ -165,11 +167,11 @@
}
.dropdownItem:hover {
background-color: #F8F9FA;
background-color: var(--dropdown-hover-bg);
}
.dropdownItem:active {
background-color: #E8F0FE;
background-color: var(--dropdown-active-bg);
}
.personAvatar {
@ -190,8 +192,8 @@
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #2563EB;
color: white;
background-color: var(--color-primary);
color: var(--color-white);
display: flex;
align-items: center;
justify-content: center;
@ -208,7 +210,7 @@
.personName {
font-weight: 500;
color: #202124;
color: var(--text-primary);
font-size: 0.875rem;
display: flex;
align-items: center;
@ -217,11 +219,11 @@
.personUsername {
font-size: 0.75rem;
color: #5F6368;
color: var(--text-secondary);
}
.addNewItem {
color: #1A73E8;
color: var(--color-primary);
font-weight: 500;
opacity: 0.5;
cursor: not-allowed;
@ -232,46 +234,46 @@
}
.selectedItem {
background-color: #F0F8FF;
background-color: var(--dropdown-selected-bg);
opacity: 0.7;
}
.selectedItem:hover {
background-color: #E0F2FE;
background-color: var(--dropdown-selected-hover-bg);
}
.selectedIndicator {
color: #2563EB;
color: var(--color-primary);
font-weight: bold;
margin-left: 0.5rem;
}
.focusedItem {
background-color: #2563EB !important;
color: white !important;
background-color: var(--color-primary) !important;
color: var(--color-white) !important;
}
.focusedItem .personName {
color: white !important;
color: var(--color-white) !important;
}
.focusedItem .personUsername {
color: rgba(255, 255, 255, 0.8) !important;
color: var(--color-white-transparent) !important;
}
.focusedItem .selectedIndicator {
color: white !important;
color: var(--color-white) !important;
}
.focusedItem .avatarInitials {
background-color: rgba(255, 255, 255, 0.2);
color: white;
background-color: var(--color-white-transparent-low);
color: var(--color-white);
}
.noResults {
padding: 1rem;
text-align: center;
color: #5F6368;
color: var(--text-secondary);
font-size: 0.875rem;
font-style: italic;
}
@ -283,7 +285,7 @@
.compactElementHeading {
font-size: 0.75rem;
color: #717171;
color: var(--text-tertiary);
font-weight: 500;
margin-bottom: 0.4rem;
margin-top: 0;
@ -294,17 +296,18 @@
.compactSearchInput {
width: 100%;
padding: 0.5rem 1rem;
border: 1px solid #ccc;
border: 1px solid var(--input-border);
border-radius: 0.375rem;
font-size: 16px;
background-color: white;
background-color: var(--input-bg);
color: var(--input-text);
font-family: inherit;
transition: border-color 0.2s ease;
box-sizing: border-box;
}
.compactSearchInput:focus {
outline: 2px solid #007AFF;
outline: 2px solid var(--color-primary);
outline-offset: 2px;
border-color: #007AFF;
border-color: var(--color-primary);
}

View File

@ -1,6 +1,6 @@
.elementHeading {
margin: 0;
color: #8E8E8E;
color: var(--text-tertiary);
font-size: 0.8rem;
font-style: normal;
font-weight: 520;

View File

@ -0,0 +1,25 @@
import React from 'react';
import { useTheme } from '../context/ThemeContext';
import styles from './ThemeToggle.module.css';
export const ThemeToggle = () => {
const { isDarkMode, toggleTheme } = useTheme();
return (
<button
className={styles.toggleButton}
onClick={toggleTheme}
aria-label={`Switch to ${isDarkMode ? 'light' : 'dark'} mode`}
title={`Switch to ${isDarkMode ? 'light' : 'dark'} mode`}
>
<div className={styles.toggleContainer}>
<div className={`${styles.toggle} ${isDarkMode ? styles.dark : styles.light}`}>
<div className={styles.toggleHandle}></div>
</div>
<span className={styles.toggleLabel}>
{isDarkMode ? '🌙' : '☀️'}
</span>
</div>
</button>
);
};

View File

@ -0,0 +1,107 @@
.toggleButton {
background: none;
border: none;
cursor: pointer;
padding: var(--spacing-xs);
border-radius: var(--border-radius-md);
transition: var(--transition-fast);
display: flex;
align-items: center;
justify-content: center;
-webkit-tap-highlight-color: transparent;
}
.toggleButton:hover {
background-color: var(--bg-muted);
}
.toggleContainer {
display: flex;
align-items: center;
gap: var(--spacing-sm);
}
.toggle {
width: 44px;
height: 24px;
border-radius: 12px;
position: relative;
transition: var(--transition-medium);
border: 1px solid var(--border-medium);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
}
.toggle.light {
background: linear-gradient(to bottom, #ffffff, #f0f0f0);
}
.toggle.dark {
background: linear-gradient(to bottom, #4a4a4a, #2a2a2a);
}
.toggleHandle {
width: 18px;
height: 18px;
border-radius: 50%;
background: linear-gradient(to bottom, #ffffff, #e0e0e0);
border: 1px solid rgba(0, 0, 0, 0.1);
position: absolute;
top: 50%;
transform: translateY(-50%);
transition: var(--transition-medium);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
.toggle.light .toggleHandle {
left: 2px;
background: linear-gradient(to bottom, #ffd700, #ffed4a);
border-color: rgba(255, 165, 0, 0.3);
}
.toggle.dark .toggleHandle {
left: calc(100% - 20px);
background: linear-gradient(to bottom, #e6e6e6, #cccccc);
border-color: rgba(0, 0, 0, 0.2);
}
.toggleLabel {
font-size: var(--font-size-lg);
line-height: 1;
user-select: none;
min-width: 20px;
text-align: center;
}
/* iPhone-style pressed state */
.toggleButton:active .toggle {
transform: scale(0.95);
}
.toggleButton:active .toggleHandle {
width: 20px;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.toggleContainer {
gap: var(--spacing-xs);
}
.toggle {
width: 40px;
height: 22px;
}
.toggleHandle {
width: 16px;
height: 16px;
}
.toggle.dark .toggleHandle {
left: calc(100% - 18px);
}
.toggleLabel {
font-size: var(--font-size-md);
}
}

View File

@ -3,14 +3,15 @@
.container {
display: flex;
flex-direction: row;
border: 1px solid #CECECE;
border: 1px solid var(--timecard-border);
min-width: 90px;
gap: 1rem;
padding: 1rem;
align-items: center;
justify-content: space-between;
border-radius: 6px;
background-color: #F8FBFC;
background-color: var(--timecard-bg);
color: var(--timecard-text);
width: 135px;
height: 20px;
transition: all 0.15s ease;
@ -18,36 +19,36 @@
@media (hover: hover) {
.container:hover {
background-color: #E5E5E5;
background-color: var(--timecard-hover-bg);
}
}
.container:active,
.container[data-pressed] {
background-color: #D1D5DB;
background-color: var(--timecard-active-bg);
transform: translateY(1px);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
box-shadow: var(--timecard-active-shadow);
transition: all 0.1s ease;
}
.container[data-focus-visible] {
outline: 2px solid #2563EB;
outline: 2px solid var(--color-primary);
outline-offset: -1px;
}
.selected {
background-color: #2563EB !important;
color: white !important;
border-color: #1d4ed8 !important;
box-shadow: 0 2px 8px rgba(37, 99, 235, 0.3);
background-color: var(--color-primary) !important;
color: var(--color-white) !important;
border-color: var(--color-primary-dark) !important;
box-shadow: var(--timecard-selected-shadow);
}
.selected .upToText {
color: rgba(255, 255, 255, 0.8) !important;
color: var(--color-white-transparent) !important;
}
.selected .hoursText {
color: white !important;
color: var(--color-white) !important;
}
.container p {
@ -62,13 +63,13 @@
.upToText {
font-weight: 300;
color: #919191;
color: var(--timecard-muted-text);
font-size: 0.9rem
}
.hoursText {
font-weight: 500;
color:#686765;
color: var(--timecard-secondary-text);
font-size: 0.9rem;
}
@ -78,9 +79,9 @@
}
.unavailableSlot {
background-color: #E5E5E5;
color: #B1B1B1;
border: 1px solid #CECECE;
background-color: var(--timecard-unavailable-bg);
color: var(--timecard-unavailable-text);
border: 1px solid var(--timecard-unavailable-border);
height: 50px;
width: 165px;
}
@ -88,7 +89,7 @@
.modalFooter {
height: fit-content;
width: 100%;
color: blue;
color: var(--color-primary);
display: flex;
align-items: center;
gap: 1rem;
@ -100,52 +101,52 @@
.cancelButton {
flex: 2;
background-color: white;
background-color: var(--modal-cancel-bg);
height: 4rem;
color: #374151;
color: var(--modal-cancel-text);
font-weight: 600;
border: 2px solid #d1d5db;
border: 2px solid var(--modal-cancel-border);
border-radius: 0.5rem;
transition: all 0.2s ease;
}
@media (hover: hover) {
.cancelButton:hover {
background-color: #f9fafb;
border-color: #9ca3af;
background-color: var(--modal-cancel-hover-bg);
border-color: var(--modal-cancel-hover-border);
cursor: pointer;
}
}
.saveButton {
flex: 3;
background-color: #059669;
color: white;
background-color: var(--modal-save-bg);
color: var(--modal-save-text);
height: 4rem;
font-weight: 600;
font-size: 1.1rem;
border: 2px solid #047857;
border: 2px solid var(--modal-save-border);
border-radius: 0.5rem;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgba(5, 150, 105, 0.2);
box-shadow: var(--modal-save-shadow);
}
@media (hover: hover) {
.saveButton:hover {
background-color: #047857;
box-shadow: 0 4px 8px rgba(5, 150, 105, 0.3);
background-color: var(--modal-save-hover-bg);
box-shadow: var(--modal-save-hover-shadow);
cursor: pointer;
}
}
.saveButton:active {
background-color: #065f46;
background-color: var(--modal-save-active-bg);
transform: translateY(1px);
box-shadow: 0 1px 2px rgba(5, 150, 105, 0.2);
box-shadow: var(--modal-save-active-shadow);
}
.cancelButton:active {
background-color: #e5e7eb;
background-color: var(--modal-cancel-active-bg);
transform: translateY(1px);
}
@ -163,7 +164,7 @@
.sectionWithTitle label {
font-size: 0.8rem;
color: #717171;
color: var(--text-tertiary);
}
.sectionWithTitle p {
@ -171,7 +172,7 @@
}
.modalContainer {
background-color: white;
background-color: var(--modal-bg);
width: 85%;
max-width: 400px;
}
@ -180,9 +181,9 @@
.timeDisplay {
margin: 1rem 0;
padding: 1rem;
background-color: #f8f9fa;
background-color: var(--modal-display-bg);
border-radius: 8px;
border: 1px solid #e9ecef;
border: 1px solid var(--modal-display-border);
}
.timeRange {
@ -200,7 +201,7 @@
.startTime label, .endTime label {
font-size: 0.75rem;
color: #6c757d;
color: var(--text-secondary);
font-weight: 500;
margin-bottom: 0.25rem;
text-transform: uppercase;
@ -210,11 +211,11 @@
.timeValue {
font-size: 1.5rem;
font-weight: 600;
color: #212529;
color: var(--text-primary);
}
.timeValue.placeholder {
color: #adb5bd;
color: var(--text-muted);
font-style: italic;
font-size: 1rem;
}
@ -222,28 +223,28 @@
.timeSeparator {
font-size: 1.5rem;
font-weight: 300;
color: #6c757d;
color: var(--text-secondary);
margin: 0 0.5rem;
}
/* Disabled button styles */
.disabledButton {
background-color: #f8f9fa !important;
color: #adb5bd !important;
border: 2px dashed #dee2e6 !important;
background-color: var(--button-disabled-bg) !important;
color: var(--button-disabled-text) !important;
border: 2px dashed var(--button-disabled-border) !important;
cursor: not-allowed !important;
opacity: 0.6 !important;
}
@media (hover: hover) {
.disabledButton:hover {
background-color: #f8f9fa !important;
background-color: var(--button-disabled-bg) !important;
cursor: not-allowed !important;
transform: none !important;
}
}
.disabledButton:active {
background-color: #f8f9fa !important;
background-color: var(--button-disabled-bg) !important;
transform: none !important;
}

View File

@ -0,0 +1,74 @@
import React, { createContext, useContext, useState, useEffect } from 'react';
const ThemeContext = createContext();
export const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
};
export const ThemeProvider = ({ children }) => {
const [isDarkMode, setIsDarkMode] = useState(() => {
// Check localStorage for saved preference
const savedTheme = localStorage.getItem('theme');
if (savedTheme) {
return savedTheme === 'dark';
}
// Check system preference if no saved preference
if (window.matchMedia) {
return window.matchMedia('(prefers-color-scheme: dark)').matches;
}
// Default to light mode
return false;
});
useEffect(() => {
// Apply theme to document element
document.documentElement.setAttribute('data-theme', isDarkMode ? 'dark' : 'light');
// Save to localStorage
localStorage.setItem('theme', isDarkMode ? 'dark' : 'light');
}, [isDarkMode]);
// Listen for system theme changes
useEffect(() => {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
const handleChange = (e) => {
// Only update if user hasn't manually set a preference
const savedTheme = localStorage.getItem('theme');
if (!savedTheme) {
setIsDarkMode(e.matches);
}
};
mediaQuery.addEventListener('change', handleChange);
return () => mediaQuery.removeEventListener('change', handleChange);
}, []);
const toggleTheme = () => {
setIsDarkMode(prev => !prev);
};
const setTheme = (theme) => {
setIsDarkMode(theme === 'dark');
};
const value = {
isDarkMode,
theme: isDarkMode ? 'dark' : 'light',
toggleTheme,
setTheme
};
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
};

View File

@ -0,0 +1,631 @@
export const scheduleData = [
// Week 45
{
week: 45,
activities: [
{
id: 1,
date: '2024-11-04',
time: '13:00-14:45',
location: 'Aula NOD',
type: 'Föreläsning',
title: 'Föreläsning 1',
description: 'Introduktion till kursen, Konceptuell modellering och databasmodellering, UML analysmönster.',
teacher: 'Ann Maria Dorotea Bergholtz'
},
{
id: 2,
date: '2024-11-05',
time: '13:00-14:45',
location: '',
type: 'Handledning',
title: 'Handledning 1',
description: 'Handledning.',
teacher: 'Ann Maria Dorotea Bergholtz'
},
{
id: 3,
date: '2024-11-06',
time: '13:00-14:45',
location: 'Lilla Hörsalen',
type: 'Lektion',
title: 'Lektion 1 grp 1',
description: 'Konceptuell modellering. Anmälan till lektionsdeltagande krävs. Lektionsgruppsanmälan görs i Daisy.',
teacher: 'Anders Thelemyr'
},
{
id: 4,
date: '2024-11-06',
time: '15:00-16:45',
location: 'Lilla Hörsalen',
type: 'Lektion',
title: 'Lektion 1 grp 2',
description: 'Konceptuell modellering. Anmälan till lektionsdeltagande krävs. Lektionsgruppsanmälan görs i Daisy.',
teacher: 'Ann Maria Dorotea Bergholtz'
},
{
id: 5,
date: '2024-11-07',
time: '10:00-11:45',
location: '',
type: 'Handledning',
title: 'Handledning 2',
description: 'Handledning.',
teacher: 'Anders Thelemyr, Workneh Yilma Ayele'
},
{
id: 6,
date: '2024-11-07',
time: '15:00-16:45',
location: '',
type: 'Handledning',
title: 'Handledning 3',
description: 'INSTÄLLD.',
teacher: 'Ann Maria Dorotea Bergholtz',
cancelled: true
}
]
},
// Week 46
{
week: 46,
activities: [
{
id: 7,
date: '2024-11-11',
time: '16:00-17:45',
location: 'Aula NOD',
type: 'Föreläsning',
title: 'Föreläsning 2',
description: 'Syntetisk databasdesign: Översättning från UML till relationsmodellen.',
teacher: 'Ann Maria Dorotea Bergholtz'
},
{
id: 8,
date: '2024-11-12',
time: '13:00-14:45',
location: 'Aula NOD',
type: 'Introduktion',
title: 'Introduktion 1',
description: 'Introduktion till DBMS och projekt Databasdesign. Vi löser en mindre uppgift som liknar den som skall göras i projekt Databasdesign.',
teacher: 'Ann Maria Dorotea Bergholtz'
},
{
id: 9,
date: '2024-11-13',
time: '10:00-11:45',
location: 'Lilla Hörsalen',
type: 'Lektion',
title: 'Lektion 2 grp 1',
description: 'Relationsmodellen och syntetisk databasdesign. Anmälan till lektionsdeltagande krävs. Lektionsgruppsanmälan görs i Daisy.',
teacher: 'Anders Thelemyr'
},
{
id: 10,
date: '2024-11-13',
time: '13:00-14:45',
location: '',
type: 'Handledning',
title: 'Handledning 4',
description: 'Handledning. Via handledning.dsv.su.se - ange zoomid eller plats i NOD. Använd helst ingen passcode om Zoom används.',
teacher: 'Workneh Yilma Ayele'
},
{
id: 11,
date: '2024-11-13',
time: '15:00-16:45',
location: 'Lilla Hörsalen',
type: 'Lektion',
title: 'Lektion 2 grp 2',
description: 'Relationsmodellen och syntetisk databasdesign. Anmälan till lektionsdeltagande krävs. Lektionsgruppsanmälan görs i Daisy.',
teacher: 'Ann Maria Dorotea Bergholtz'
},
{
id: 12,
date: '2024-11-15',
time: '13:00-15:45',
location: '',
type: 'Handledning',
title: 'Handledning 5',
description: 'Handledning. Via handledning.dsv.su.se - ange zoomid eller plats i NOD. Använd helst ingen passcode om Zoom används.',
teacher: 'Anders Thelemyr, Workneh Yilma Ayele'
}
]
},
// Week 47
{
week: 47,
activities: [
{
id: 13,
date: '2024-11-18',
time: '13:00-14:45',
location: 'Aula NOD',
type: 'Föreläsning',
title: 'Föreläsning 3',
description: 'SQL I : inledande operatorer',
teacher: 'Anders Thelemyr'
},
{
id: 14,
date: '2024-11-18',
time: '14:45-15:45',
location: 'L30',
type: 'Info',
title: 'Info om år 3 EIT frivillig SVL',
description: '',
teacher: ''
},
{
id: 15,
date: '2024-11-19',
time: '12:00-17:00',
location: 'G10:6',
type: 'Redovisning',
title: 'Redovisning 1 grp 1',
description: 'Redov. första delen av Projekt Databasdesign. Tidsschema per grupp för Red 1 (och även Red 2) kommer på kurswebbplatsen senast dagen efter deadline för projektgruppsanmälan.',
teacher: 'Ann Maria Dorotea Bergholtz',
important: true
},
{
id: 16,
date: '2024-11-19',
time: '12:00-17:00',
location: 'G10:7',
type: 'Redovisning',
title: 'Redovisning 1 grp 2',
description: 'Redov. första delen av Projekt Databasdesign. Tidsschema per grupp för Red 1 (och även Red 2) kommer på kurswebbplatsen senast dagen efter deadline för projektgruppsanmälan.',
teacher: 'Anders Thelemyr',
important: true
},
{
id: 17,
date: '2024-11-19',
time: '12:00-17:00',
location: 'G10:8',
type: 'Redovisning',
title: 'Redovisning 1 grp 3',
description: 'Redov. första delen av Projekt Databasdesign. Tidsschema per grupp för Red 1 (och även Red 2) kommer på kurswebbplatsen senast dagen efter deadline för projektgruppsanmälan.',
teacher: 'Workneh Yilma Ayele',
important: true
},
{
id: 18,
date: '2024-11-20',
time: '10:00-11:45',
location: '',
type: 'Handledning',
title: 'Handledning 6',
description: 'Handledning. Via handledning.dsv.su.se - ange zoomid eller plats i NOD. Använd helst ingen passcode om Zoom används.',
teacher: 'Adrian Olbricht, Jennifer Skopac, Melle Carnesten'
},
{
id: 19,
date: '2024-11-22',
time: '08:00-09:45',
location: 'Lilla Hörsalen',
type: 'Lektion',
title: 'Lektion 3 grp 1',
description: 'SQL introduktion - enkla operatorer. Anmälan till lektionsdeltagande krävs. Lektionsgruppsanmälan görs i Daisy.',
teacher: 'Anders Thelemyr'
},
{
id: 20,
date: '2024-11-22',
time: '13:00-14:45',
location: 'Lilla Hörsalen',
type: 'Lektion',
title: 'Lektion 3 grp 2',
description: 'SQL introduktion - enkla operatorer. Anmälan till lektionsdeltagande krävs. Lektionsgruppsanmälan görs i Daisy.',
teacher: 'Ann Maria Dorotea Bergholtz'
}
]
},
// Week 48
{
week: 48,
activities: [
{
id: 21,
date: '2024-11-25',
time: '10:00-11:45',
location: 'Aula NOD',
type: 'Föreläsning',
title: 'Föreläsning 4',
description: 'SQL II: avancerade operatorer.',
teacher: 'Anders Thelemyr'
},
{
id: 22,
date: '2024-11-25',
time: '13:00-14:45',
location: '',
type: 'Handledning',
title: 'Handledning 7',
description: 'Handledning. Via handledning.dsv.su.se - ange zoomid eller plats i NOD. Använd helst ingen passcode om Zoom används.',
teacher: 'Jennifer Skopac, Melle Carnesten'
},
{
id: 23,
date: '2024-11-27',
time: '15:00-16:45',
location: '',
type: 'Handledning',
title: 'Handledning 8',
description: 'Handledning. Via handledning.dsv.su.se - ange zoomid eller plats i NOD. Använd helst ingen passcode om Zoom används.',
teacher: 'Melle Carnesten, Workneh Yilma Ayele'
},
{
id: 24,
date: '2024-11-29',
time: '11:00-12:00',
location: 'L30',
type: 'Info',
title: 'Info om år 3 MarkIT frivillig',
description: '',
teacher: ''
},
{
id: 25,
date: '2024-11-29',
time: '13:00-14:45',
location: 'Lilla Hörsalen',
type: 'Lektion',
title: 'Lektion 4 grp 1',
description: 'SQL fortsättning. Anmälan till lektionsdeltagande krävs. Lektionsgruppsanmälan görs i Daisy.',
teacher: 'Anders Thelemyr'
},
{
id: 26,
date: '2024-11-29',
time: '15:00-16:45',
location: 'Aula NOD',
type: 'Lektion',
title: 'Lektion 4 grp 2',
description: 'SQL fortsättning. Anmälan till lektionsdeltagande krävs. Lektionsgruppsanmälan görs i Daisy.',
teacher: 'Ann Maria Dorotea Bergholtz'
}
]
},
// Week 49
{
week: 49,
activities: [
{
id: 27,
date: '2024-12-02',
time: '13:00-14:45',
location: '',
type: 'Handledning',
title: 'Handledning 9',
description: 'Handledning. Via handledning.dsv.su.se - ange zoomid eller plats i NOD. Använd helst ingen passcode om Zoom används.',
teacher: 'Adrian Olbricht, Jennifer Skopac, Melle Carnesten'
},
{
id: 28,
date: '2024-12-03',
time: '15:00-16:45',
location: 'Aula NOD',
type: 'Föreläsning',
title: 'Föreläsning 5',
description: 'Frågespråk för relationsmodellen: relationsalgebra.',
teacher: 'Ann Maria Dorotea Bergholtz'
},
{
id: 29,
date: '2024-12-04',
time: '10:00-11:45',
location: '',
type: 'Handledning',
title: 'Handledning 10',
description: 'Handledning. Via handledning.dsv.su.se - ange zoomid eller plats i NOD. Använd helst ingen passcode om Zoom används.',
teacher: 'Adrian Olbricht, Edwin Sundberg, Workneh Yilma Ayele'
},
{
id: 30,
date: '2024-12-06',
time: '10:00-11:45',
location: 'Lilla Hörsalen',
type: 'Lektion',
title: 'Lektion 5 grp 1',
description: 'Relationsalgebra. Anmälan till lektionsdeltagande krävs. Lektionsgruppsanmälan görs i Daisy.',
teacher: 'Anders Thelemyr'
},
{
id: 31,
date: '2024-12-06',
time: '15:00-16:45',
location: 'Lilla Hörsalen',
type: 'Lektion',
title: 'Lektion 5 grp 2',
description: 'Relationsalgebra. Anmälan till lektionsdeltagande krävs. Lektionsgruppsanmälan görs i Daisy.',
teacher: 'Ann Maria Dorotea Bergholtz'
}
]
},
// Week 50
{
week: 50,
activities: [
{
id: 32,
date: '2024-12-10',
time: '13:00-18:00',
location: 'G10:6',
type: 'Redovisning',
title: 'Redovisning 2 grp 1',
description: 'Redov. av de fem första frågorna för projekt Frågespråk, både SQL och relationsalgebra.',
teacher: 'Ann Maria Dorotea Bergholtz',
important: true
},
{
id: 33,
date: '2024-12-10',
time: '13:00-18:00',
location: 'G10:7',
type: 'Redovisning',
title: 'Redovisning 2 grp 2',
description: 'Redov. av de fem första frågorna för projekt Frågespråk, både SQL och relationsalgebra.',
teacher: 'Anders Thelemyr',
important: true
},
{
id: 34,
date: '2024-12-10',
time: '13:00-18:00',
location: 'G10:8',
type: 'Redovisning',
title: 'Redovisning 2 grp 3',
description: 'Redov. av de fem första frågorna för projekt Frågespråk, både SQL och relationsalgebra.',
teacher: 'Workneh Yilma Ayele',
important: true
},
{
id: 35,
date: '2024-12-12',
time: '10:00-11:45',
location: 'Aula NOD',
type: 'Föreläsning',
title: 'Föreläsning 6',
description: 'Analytisk databasdesign: normalisering.',
teacher: 'Ann Maria Dorotea Bergholtz'
}
]
},
// Week 51
{
week: 51,
activities: [
{
id: 36,
date: '2024-12-16',
time: '10:00-11:45',
location: '',
type: 'Handledning',
title: 'Handledning 11',
description: 'Handledning. Via handledning.dsv.su.se - ange zoomid eller plats i NOD. Använd helst ingen passcode om Zoom används.',
teacher: 'Adrian Olbricht, Edwin Sundberg'
},
{
id: 37,
date: '2024-12-17',
time: '10:00-12:45',
location: '',
type: 'Handledning',
title: 'Handledning 12',
description: 'Handledning. Via handledning.dsv.su.se - ange zoomid eller plats i NOD. Använd helst ingen passcode om Zoom används.',
teacher: 'Edwin Sundberg, Workneh Yilma Ayele'
},
{
id: 38,
date: '2024-12-17',
time: '13:00-15:00',
location: 'Aula NOD',
type: 'Föreläsning',
title: 'Föreläsning 8',
description: 'Alternativ till relationsmodellen - noSQL-ansatser.',
teacher: 'Martin Duneld'
},
{
id: 39,
date: '2024-12-18',
time: '10:00-11:45',
location: 'Lilla Hörsalen',
type: 'Lektion',
title: 'Lektion 6 grp 1',
description: 'Analytisk databasdesign: normalisering. Anmälan till lektionsdeltagande krävs. Lektionsgruppsanmälan görs i Daisy.',
teacher: 'Anders Thelemyr'
},
{
id: 40,
date: '2024-12-18',
time: '13:00-14:45',
location: 'Lilla Hörsalen',
type: 'Lektion',
title: 'Lektion 6 grp 2',
description: 'Analytisk databasdesign: normalisering. Anmälan till lektionsdeltagande krävs. Lektionsgruppsanmälan görs i Daisy.',
teacher: 'Ann Maria Dorotea Bergholtz'
},
{
id: 41,
date: '2024-12-19',
time: '10:00-11:45',
location: '',
type: 'Handledning',
title: 'Handledning 14',
description: 'Handledning. Via handledning.dsv.su.se - ange zoomid eller plats i NOD. Använd helst ingen passcode om Zoom används.',
teacher: 'Edwin Sundberg, Melle Carnesten'
},
{
id: 42,
date: '2024-12-19',
time: '15:00-16:45',
location: 'Aula NOD',
type: 'Föreläsning',
title: 'Föreläsning 7',
description: 'Databashanteringssystem.',
teacher: 'Anders Thelemyr'
},
{
id: 43,
date: '2024-12-20',
time: '13:00-14:45',
location: '',
type: 'Handledning',
title: 'Handledning 13',
description: 'Handledning. Via handledning.dsv.su.se - ange zoomid eller plats i NOD. Använd helst ingen passcode om Zoom används.',
teacher: 'Anders Thelemyr, Edwin Sundberg'
}
]
},
// Week 52
{
week: 52,
activities: [
{
id: 44,
date: '2024-12-23',
time: '13:00-15:00',
location: '',
type: 'Handledning',
title: 'Handledning',
description: 'Zoom-handledning!!!',
teacher: 'Edwin Sundberg, Jennifer Skopac'
}
]
},
// Week 2 (January)
{
week: 2,
activities: [
{
id: 45,
date: '2025-01-07',
time: '15:00-16:45',
location: '',
type: 'Handledning',
title: 'Handledning 15',
description: 'Projekthandledning (ej labb). Via handledning.dsv.su.se - ange zoomid eller plats i NOD. Använd helst ingen passcode om Zoom används.',
teacher: 'Edwin Sundberg, Jennifer Skopac'
},
{
id: 46,
date: '2025-01-08',
time: '10:00-11:45',
location: '',
type: 'Handledning',
title: 'Handledning 16',
description: 'Projekthandledning (ej labb). Via handledning.dsv.su.se - ange zoomid eller plats i NOD. Använd helst ingen passcode om Zoom används.',
teacher: 'Edwin Sundberg, Workneh Yilma Ayele'
},
{
id: 47,
date: '2025-01-08',
time: '13:00-14:45',
location: 'Aula NOD',
type: 'Föreläsning',
title: 'Föreläsning 9',
description: 'Embedded SQL och intro till Labb.',
teacher: 'Nikos Dimitrakas'
},
{
id: 48,
date: '2025-01-09',
time: '09:00-11:45',
location: '',
type: 'Laboration',
title: 'Laboration',
description: 'Öronmärkt tillfälle för arbete på plats i NOD med laborationen i "Embedded SQL i Java". Under tillfället kan labbgrupper, som behöver, få hjälp med labben genom att ställa sig i kö via handledningssystemet.',
teacher: 'Anders Thelemyr, Edwin Sundberg, Nikos Dimitrakas'
},
{
id: 49,
date: '2025-01-10',
time: '09:00-11:45',
location: '',
type: 'Laboration',
title: 'Laboration',
description: 'Öronmärkt tillfälle för arbete på plats i NOD med laborationen i "Embedded SQL i Java". Under tillfället kan labbgrupper, som behöver, få hjälp med labben genom att ställa sig i kö via handledningssystemet.',
teacher: 'Anders Thelemyr, Edwin Sundberg, Nikos Dimitrakas'
}
]
},
// Week 3 (January)
{
week: 3,
activities: [
{
id: 50,
date: '2025-01-13',
time: '09:00-11:45',
location: '',
type: 'Laboration',
title: 'Laboration',
description: 'Öronmärkt tillfälle för arbete på plats i NOD med laborationen i "Embedded SQL i Java". Under tillfället kan labbgrupper, som behöver, få hjälp med labben genom att ställa sig i kö via handledningssystemet.',
teacher: 'Anders Thelemyr, Edwin Sundberg, Nikos Dimitrakas'
},
{
id: 51,
date: '2025-01-13',
time: '15:00-16:45',
location: 'Aula NOD',
type: 'Föreläsning',
title: 'Föreläsning 10',
description: 'Genomgång av en exempeltentamen',
teacher: 'Ann Maria Dorotea Bergholtz'
},
{
id: 52,
date: '2025-01-14',
time: '10:00-11:45',
location: '',
type: 'Handledning',
title: 'Handledning 18',
description: 'Handledning för projekt och ev. kvarvarande övriga uppgifter. Via handledning.dsv.su.se - ange zoomid eller plats i NOD. Använd helst ingen passcode om Zoom används.',
teacher: 'Adrian Olbricht, Edwin Sundberg, Jennifer Skopac, Melle Carnesten'
},
{
id: 53,
date: '2025-01-17',
time: '13:00-14:45',
location: '',
type: 'Handledning',
title: 'Handledning 17',
description: 'Handledning för projekt och ev. kvarvarande övriga uppgifter. Via handledning.dsv.su.se - ange zoomid eller plats i NOD. Använd helst ingen passcode om Zoom används.',
teacher: 'Adrian Olbricht, Edwin Sundberg, Jennifer Skopac, Melle Carnesten'
}
]
}
];
export const examData = [
{
id: 'exam1',
date: '2025-01-16',
time: '13:00-17:00',
type: 'Tentamen',
title: 'Tentamen (4hp)',
location: 'Aula NOD + flera salar',
important: true,
isExam: true
},
{
id: 'exam2',
date: '2025-01-19',
time: '',
type: 'Projektarbete',
title: 'Projektarbete (3.5hp)',
location: '',
important: true,
isExam: true
}
];
export const courseInfo = {
title: 'Databasmetodik',
code: 'DB HT2024',
credits: '7,5 hp',
coordinator: 'Ann Maria Dorotea Bergholtz',
examCredits: '4 hp',
projectCredits: '3,5 hp'
};

View File

@ -0,0 +1,24 @@
export const coursesData = [
{
id: 'db-ht2024',
title: 'Databasmetodik',
code: 'DB HT2024',
credits: '7,5 hp',
coordinator: 'Ann Maria Dorotea Bergholtz',
period: 'HT 2024',
description: 'Kursen behandlar konceptuell modellering, databasdesign, SQL, relationsalgebra och normalisering.',
color: '#1d4ed8',
dataModule: () => import('./courseScheduleData')
},
{
id: 'proto2-vt2025',
title: 'Prototyper inom interaktionsdesign II',
code: 'PROTO2 VT2025',
credits: '7,5 hp',
coordinator: 'Ola Knutsson, Johan Stymne',
period: 'VT 2025',
description: 'Kursen behandlar designsprintar, prototypning i fysiska och digitala material, användarflöden och testning av prototyper.',
color: '#059669',
dataModule: () => import('./proto2ScheduleData')
}
];

View File

@ -0,0 +1,526 @@
export const scheduleData = [
// Week 13
{
week: 13,
activities: [
{
id: 1,
date: '2025-03-24',
time: '08:00-09:00',
location: 'L70',
type: 'Föreläsning',
title: 'Föreläsning 1',
description: 'Kursintroduktion and Introduktion till Designsprint Fas 1: Map. Det är obligatoriskt att deltaga i designsprinten. Eget arbete i designteamet 09:00-11:00',
teacher: 'Lon Hansson, Ola Knutsson'
},
{
id: 2,
date: '2025-03-24',
time: '11:00-11:45',
location: 'L70',
type: 'Seminarium',
title: 'Seminarium 1',
description: 'Designsprint Fas 1: Map, Debriefing',
teacher: 'Lon Hansson'
},
{
id: 3,
date: '2025-03-24',
time: '13:00-15:00',
location: 'L70',
type: 'Lektion',
title: 'Lektion 1',
description: 'Designsprint Fas 2: Sketch & Decide, Introduktion',
teacher: 'Lon Hansson'
},
{
id: 4,
date: '2025-03-24',
time: '15:00-15:45',
location: 'L70',
type: 'Seminarium',
title: 'Seminarium 2',
description: 'Designsprint Fas 2: Sketch & Decide, Debriefing',
teacher: 'Lon Hansson, Ola Knutsson'
},
{
id: 5,
date: '2025-03-25',
time: '08:00-11:45',
location: 'Studentlabb ID:fix',
type: 'Lektion',
title: 'Lektion 2 grp 1',
description: 'Designsprint Fas 3: Prototype in physical design material, Introduktion. Designsprint-grupp 1-7',
teacher: 'Johan Stymne'
},
{
id: 6,
date: '2025-03-25',
time: '13:00-16:45',
location: 'Studentlabb ID:fix',
type: 'Lektion',
title: 'Lektion 2 grp 2',
description: 'Designsprint Fas 3: Prototype in physical design material, Introduktion. Designsprint-grupp 8-14',
teacher: 'Johan Stymne'
},
{
id: 7,
date: '2025-03-27',
time: '08:00-08:45',
location: 'D3, D4',
type: 'Lektion',
title: 'Lektion 3',
description: 'Designsprint Fas 4:Prototype in digital design material, Wireframing, Introduktion. Uppstart av dagen. Eget arbete i designteamet 08:20-11:00',
teacher: 'Lon Hansson'
},
{
id: 8,
date: '2025-03-27',
time: '11:00-11:45',
location: 'D3, D4',
type: 'Seminarium',
title: 'Seminarium 3',
description: 'Designsprint Fas 4: Prototype in digital design material, Wireframing, Debriefing. D3: Designsprint-grupp 1-7, D4: Designsprint-grupp 8-14',
teacher: 'Lon Hansson'
},
{
id: 9,
date: '2025-03-27',
time: '13:00-16:45',
location: 'L50',
type: 'Lektion',
title: 'Lektion 4 grp 1',
description: 'Designsprint Fas 5: Test: Introduktion, Tester, Debriefing. Designsprint-grupp 1-7',
teacher: 'Lon Hansson'
},
{
id: 10,
date: '2025-03-27',
time: '13:00-16:45',
location: 'L70',
type: 'Lektion',
title: 'Lektion 4 grp 2',
description: 'Designsprint Fas 5: Test: Introduktion, Tester, Debriefing. Designsprint-grupp 8-14',
teacher: 'Ola Knutsson'
},
{
id: 11,
date: '2025-03-28',
time: '13:00-15:45',
location: 'Studentlabb ID:fix',
type: 'Laboration',
title: 'Laboration 1 grp 1',
description: 'Labb 1: Arduino. Muntlig redovisning på plats. Designsprint-grupp 1-5',
teacher: 'António Miguel Beleza Maciel Pinheiro Braga, Jacob Reinikainen Lindström, Johan Stymne'
}
]
},
// Week 14
{
week: 14,
activities: [
{
id: 12,
date: '2025-03-31',
time: '09:00-11:45',
location: 'Studentlabb ID:fix',
type: 'Laboration',
title: 'Laboration 1 grp 2',
description: 'Labb 1: Arduino. Muntlig redovisning på plats. Designsprint-grupp 6-10',
teacher: 'António Miguel Beleza Maciel Pinheiro Braga, Jacob Reinikainen Lindström, Johan Stymne'
},
{
id: 13,
date: '2025-03-31',
time: '13:00-15:45',
location: 'Studentlabb ID:fix',
type: 'Laboration',
title: 'Laboration 1 grp 3',
description: 'Labb 1: Arduino. Muntlig redovisning på plats. Designsprint-grupp 11-14',
teacher: 'António Miguel Beleza Maciel Pinheiro Braga, Jacob Reinikainen Lindström, Johan Stymne'
},
{
id: 14,
date: '2025-04-01',
time: '10:00-12:45',
location: 'L70',
type: 'Workshop',
title: 'Workshop 1',
description: 'UI och Interaktionsprogrammering, del 1. Designsprint-grupp 1-14. Vi kommer att sluta ca kl. 12',
teacher: 'Ola Knutsson'
},
{
id: 15,
date: '2025-04-02',
time: '08:00-11:45',
location: 'Studentlabb ID Höger',
type: 'Laboration',
title: 'Laboration 2 grp 1',
description: 'Labb 2: Gränssnittsdesign och prototypning med SwiftUI i XCode. Muntlig redovisning på plats och filinlämning i iLearn. Designsprint-grupp 1-5',
teacher: 'António Miguel Beleza Maciel Pinheiro Braga, Mika Haverås, Ola Knutsson'
},
{
id: 16,
date: '2025-04-02',
time: '13:00-16:45',
location: 'Studentlabb ID Höger',
type: 'Laboration',
title: 'Laboration 2 grp 2',
description: 'Labb 2: Gränssnittsdesign och prototypning med SwiftUI i XCode. Muntlig redovisning på plats och filinlämning i iLearn. Designsprint-grupp 6-10',
teacher: 'António Miguel Beleza Maciel Pinheiro Braga, Mika Haverås, Ola Knutsson'
},
{
id: 17,
date: '2025-04-03',
time: '09:00-12:45',
location: 'Studentlabb ID Höger',
type: 'Laboration',
title: 'Laboration 2 grp 3',
description: 'Labb 2: Gränssnittsdesign och prototypning med SwiftUI i XCode. Muntlig redovisning på plats och filinlämning i iLearn. Designsprint-grupp 11-14',
teacher: 'António Miguel Beleza Maciel Pinheiro Braga, Mika Haverås, Ola Knutsson'
},
{
id: 18,
date: '2025-04-03',
time: '15:00-17:00',
location: 'Lilla Hörsalen',
type: 'Workshop',
title: 'Workshop 2',
description: 'UI och Interaktionsprogrammering, del 2. Designsprint-grupp 1-14',
teacher: 'Ola Knutsson'
}
]
},
// Week 15
{
week: 15,
activities: [
{
id: 19,
date: '2025-04-07',
time: '08:00-11:00',
location: 'Studentlabb ID Höger',
type: 'Laboration',
title: 'Laboration 3 grp 1',
description: 'Labb 3: Jetpack Compose i Android Studio. Muntlig redovisning på plats och filinlämning i iLearn. Designsprint-grupp 1-5',
teacher: 'António Miguel Beleza Maciel Pinheiro Braga, Jacob Reinikainen Lindström, Lon Hansson, Ola Knutsson'
},
{
id: 20,
date: '2025-04-07',
time: '12:00-15:00',
location: 'Studentlabb ID Höger',
type: 'Laboration',
title: 'Laboration 3 grp 2',
description: 'Labb 3: Jetpack Compose i Android Studio. Muntlig redovisning på plats och filinlämning i iLearn. Designsprint-grupp 6-10',
teacher: 'António Miguel Beleza Maciel Pinheiro Braga, Jacob Reinikainen Lindström, Kim Lindfors, Lon Hansson, Ola Knutsson'
},
{
id: 21,
date: '2025-04-08',
time: '09:00-12:00',
location: 'Studentlabb ID Höger',
type: 'Laboration',
title: 'Laboration 3 grp 3',
description: 'Labb 3: Jetpack Compose i Android Studio. Muntlig redovisning på plats och filinlämning i iLearn. Designsprint-grupp 11-14',
teacher: 'António Miguel Beleza Maciel Pinheiro Braga, Jacob Reinikainen Lindström, Kim Lindfors, Lon Hansson, Ola Knutsson'
},
{
id: 22,
date: '2025-04-10',
time: '13:00-15:45',
location: 'Studentlabb ID:fix',
type: 'Handledning',
title: 'Handledning 1',
description: 'Skisser och Arduino',
teacher: 'António Miguel Beleza Maciel Pinheiro Braga, Jacob Reinikainen Lindström, Johan Stymne, Kim Lindfors, Mika Haverås'
}
]
},
// Week 16
{
week: 16,
activities: [
{
id: 23,
date: '2025-04-14',
time: '09:00-09:45',
location: 'L50',
type: 'Workshop',
title: 'Workshop 3 grp 2',
description: 'Kick-off för individuella designprojektet. Designsprint-grupp 8-14',
teacher: 'Ola Knutsson'
},
{
id: 24,
date: '2025-04-14',
time: '10:00-10:45',
location: 'L50',
type: 'Workshop',
title: 'Workshop 3 grp 1',
description: 'Kick-off för individuella designprojektet. Designsprint-grupp 1-7',
teacher: 'Ola Knutsson'
},
{
id: 25,
date: '2025-04-14',
time: '13:00-16:45',
location: 'D3, D4',
type: 'Laboration',
title: 'Laboration 4 grp 1',
description: 'Labb 4: HTML, Bootstrap & JavaScript. Designsprint-grupp 1-7',
teacher: 'Lon Hansson, Stefan Nenzén'
},
{
id: 26,
date: '2025-04-15',
time: '09:00-11:45',
location: 'Studentlabb ID:fix',
type: 'Handledning',
title: 'Handledning 2',
description: 'Användarflöden',
teacher: 'António Miguel Beleza Maciel Pinheiro Braga, Jacob Reinikainen Lindström, Johan Stymne'
},
{
id: 27,
date: '2025-04-15',
time: '13:00-16:45',
location: 'D3, D4',
type: 'Laboration',
title: 'Laboration 4 grp 2',
description: 'Labb 4: HTML, Bootstrap & JavaScript. Designsprint-grupp 8-14',
teacher: 'Lon Hansson, Stefan Nenzén'
},
{
id: 28,
date: '2025-04-16',
time: '09:00-11:45',
location: 'Studentlabb ID:fix',
type: 'Redovisning',
title: 'Redovisning 1 grp 1',
description: 'Interaktion, gränssnittsskisser och UI i fysiska och digitala material. Designsprint-grupp 1-7',
teacher: 'Johan Stymne, Ola Knutsson',
important: true
},
{
id: 29,
date: '2025-04-16',
time: '14:00-16:45',
location: 'Studentlabb ID:fix',
type: 'Redovisning',
title: 'Redovisning 1 grp 2',
description: 'Interaktion, gränssnittsskisser och UI i fysiska och digitala material. Designsprint-grupp 8-14',
teacher: 'Johan Stymne, Ola Knutsson',
important: true
}
]
},
// Week 17
{
week: 17,
activities: [
{
id: 30,
date: '2025-04-23',
time: '09:00-10:45',
location: 'L70',
type: 'Workshop',
title: 'Workshop 4 grp 2',
description: 'McElroys begrepp. Designsprint-grupp 8-14',
teacher: 'Johan Stymne'
},
{
id: 31,
date: '2025-04-23',
time: '13:00-14:45',
location: 'L70',
type: 'Workshop',
title: 'Workshop 4 grp 1',
description: 'McElroys begrepp. Designsprint-grupp 1-7',
teacher: 'Johan Stymne'
},
{
id: 32,
date: '2025-04-24',
time: '09:00-11:45',
location: 'Studentlabb ID:fix',
type: 'Handledning',
title: 'Handledning 3',
description: 'Gestaltning, Bredd, Djup, Interaktivitet, Data',
teacher: 'António Miguel Beleza Maciel Pinheiro Braga, Jacob Reinikainen Lindström, Johan Stymne, Kim Lindfors, Mika Haverås'
}
]
},
// Week 18
{
week: 18,
activities: [
{
id: 33,
date: '2025-04-29',
time: '13:00-14:45',
location: 'Lilla Hörsalen',
type: 'Workshop',
title: 'Workshop 5',
description: 'Designsystem och värdering. Designsprint-grupp 1-14',
teacher: 'Johan Stymne'
}
]
},
// Week 19
{
week: 19,
activities: [
{
id: 34,
date: '2025-05-05',
time: '09:00-11:45',
location: 'Studentlabb ID:fix',
type: 'Handledning',
title: 'Handledning 4',
description: 'Integration av prototypens delar',
teacher: 'António Miguel Beleza Maciel Pinheiro Braga, Jacob Reinikainen Lindström, Johan Stymne, Kim Lindfors, Mika Haverås'
}
]
},
// Week 20
{
week: 20,
activities: [
{
id: 35,
date: '2025-05-12',
time: '13:00-14:45',
location: 'L70',
type: 'Workshop',
title: 'Workshop 6 grp 1',
description: 'Testning av prototyper. Designsprint-grupp 1-7',
teacher: 'Johan Stymne, Lon Hansson'
},
{
id: 36,
date: '2025-05-12',
time: '15:00-16:45',
location: 'Lilla Hörsalen',
type: 'Workshop',
title: 'Workshop 6 grp 2',
description: 'Testning av prototyper. Designsprint-grupp 8-14',
teacher: 'Johan Stymne, Lon Hansson'
}
]
},
// Week 21
{
week: 21,
activities: [
{
id: 37,
date: '2025-05-19',
time: '09:00-11:45',
location: 'Studentlabb ID:fix',
type: 'Handledning',
title: 'Handledning 5',
description: 'Designsystem, Ändringar efter testerna',
teacher: 'António Miguel Beleza Maciel Pinheiro Braga, Jacob Reinikainen Lindström, Johan Stymne, Kim Lindfors'
},
{
id: 38,
date: '2025-05-22',
time: '15:00-17:00',
location: 'Studentlabb ID:fix',
type: 'Workshop',
title: 'Workshop 7',
description: 'Resttillfälle Workshop, möjlighet att hämta ut prototyper kl. 12 i ID:Fix för att förbereda dessa för testning.',
teacher: 'Ola Knutsson'
}
]
},
// Week 22
{
week: 22,
activities: [
{
id: 39,
date: '2025-05-28',
time: '12:00-14:45',
location: 'Studentlabb ID:fix',
type: 'Handledning',
title: 'Handledning 6',
description: 'Alla delar av projektet',
teacher: 'António Miguel Beleza Maciel Pinheiro Braga, Jacob Reinikainen Lindström, Mika Haverås, Ola Knutsson'
}
]
},
// Week 23
{
week: 23,
activities: [
{
id: 40,
date: '2025-06-02',
time: '12:00-13:45',
location: 'Studentlabb ID:fix',
type: 'Redovisning',
title: 'Redovisning 2 grp 1',
description: 'Visning och demo av de individuella designprojekten',
teacher: 'Jacob Reinikainen Lindström, Johan Stymne, Ola Knutsson',
important: true
},
{
id: 41,
date: '2025-06-02',
time: '14:00-15:45',
location: 'Studentlabb ID:fix',
type: 'Redovisning',
title: 'Redovisning 2 grp 2',
description: 'Visning och demo av de individuella designprojekten',
teacher: 'Jacob Reinikainen Lindström, Johan Stymne, Ola Knutsson',
important: true
},
{
id: 42,
date: '2025-06-03',
time: '12:00-13:45',
location: 'Studentlabb ID:fix',
type: 'Redovisning',
title: 'Redovisning 2 grp 3',
description: 'Visning och demo av de individuella designprojekten',
teacher: 'Johan Stymne, Mika Haverås, Ola Knutsson',
important: true
},
{
id: 43,
date: '2025-06-03',
time: '14:00-15:45',
location: 'Studentlabb ID:fix',
type: 'Redovisning',
title: 'Redovisning 2 grp 4',
description: 'Visning och demo av de individuella designprojekten',
teacher: 'Johan Stymne, Mika Haverås, Ola Knutsson',
important: true
}
]
}
];
export const examData = [
{
id: 'exam1',
date: '2025-06-09',
time: '',
type: 'Inlämningsuppgifter',
title: 'Inlämningsuppgifter (7,5hp)',
location: '',
important: true,
isExam: true
}
];
export const courseInfo = {
title: 'Prototyper inom interaktionsdesign II',
code: 'PROTO2 VT2025',
credits: '7,5 hp',
coordinator: 'Ola Knutsson, Johan Stymne',
examCredits: '7,5 hp',
projectCredits: 'Inlämningsuppgifter'
};

View File

@ -1,4 +1,5 @@
@import './fonts.css';
@import './styles/variables.css';
:root {
font-family: 'The Sans', system-ui, Avenir, Helvetica, Arial, sans-serif !important;
@ -6,8 +7,8 @@
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
color: var(--text-primary);
background-color: var(--bg-primary);
font-synthesis: none;
text-rendering: optimizeLegibility;
@ -17,11 +18,11 @@
a {
font-weight: 500;
color: #646cff;
color: var(--color-primary);
text-decoration: inherit;
}
a:hover {
color: #535bf2;
color: var(--color-primary-hover);
}
body {
@ -56,14 +57,14 @@ button {
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
color: var(--text-primary);
background-color: var(--bg-primary);
}
a:hover {
color: #747bff;
color: var(--color-primary);
}
button {
background-color: #f9f9f9;
background-color: var(--button-bg);
}
}

View File

@ -0,0 +1,44 @@
import React from 'react';
import { Link } from 'react-router-dom';
import styles from './CourseSchedule.module.css';
import { coursesData } from '../data/coursesData';
export function CourseSchedule() {
return (
<div className={styles.pageContainer}>
<div className={styles.header}>
<h1 className={styles.courseTitle}>Kursscheman</h1>
<p className={styles.courseInfo}>Välj en kurs för att se dess schema</p>
</div>
<div className={styles.content}>
<div className={styles.coursesList}>
{coursesData.map((course) => (
<Link
key={course.id}
to={`/course-schedule/${course.id}`}
className={styles.courseCard}
>
<div className={styles.courseCardHeader}>
<div
className={styles.courseColorIndicator}
style={{ backgroundColor: course.color }}
></div>
<div className={styles.courseCardContent}>
<h3 className={styles.courseCardTitle}>{course.title}</h3>
<p className={styles.courseCardCode}>{course.code} {course.credits}</p>
<p className={styles.courseCardDescription}>{course.description}</p>
<div className={styles.courseCardFooter}>
<span className={styles.courseCardPeriod}>{course.period}</span>
<span className={styles.courseCardCoordinator}>{course.coordinator}</span>
</div>
</div>
</div>
</Link>
))}
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,532 @@
.pageContainer {
min-height: 100vh;
background: var(--bg-iphone-gradient);
padding: var(--container-padding);
max-width: var(--page-max-width);
}
.header {
margin-bottom: var(--spacing-3xl);
text-align: center;
padding: var(--spacing-2xl) var(--spacing-lg);
background: var(--bg-iphone-header);
border: var(--border-width-thin) solid var(--border-dark);
border-radius: var(--border-radius-xl);
box-shadow: var(--shadow-iphone-inset), var(--shadow-iphone-outer);
}
.courseTitle {
font-size: var(--font-size-5xl);
font-weight: var(--font-weight-bold);
color: var(--color-black);
margin: 0 0 var(--spacing-sm) 0;
font-family: var(--font-primary);
text-shadow: var(--text-shadow-light);
}
.courseInfo {
color: var(--text-primary);
font-size: var(--font-size-lg);
margin: 0;
font-weight: var(--font-weight-medium);
text-shadow: var(--text-shadow-light);
}
.content {
display: flex;
flex-direction: column;
gap: var(--spacing-2xl);
}
.coursesList {
display: flex;
flex-direction: column;
gap: var(--spacing-lg);
}
.courseCard {
display: block;
background: var(--bg-primary);
border-radius: var(--border-radius-2xl);
overflow: hidden;
box-shadow: var(--shadow-lg);
text-decoration: none;
color: inherit;
transition: var(--transition-transform);
}
.courseCard:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-xl);
}
.courseCardHeader {
display: flex;
align-items: flex-start;
gap: var(--spacing-lg);
padding: var(--spacing-2xl);
}
.courseColorIndicator {
width: var(--color-indicator-width);
height: var(--color-indicator-height);
border-radius: var(--border-radius-sm);
flex-shrink: 0;
}
.courseCardContent {
flex: 1;
min-width: 0;
}
.courseCardTitle {
font-size: var(--font-size-3xl);
font-weight: var(--font-weight-semibold);
color: var(--color-gray-800);
margin: 0 0 var(--spacing-sm) 0;
line-height: var(--line-height-normal);
}
.courseCardCode {
color: var(--text-secondary);
font-size: var(--font-size-md);
margin: 0 0 var(--spacing-md) 0;
font-weight: var(--font-weight-medium);
}
.courseCardDescription {
color: var(--color-gray-600);
font-size: var(--font-size-md);
line-height: var(--line-height-relaxed);
margin: 0 0 var(--spacing-lg) 0;
}
.courseCardFooter {
display: flex;
flex-direction: column;
gap: var(--spacing-xs);
}
.courseCardPeriod {
color: var(--text-secondary);
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
}
.courseCardCoordinator {
color: var(--color-gray-400);
font-size: var(--font-size-sm);
}
/* Day grouping styles */
.dayGroup {
margin-bottom: 1.5rem;
}
.dayGroup:last-child {
margin-bottom: 0;
}
.dayHeader {
margin-bottom: 0.75rem;
padding: 0 1.25rem;
}
.dayTitle {
font-size: 1rem;
font-weight: 600;
color: #374151;
margin: 0;
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0;
border-bottom: 1px solid #e5e7eb;
}
.dayActivitiesList {
display: flex;
flex-direction: column;
gap: 0.75rem;
padding: 0 1.25rem;
}
.singleActivityWrapper {
padding: 0 1.25rem;
margin-bottom: 1rem;
}
/* Day divider styles */
.dayDivider {
background: var(--bg-iphone-day-divider);
border-top: var(--border-width-thin) solid var(--border-dark);
border-bottom: var(--border-width-thin) solid var(--border-dark);
padding: var(--spacing-sm) var(--spacing-xl);
margin: 0;
position: relative;
box-shadow: var(--shadow-iphone-day-divider);
}
.dayDivider:first-child {
margin-top: 0;
}
.dayDividerText {
font-size: var(--font-size-base);
font-weight: var(--font-weight-semibold);
color: var(--color-black);
display: flex;
align-items: center;
gap: var(--spacing-sm);
text-shadow: var(--text-shadow-light);
}
.todayIndicator {
color: var(--color-iphone-blue);
font-weight: var(--font-weight-medium);
font-size: var(--font-size-sm);
}
.section {
background: var(--bg-primary);
border: var(--border-width-thin) solid var(--border-dark);
border-radius: var(--border-radius-xl);
overflow: hidden;
box-shadow: var(--shadow-iphone-inset), var(--shadow-iphone-outer);
}
.sectionTitle {
font-size: var(--font-size-2xl);
font-weight: var(--font-weight-semibold);
color: var(--color-black);
margin: 0;
padding: var(--spacing-lg) var(--spacing-xl);
background: var(--bg-iphone-section);
border-bottom: var(--border-width-thin) solid var(--border-dark);
text-shadow: var(--text-shadow-light);
display: flex;
align-items: center;
gap: var(--spacing-sm);
}
.currentWeekBadge {
background: linear-gradient(to bottom, var(--color-iphone-blue), var(--color-iphone-blue-dark));
color: var(--color-white);
font-size: var(--font-size-xs);
padding: var(--spacing-xs) var(--spacing-sm);
border-radius: var(--border-radius-xl);
border: var(--border-width-thin) solid var(--color-iphone-blue-border);
font-weight: var(--font-weight-bold);
margin-left: var(--spacing-sm);
text-shadow: var(--text-shadow-dark);
box-shadow: var(--shadow-iphone-button);
}
.activitiesList {
display: flex;
flex-direction: column;
}
.activityCard {
padding: var(--spacing-lg) var(--spacing-xl);
border-bottom: var(--border-width-thin) solid var(--border-divider);
cursor: pointer;
transition: var(--transition-fast);
position: relative;
background: var(--bg-primary);
}
.activityCard:hover {
background: var(--bg-iphone-hover);
}
.activityCard:last-child {
border-bottom: none;
}
.activityCard.today {
background: var(--state-today-bg);
border-left: var(--border-width-thick) solid var(--state-today-border);
}
.activityCard.important {
border-left: var(--border-width-thick) solid var(--state-important-border);
background: var(--state-important-bg);
}
.activityCard.cancelled {
border-left: var(--border-width-thick) solid var(--state-cancelled-border);
background: var(--state-cancelled-bg);
opacity: 0.7;
}
.activityCard.cancelled .activityTitle {
text-decoration: line-through;
color: var(--state-cancelled-text);
}
.activityCard.cancelled .activityTitle {
background: var(--color-gray-200) !important;
color: var(--state-cancelled-text) !important;
}
.activityHeader {
display: flex;
align-items: flex-start;
gap: 1rem;
width: 100%;
}
.activityMeta {
min-width: 80px;
flex-shrink: 0;
}
.activityDate {
font-size: 0.875rem;
font-weight: 600;
color: #374151;
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.25rem;
}
.todayBadge {
background: #059669;
color: white;
font-size: 0.7rem;
padding: 0.15rem 0.4rem;
border-radius: 4px;
font-weight: 500;
}
.activityTime {
font-size: var(--font-size-sm);
color: var(--text-secondary);
font-weight: var(--font-weight-medium);
}
.activityMain {
flex: 1;
min-width: 0;
}
.activityTitle {
font-size: var(--font-size-xl);
font-weight: var(--font-weight-semibold);
color: var(--color-gray-900);
margin: 0 0 var(--spacing-xs) 0;
line-height: var(--line-height-normal);
}
.activityLocation {
font-size: var(--font-size-base);
color: var(--text-secondary);
margin: var(--spacing-xs) 0 0 0;
}
.expandIcon {
font-size: 1.2rem;
color: var(--color-gray-400);
font-weight: var(--font-weight-semibold);
flex-shrink: 0;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
}
.activityDetails {
margin-top: var(--spacing-lg);
padding-top: var(--spacing-lg);
border-top: var(--border-width-thin) solid var(--color-gray-200);
margin-left: 92px;
}
.activityDescription {
font-size: var(--font-size-md);
color: var(--color-gray-600);
line-height: var(--line-height-loose);
margin: 0 0 var(--spacing-md) 0;
}
.activityTeacher {
font-size: var(--font-size-base);
color: var(--text-secondary);
margin: 0;
}
.courseInfoCard {
padding: var(--spacing-xl);
}
.courseInfoCard p {
margin: 0 0 var(--spacing-md) 0;
font-size: var(--font-size-md);
color: var(--color-gray-600);
}
.courseInfoCard p:last-child {
margin-bottom: 0;
}
/* Activity type colors for titles */
.lecture .activityTitle {
background: var(--activity-lecture-bg);
color: var(--activity-lecture-text);
padding: var(--spacing-xs) var(--spacing-md);
border-radius: var(--border-radius-md);
width: fit-content;
font-size: var(--font-size-md);
}
.lesson .activityTitle {
background: var(--activity-lesson-bg);
color: var(--activity-lesson-text);
padding: var(--spacing-xs) var(--spacing-md);
border-radius: var(--border-radius-md);
width: fit-content;
font-size: var(--font-size-md);
}
.guidance .activityTitle {
background: var(--activity-guidance-bg);
color: var(--activity-guidance-text);
padding: var(--spacing-xs) var(--spacing-md);
border-radius: var(--border-radius-md);
width: fit-content;
font-size: var(--font-size-md);
}
.presentation .activityTitle {
background: var(--activity-presentation-bg);
color: var(--activity-presentation-text);
padding: var(--spacing-xs) var(--spacing-md);
border-radius: var(--border-radius-md);
width: fit-content;
font-size: var(--font-size-md);
}
.introduction .activityTitle {
background: var(--activity-info-bg);
color: var(--activity-info-text);
padding: var(--spacing-xs) var(--spacing-md);
border-radius: var(--border-radius-md);
width: fit-content;
font-size: var(--font-size-md);
}
.exam .activityTitle {
background: var(--activity-exam-bg);
color: var(--activity-exam-text);
padding: var(--spacing-xs) var(--spacing-md);
border-radius: var(--border-radius-md);
width: fit-content;
font-size: var(--font-size-md);
}
.project .activityTitle {
background: var(--activity-project-bg);
color: var(--activity-project-text);
padding: var(--spacing-xs) var(--spacing-md);
border-radius: var(--border-radius-md);
width: fit-content;
font-size: var(--font-size-md);
}
.info .activityTitle {
background: var(--activity-info-bg);
color: var(--activity-info-text);
padding: var(--spacing-xs) var(--spacing-md);
border-radius: var(--border-radius-md);
width: fit-content;
font-size: var(--font-size-md);
}
.laboration .activityTitle {
background: var(--activity-laboration-bg);
color: var(--activity-laboration-text);
padding: var(--spacing-xs) var(--spacing-md);
border-radius: var(--border-radius-md);
width: fit-content;
font-size: var(--font-size-md);
}
.workshop .activityTitle {
background: var(--activity-workshop-bg);
color: var(--activity-workshop-text);
padding: var(--spacing-xs) var(--spacing-md);
border-radius: var(--border-radius-md);
width: fit-content;
font-size: var(--font-size-md);
}
.seminar .activityTitle {
background: var(--activity-seminar-bg);
color: var(--activity-seminar-text);
padding: var(--spacing-xs) var(--spacing-md);
border-radius: var(--border-radius-md);
width: fit-content;
font-size: var(--font-size-md);
}
.default .activityTitle {
background: var(--activity-default-bg);
color: var(--activity-default-text);
padding: var(--spacing-xs) var(--spacing-md);
border-radius: var(--border-radius-md);
width: fit-content;
font-size: var(--font-size-md);
}
/* Responsive design */
@media (max-width: 640px) {
.pageContainer {
padding: 0.75rem;
}
.activityHeader {
gap: 0.75rem;
}
.activityDetails {
margin-left: 0;
margin-top: 0.75rem;
padding-top: 0.75rem;
}
.activityMeta {
min-width: 70px;
}
.courseTitle {
font-size: 1.5rem;
}
.header {
padding: 1.25rem 1rem;
}
}
@media (min-width: 768px) {
.pageContainer {
max-width: 768px;
margin: 0 auto;
padding: 2rem;
}
.content {
gap: 2rem;
}
.header {
padding: 2rem;
}
.courseTitle {
font-size: 2rem;
}
}

View File

@ -0,0 +1,232 @@
import React, { useState, useEffect } from 'react';
import { useParams, Link } from 'react-router-dom';
import styles from './CourseSchedule.module.css';
import { coursesData } from '../data/coursesData';
export function CourseScheduleView() {
const { courseId } = useParams();
const [courseData, setCourseData] = useState(null);
const [scheduleData, setScheduleData] = useState([]);
const [examData, setExamData] = useState([]);
const [courseInfo, setCourseInfo] = useState({});
const [expandedActivity, setExpandedActivity] = useState(null);
const [currentWeek, setCurrentWeek] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const loadCourseData = async () => {
const course = coursesData.find(c => c.id === courseId);
if (!course) {
setLoading(false);
return;
}
setCourseData(course);
try {
const dataModule = await course.dataModule();
setScheduleData(dataModule.scheduleData);
setExamData(dataModule.examData);
setCourseInfo(dataModule.courseInfo);
} catch (error) {
console.error('Failed to load course data:', error);
}
setLoading(false);
};
loadCourseData();
// Get current week number (simplified)
const now = new Date();
const week = Math.ceil(((now - new Date(now.getFullYear(), 0, 1)) / 86400000 + new Date(now.getFullYear(), 0, 1).getDay() + 1) / 7);
setCurrentWeek(week);
}, [courseId]);
const toggleActivity = (activityId) => {
setExpandedActivity(expandedActivity === activityId ? null : activityId);
};
const getActivityTypeClass = (type) => {
// Only apply color styling to clear activity categories
switch (type) {
case 'Föreläsning': return styles.lecture;
case 'Lektion': return styles.lesson;
case 'Handledning': return styles.guidance;
case 'Redovisning': return styles.presentation;
case 'Laboration': return styles.laboration;
case 'Tentamen': return styles.exam;
case 'Projektarbete': return styles.project;
case 'Workshop': return styles.workshop;
case 'Seminarium': return styles.seminar;
// Leave unclear/mixed categories without color styling
case 'Introduktion':
case 'Info':
case 'Inlämningsuppgifter':
default:
return ''; // No color styling
}
};
const formatDate = (dateString) => {
const date = new Date(dateString);
const today = new Date();
const isToday = date.toDateString() === today.toDateString();
const options = {
weekday: 'short',
month: 'short',
day: 'numeric'
};
return {
formatted: date.toLocaleDateString('sv-SE', options),
isToday
};
};
const ActivityCard = ({ activity, isExam = false, hideDayInfo = false }) => {
const { formatted: formattedDate, isToday } = formatDate(activity.date);
const isExpanded = expandedActivity === activity.id;
return (
<div
className={`${styles.activityCard} ${getActivityTypeClass(activity.type)} ${!hideDayInfo && isToday ? styles.today : ''} ${activity.important ? styles.important : ''} ${activity.cancelled ? styles.cancelled : ''}`}
onClick={() => toggleActivity(activity.id)}
>
<div className={styles.activityHeader}>
<div className={styles.activityMeta}>
{!hideDayInfo && (
<div className={styles.activityDate}>
{formattedDate}
{isToday && <span className={styles.todayBadge}>Idag</span>}
</div>
)}
<div className={styles.activityTime}>{activity.time}</div>
</div>
<div className={styles.activityMain}>
<h3 className={`${styles.activityTitle} ${getActivityTypeClass(activity.type)}`}>{activity.title}</h3>
{activity.location && (
<div className={styles.activityLocation}>📍 {activity.location}</div>
)}
</div>
<div className={styles.expandIcon}>
{isExpanded ? '' : '+'}
</div>
</div>
{isExpanded && (
<div className={styles.activityDetails}>
<p className={styles.activityDescription}>{activity.description}</p>
{activity.teacher && (
<div className={styles.activityTeacher}>
<strong>Lärare:</strong> {activity.teacher}
</div>
)}
</div>
)}
</div>
);
};
if (loading) {
return <div className={styles.pageContainer}>Loading...</div>;
}
if (!courseData) {
return (
<div className={styles.pageContainer}>
<div className={styles.notFound}>
<h1>Course not found</h1>
<Link to="/course-schedule"> Back to courses</Link>
</div>
</div>
);
}
return (
<div className={styles.pageContainer}>
<div className={styles.header}>
<div className={styles.backLink}>
<Link to="/course-schedule"> Tillbaka till kurser</Link>
</div>
<h1 className={styles.courseTitle}>{courseInfo.title}</h1>
<p className={styles.courseInfo}>{courseInfo.code} {courseInfo.credits}</p>
</div>
<div className={styles.content}>
{/* Upcoming Exams */}
{examData.length > 0 && (
<section className={styles.section}>
<h2 className={styles.sectionTitle}>📅 Examinationer</h2>
<div className={styles.activitiesList}>
{examData.map((exam) => (
<ActivityCard key={exam.id} activity={exam} isExam={true} />
))}
</div>
</section>
)}
{/* Schedule by weeks */}
{scheduleData.map((weekData) => {
// Group activities by date for day dividers
const activitiesByDate = weekData.activities.reduce((acc, activity) => {
const dateKey = activity.date;
if (!acc[dateKey]) {
acc[dateKey] = [];
}
acc[dateKey].push(activity);
return acc;
}, {});
// Sort dates chronologically
const sortedDates = Object.keys(activitiesByDate).sort((a, b) => new Date(a) - new Date(b));
return (
<section key={weekData.week} className={styles.section}>
<h2 className={styles.sectionTitle}>
Vecka {weekData.week}
{currentWeek === weekData.week && <span className={styles.currentWeekBadge}>Aktuell</span>}
</h2>
<div className={styles.activitiesList}>
{sortedDates.map((date, dateIndex) => {
const { formatted: formattedDate, isToday } = formatDate(date);
const dayActivities = activitiesByDate[date];
return (
<div key={date}>
<div className={styles.dayDivider}>
<span className={styles.dayDividerText}>
{formattedDate}
{isToday && <span className={styles.todayIndicator}> Idag</span>}
</span>
</div>
{dayActivities.map((activity) => (
<ActivityCard key={activity.id} activity={activity} hideDayInfo={true} />
))}
</div>
);
})}
</div>
</section>
);
})}
{/* Course Info */}
<section className={styles.section}>
<h2 className={styles.sectionTitle}> Kursinformation</h2>
<div className={styles.courseInfoCard}>
<p><strong>Kursansvarig:</strong> {courseInfo.coordinator}</p>
<p><strong>Omfattning:</strong> {courseInfo.credits}</p>
{courseInfo.examCredits && (
<p><strong>Tentamen:</strong> {courseInfo.examCredits}</p>
)}
{courseInfo.projectCredits && (
<p><strong>Projektarbete:</strong> {courseInfo.projectCredits}</p>
)}
</div>
</section>
</div>
</div>
);
}

View File

@ -1,6 +1,8 @@
.pageContainer {
padding: 1rem;
background-color: white;
padding: var(--container-padding);
background-color: var(--bg-tertiary);
color: var(--text-primary);
min-height: 100vh;
}
.formContainer {
@ -20,7 +22,7 @@
margin-top: 2rem;
padding: 2rem;
border-radius: 0.3rem;
outline: 1px solid #E7E7E7;
outline: 1px solid var(--border-light);
display: flex;
flex-direction: column;
align-items: center;
@ -38,8 +40,8 @@
padding: 0;
z-index: 10;
padding: 1rem;
border-top: 1px solid rgb(214, 214, 214);
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
border-top: 1px solid var(--border-medium);
box-shadow: var(--modal-footer-shadow);
}
.cancelButton {
@ -56,7 +58,7 @@
.inactiveButton {
color: #D4D4D4;
color: var(--text-disabled);
}
@ -95,9 +97,9 @@
font-size: 1.2rem;
padding: 1rem 4rem;
/*border: 1px solid #D4D4D4;*/
background-color: #2B4EF5;
background-color: var(--color-primary);
box-sizing: border-box;
color: white;
color: var(--color-white);
font-weight: bolder;
}
@ -109,7 +111,7 @@
bottom: 16px;
right: 16px;
z-index: 1000;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
box-shadow: var(--button-mobile-shadow);
cursor: pointer;
}
.bookingSummaryRight {
@ -118,17 +120,17 @@
bottom: 16px;
left: 16px;
z-index: 1000;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
box-shadow: var(--button-mobile-shadow);
height: fit-content;
background-color: white;
background-color: var(--bg-primary);
padding: 1rem;
font-size: 0.5rem;
}
}
.inactiveButton {
background-color: rgb(220, 220, 220);
color: #8f8f8f;
background-color: var(--button-disabled-bg);
color: var(--button-disabled-text);
}
.bookingSummary p {
@ -139,7 +141,7 @@
.line {
height: 1px;
background-color: rgb(216, 216, 216);
background-color: var(--divider-color);
width: 100%;
margin: 1rem 0;
}
@ -158,8 +160,8 @@
.filterButton {
width: fit-content;
background: white;
border: 1px solid #D1D5DB;
background: var(--filter-button-bg);
border: 1px solid var(--filter-button-border);
border-radius: 0.375rem;
padding: 0.5rem 0.75rem;
display: flex;
@ -169,27 +171,27 @@
transition: all 0.2s ease;
font-size: 0.875rem;
font-weight: 500;
color: #374151;
color: var(--filter-button-text);
}
.filterButton:hover {
background: #F9FAFB;
border-color: #9CA3AF;
background: var(--filter-button-hover-bg);
border-color: var(--filter-button-hover-border);
}
.filterButton:active {
background: #F3F4F6;
background: var(--filter-button-active-bg);
}
.activeFilter {
background: #EBF8FF !important;
border-color: #3B82F6 !important;
color: #1E40AF !important;
background: var(--filter-active-bg) !important;
border-color: var(--filter-active-border) !important;
color: var(--filter-active-text) !important;
}
.activeFilter:hover {
background: #DBEAFE !important;
border-color: #2563EB !important;
background: var(--filter-active-hover-bg) !important;
border-color: var(--filter-active-hover-border) !important;
}
.filterIcon {
@ -198,7 +200,7 @@
.chevron {
font-size: 0.75rem;
color: #6B7280;
color: var(--filter-chevron-color);
transition: transform 0.2s ease;
}
@ -214,8 +216,8 @@
width: fit-content;
max-width: 600px;
padding: 1rem;
background: #F9FAFB;
border: 1px solid #E5E7EB;
background: var(--filter-content-bg);
border: 1px solid var(--filter-content-border);
border-radius: 0.5rem;
animation: slideDown 0.2s ease-out;
}
@ -230,15 +232,15 @@
.resetSection {
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid #E5E7EB;
border-top: 1px solid var(--filter-reset-divider);
display: flex;
justify-content: center;
}
.resetButton {
background: white;
border: 1px solid #DC2626;
color: #DC2626;
background: var(--reset-button-bg);
border: 1px solid var(--reset-button-border);
color: var(--reset-button-text);
border-radius: 0.375rem;
padding: 0.5rem 1rem;
font-size: 0.875rem;
@ -248,13 +250,13 @@
}
.resetButton:hover {
background: #FEF2F2;
border-color: #B91C1C;
color: #B91C1C;
background: var(--reset-button-hover-bg);
border-color: var(--reset-button-hover-border);
color: var(--reset-button-hover-text);
}
.resetButton:active {
background: #FEE2E2;
background: var(--reset-button-active-bg);
transform: translateY(1px);
}

View File

@ -50,7 +50,7 @@ export function RoomBooking({ bookings, showSuccessBanner, lastCreatedBooking, o
<hr className={styles.sectionDivider} />
<h2 className={styles.sectionHeading}>Ny bokning</h2>
<Link to='/new-booking'>
<Card imageUrl="/grupprum.jpg" header="Litet grupprum" subheader="Plats för 5 personer" />
<Card imageUrl="./grupprum.jpg" header="Litet grupprum" subheader="Plats för 5 personer" />
</Link>
</div>
);

View File

@ -1,24 +1,28 @@
.pageContainer {
color: rgb(51, 51, 51);
padding: 1rem;
color: var(--text-primary);
background-color: var(--bg-tertiary);
padding: var(--container-padding);
min-height: 100vh;
}
.pageHeading {
font-size: 2.6rem;
font-weight: 300;
font-family: 'The Sans', system-ui, sans-serif;
font-size: var(--font-size-6xl);
font-weight: var(--font-weight-light);
font-family: var(--font-primary);
color: var(--text-primary);
}
.sectionHeading {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 1rem;
font-size: var(--font-size-4xl);
font-weight: var(--font-weight-semibold);
margin-bottom: var(--spacing-lg);
color: var(--text-primary);
}
.sectionDivider {
border: none;
border-top: 1px solid #dedede;
margin-bottom: 2rem;
border-top: var(--border-width-thin) solid var(--border-medium);
margin-bottom: var(--spacing-3xl);
}
.welcomeSection {
@ -26,10 +30,10 @@
align-items: center;
justify-content: space-between;
background: #3d50a8;
padding: 2rem 2.5rem;
margin: 1.5rem 0 2.5rem 0;
color: white;
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.15);
padding: var(--spacing-3xl) var(--spacing-4xl);
margin: var(--spacing-2xl) 0 var(--spacing-4xl) 0;
color: var(--color-white);
box-shadow: var(--shadow-2xl);
position: relative;
overflow: hidden;
}
@ -52,24 +56,24 @@
.welcomeTitle {
font-size: 1.8rem;
font-weight: 700;
font-weight: var(--font-weight-bold);
margin: 0;
font-family: 'The Sans', system-ui, sans-serif;
font-family: var(--font-primary);
letter-spacing: -0.5px;
}
.welcomeSubtitle {
font-size: 1rem;
margin: 0.5rem 0 0 0;
font-size: var(--font-size-xl);
margin: var(--spacing-sm) 0 0 0;
opacity: 0.9;
font-weight: 400;
font-weight: var(--font-weight-normal);
}
.welcomeIcon {
width: 48px;
height: 48px;
opacity: 0.8;
color: white;
color: var(--color-white);
}
.welcomeIcon svg {

View File

@ -44,7 +44,7 @@
}
&:hover:not([data-selected]):not([data-disabled]):not([data-unavailable]) {
background-color: #e9e9e9;
background-color: var(--highlight-hover);
}
&[data-pressed] {
@ -71,8 +71,7 @@
.react-aria-CalendarCell {
&[data-unavailable] {
text-decoration: line-through;
color: var(--invalid-color);
color: rgb(203, 203, 203);
color: var(--text-color-disabled);
}
}

View File

@ -31,23 +31,26 @@
align-items: center;
justify-content: center;
border-radius: 4px;
transition: background-color 0.2s, opacity 0.2s;
transition: background-color 0.2s, opacity 0.2s, color 0.2s;
color: var(--chevron-button-color);
}
.chevron-button:hover:not(:disabled) {
background-color: rgba(0, 0, 0, 0.05);
background-color: var(--highlight-hover);
}
.chevron-button:active:not(:disabled) {
background-color: rgba(0, 0, 0, 0.1);
background-color: var(--highlight-pressed);
}
.chevron-button:disabled {
cursor: default;
color: var(--chevron-button-disabled-color);
opacity: 0.4;
}
.chevron-button:focus-visible {
outline: 2px solid #2563EB;
outline: 2px solid var(--focus-ring-color);
outline-offset: 2px;
}
@ -58,7 +61,7 @@
forced-color-adjust: none;
border-radius: 4px;
/*border: none;*/
border: 1px solid #D4D4D4;
border: 1px solid var(--border-color);
/*width: 1.429rem;*/
/*height: 1.429rem;*/
width: fit-content;
@ -69,7 +72,7 @@
&[data-pressed] {
box-shadow: none;
/*background: var(--highlight-background);*/
background: #f1f1f1;
background: var(--button-background-pressed);
}
&[data-focus-visible] {
@ -85,12 +88,12 @@
justify-content: space-between !important;
gap: 0.75rem !important;
cursor: pointer !important;
background: white !important;
border: 1px solid #E5E7EB !important;
background: var(--field-background) !important;
border: 1px solid var(--border-color) !important;
border-radius: 8px !important;
padding: 12px 16px !important;
font-weight: 500 !important;
color: #374151 !important;
color: var(--field-text-color) !important;
transition: all 0.2s ease !important;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) !important;
white-space: nowrap !important;
@ -103,21 +106,21 @@
}
.calendar-button:hover {
border-color: #D1D5DB !important;
border-color: var(--border-color-hover) !important;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) !important;
}
.calendar-button[data-pressed] {
background: #F9FAFB !important;
border-color: #9CA3AF !important;
background: var(--button-background-pressed) !important;
border-color: var(--border-color-pressed) !important;
transform: translateY(1px) !important;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) !important;
}
.calendar-button[data-focus-visible] {
outline: 2px solid #2563EB !important;
outline: 2px solid var(--focus-ring-color) !important;
outline-offset: 2px !important;
border-color: #2563EB !important;
border-color: var(--focus-ring-color) !important;
}
.calendar-date {

View File

@ -71,7 +71,6 @@ export function DatePicker<T extends DateValue>(
disabled={!canNavigatePrevious}
>
<ChevronLeft
color={!canNavigatePrevious ? "#666" : "#111"}
disabled={!canNavigatePrevious}
/>
</button>
@ -85,7 +84,6 @@ export function DatePicker<T extends DateValue>(
disabled={!canNavigateNext}
>
<ChevronRight
color={!canNavigateNext ? "#666" : "#111"}
disabled={!canNavigateNext}
/>
</button>

View File

@ -61,6 +61,32 @@
}
}
/* Dark mode support for data-theme attribute */
[data-theme="dark"] {
--background-color: #1d1d1d;
--gray-50: #101010;
--gray-100: #393939;
--gray-200: #4f4f4f;
--gray-300: #CECECE;
--gray-400: #848484;
--gray-500: #a7a7a7;
--gray-600: #cfcfcf;
--purple-100: #3c1e95;
--purple-200: #522acd;
--purple-300: #6f46ed;
--purple-400: #8e6ef1;
--purple-500: #b099f5;
--purple-600: #d5c8fa;
--red-100: #721200;
--red-200: #9c1900;
--red-300: #cc2000;
--red-400: #e95034;
--red-500: #f08c79;
--red-600: #f7c3ba;
--highlight-hover: rgb(255 255 255 / 0.1);
--highlight-pressed: rgb(255 255 255 / 0.2);
}
/* Semantic colors */
:root {
--focus-ring-color: var(--purple-400);

View File

@ -0,0 +1,616 @@
:root {
/* === COLORS === */
/* Primary Colors */
--color-primary: #007AFF;
--color-primary-hover: #0056b3;
--color-primary-light: rgba(0, 122, 255, 0.12);
/* iPhone/iPod Style Colors */
--color-iphone-blue: #4d90fe;
--color-iphone-blue-dark: #3e7ce0;
--color-iphone-blue-border: #2d5aa0;
/* Grayscale */
--color-black: #000;
--color-gray-900: #111827;
--color-gray-800: #1f2937;
--color-gray-700: #374151;
--color-gray-600: #4b5563;
--color-gray-500: #6b7280;
--color-gray-400: #9ca3af;
--color-gray-300: #d1d5db;
--color-gray-200: #e5e7eb;
--color-gray-100: #f3f4f6;
--color-gray-50: #f9fafb;
--color-white: #fff;
/* Text Colors */
--text-primary: #333;
--text-secondary: #6b7280;
--text-tertiary: #9ca3af;
--text-muted: #999;
/* Background Colors */
--bg-primary: #fff;
--bg-secondary: #f8f8f8;
--bg-tertiary: #fafafa;
--bg-muted: #f0f0f0;
/* iPhone/iPod Style Backgrounds */
--bg-iphone-gradient: linear-gradient(to bottom, #c5ccd4, #92a5b8);
--bg-iphone-header: linear-gradient(to bottom, #ffffff, #e6e6e6);
--bg-iphone-section: linear-gradient(to bottom, #f2f2f2, #d9d9d9);
--bg-iphone-day-divider: linear-gradient(to bottom, #e6e6e6, #d9d9d9);
--bg-iphone-hover: linear-gradient(to bottom, #e8f0ff, #d0e2ff);
/* Activity Type Colors */
--activity-lecture-bg: #dbeafe;
--activity-lecture-text: #1d4ed8;
--activity-lesson-bg: #dcfce7;
--activity-lesson-text: #166534;
--activity-guidance-bg: #fef3c7;
--activity-guidance-text: #b45309;
--activity-presentation-bg: #f3e8ff;
--activity-presentation-text: #7c3aed;
--activity-exam-bg: #fee2e2;
--activity-exam-text: #dc2626;
--activity-project-bg: #f0fdf4;
--activity-project-text: #15803d;
--activity-info-bg: #fef3c7;
--activity-info-text: #b45309;
--activity-laboration-bg: #ecfdf5;
--activity-laboration-text: #047857;
--activity-workshop-bg: #fef2f2;
--activity-workshop-text: #dc2626;
--activity-seminar-bg: #f0f4ff;
--activity-seminar-text: #3b82f6;
--activity-default-bg: #f1f5f9;
--activity-default-text: #475569;
/* Border Colors */
--border-light: #E5E5E5;
--border-medium: #dedede;
--border-dark: #999;
--border-divider: #d9d9d9;
/* === SPACING === */
--spacing-xs: 0.25rem; /* 4px */
--spacing-sm: 0.5rem; /* 8px */
--spacing-md: 0.75rem; /* 12px */
--spacing-lg: 1rem; /* 16px */
--spacing-xl: 1.25rem; /* 20px */
--spacing-2xl: 1.5rem; /* 24px */
--spacing-3xl: 2rem; /* 32px */
--spacing-4xl: 2.5rem; /* 40px */
--spacing-5xl: 3rem; /* 48px */
/* === TYPOGRAPHY === */
/* Font Family */
--font-primary: 'The Sans', system-ui, sans-serif;
--font-secondary: system-ui, -apple-system, sans-serif;
/* Font Weights */
--font-weight-light: 300;
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
--font-weight-extrabold: 800;
/* Font Sizes */
--font-size-xs: 0.7rem; /* 11.2px */
--font-size-sm: 0.8rem; /* 12.8px */
--font-size-base: 0.85rem; /* 13.6px */
--font-size-md: 0.9rem; /* 14.4px */
--font-size-lg: 0.95rem; /* 15.2px */
--font-size-xl: 1rem; /* 16px */
--font-size-2xl: 1.1rem; /* 17.6px */
--font-size-3xl: 1.25rem; /* 20px */
--font-size-4xl: 1.5rem; /* 24px */
--font-size-5xl: 1.75rem; /* 28px */
--font-size-6xl: 2.6rem; /* 41.6px */
/* Line Heights */
--line-height-tight: 1.1;
--line-height-normal: 1.3;
--line-height-relaxed: 1.4;
--line-height-loose: 1.5;
/* === BORDERS === */
--border-radius-sm: 0.25rem; /* 4px */
--border-radius-md: 0.5rem; /* 8px */
--border-radius-lg: 0.75rem; /* 12px */
--border-radius-xl: 10px; /* iPhone style */
--border-radius-2xl: 12px; /* Modern style */
--border-width-thin: 1px;
--border-width-medium: 2px;
--border-width-thick: 4px;
/* === SHADOWS === */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-md: 0 2px 4px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 2px 8px rgba(0, 0, 0, 0.06);
--shadow-xl: 0 4px 16px rgba(0, 0, 0, 0.12);
--shadow-2xl: 0 10px 30px rgba(102, 126, 234, 0.15);
/* iPhone/iPod Style Shadows */
--shadow-iphone-inset: inset 0 1px 0 rgba(255, 255, 255, 0.8);
--shadow-iphone-outer: 0 1px 3px rgba(0, 0, 0, 0.3);
--shadow-iphone-button: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.3);
--shadow-iphone-day-divider: inset 0 1px 0 rgba(255, 255, 255, 0.5);
/* Text Shadows */
--text-shadow-light: 0 1px 0 rgba(255, 255, 255, 0.8);
--text-shadow-dark: 0 -1px 0 rgba(0, 0, 0, 0.25);
/* === TRANSITIONS === */
--transition-fast: 0.15s ease;
--transition-medium: 0.2s ease;
--transition-slow: 0.3s ease;
--transition-transform: transform 0.2s ease, box-shadow 0.2s ease;
/* === Z-INDEX === */
--z-dropdown: 10;
--z-sticky: 100;
--z-header: 1000;
--z-modal: 9999;
/* === LAYOUT === */
--container-padding: var(--spacing-lg);
--page-max-width: 100%;
--header-height: 60px;
/* === SPECIFIC COMPONENT SIZES === */
--card-padding: var(--spacing-xl);
--button-padding-y: var(--spacing-sm);
--button-padding-x: var(--spacing-lg);
--input-padding: var(--spacing-md);
/* Color indicator sizes */
--color-indicator-width: 4px;
--color-indicator-height: 60px;
/* Special states */
--state-today-bg: linear-gradient(135deg, #fef3c7, #fde68a);
--state-today-border: #f59e0b;
--state-important-bg: linear-gradient(135deg, #fef2f2, #fee2e2);
--state-important-border: #ef4444;
--state-cancelled-bg: linear-gradient(135deg, #f9fafb, #f3f4f6);
--state-cancelled-border: #6b7280;
--state-cancelled-text: #6b7280;
/* Room category colors */
--room-green-bg: #D4EDDA;
--room-green-text: #155724;
--room-red-bg: #F8D7DA;
--room-red-text: #721C24;
--room-blue-bg: #D1ECF1;
--room-blue-text: #0C5460;
--room-yellow-bg: #FFF3CD;
--room-yellow-text: #856404;
/* Notification colors */
--notification-success-bg: #E8F5E8;
--notification-success-border: #4CAF50;
--notification-success-icon-bg: #4CAF50;
--notification-success-icon-text: white;
--notification-success-title: #2E7D32;
--notification-success-details: #388E3C;
--notification-error-bg: #FFF4F4;
--notification-error-border: #F87171;
--notification-error-icon-bg: #EF4444;
--notification-error-icon-text: white;
--notification-error-title: #DC2626;
--notification-error-details: #EF4444;
--notification-warning-bg: #FFF8E1;
--notification-warning-border: #FFB74D;
--notification-warning-icon: #FF9800;
--notification-close-button: #6C757D;
--notification-close-button-hover: #495057;
--notification-close-button-bg-hover: rgba(108, 117, 125, 0.1);
--tooltip-bg: #333;
--tooltip-text: white;
/* Button colors */
--button-bg: #f9f9f9;
--button-secondary-bg: #ffffff;
--button-secondary-text: #000;
--button-secondary-hover-bg: #f2f6ff;
--button-secondary-shadow: 0 2px 8px rgba(143, 143, 143, 0.2);
--button-secondary-hover-shadow: 0 4px 12px rgba(192, 192, 192, 0.3);
/* Input colors */
--input-bg: #FAFBFC;
--input-text: #333;
--input-border: #D2D9E0;
--input-placeholder: #adadad;
/* Chip colors */
--chip-bg: #F0F8FF;
--chip-text: #2563EB;
--chip-border: #D1E7FF;
--chip-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
--chip-hover-bg: #E0F2FE;
--chip-hover-border: #BAE6FD;
--chip-active-bg: #BFDBFE;
--chip-default-bg: #F3F4F6;
--chip-default-text: #374151;
--chip-default-border: #D1D5DB;
/* Dropdown colors */
--dropdown-bg: white;
--dropdown-border: #D2D9E0;
--dropdown-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.08);
--dropdown-divider: #F1F3F4;
--dropdown-hover-bg: #F8F9FA;
--dropdown-active-bg: #E8F0FE;
--dropdown-selected-bg: #F0F8FF;
--dropdown-selected-hover-bg: #E0F2FE;
/* TimeCard colors */
--timecard-bg: #F8FBFC;
--timecard-text: #333;
--timecard-border: #CECECE;
--timecard-hover-bg: #E5E5E5;
--timecard-active-bg: #D1D5DB;
--timecard-active-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
--timecard-selected-shadow: 0 2px 8px rgba(37, 99, 235, 0.3);
--timecard-muted-text: #919191;
--timecard-secondary-text: #686765;
--timecard-unavailable-bg: #E5E5E5;
--timecard-unavailable-text: #B1B1B1;
--timecard-unavailable-border: #CECECE;
/* Modal colors */
--modal-bg: white;
--modal-display-bg: #f8f9fa;
--modal-display-border: #e9ecef;
--modal-cancel-bg: white;
--modal-cancel-text: #374151;
--modal-cancel-border: #d1d5db;
--modal-cancel-hover-bg: #f9fafb;
--modal-cancel-hover-border: #9ca3af;
--modal-cancel-active-bg: #e5e7eb;
--modal-save-bg: #059669;
--modal-save-text: white;
--modal-save-border: #047857;
--modal-save-shadow: 0 2px 4px rgba(5, 150, 105, 0.2);
--modal-save-hover-bg: #047857;
--modal-save-hover-shadow: 0 4px 8px rgba(5, 150, 105, 0.3);
--modal-save-active-bg: #065f46;
--modal-save-active-shadow: 0 1px 2px rgba(5, 150, 105, 0.2);
/* Button disabled states */
--button-disabled-bg: #f8f9fa;
--button-disabled-text: #adb5bd;
--button-disabled-border: #dee2e6;
/* Additional color variants */
--color-primary-dark: #1d4ed8;
--color-white-transparent: rgba(255, 255, 255, 0.8);
--color-white-transparent-low: rgba(255, 255, 255, 0.2);
/* Header colors */
--header-brand-color: #002E5F;
/* Filter colors */
--filter-button-bg: white;
--filter-button-text: #374151;
--filter-button-border: #D1D5DB;
--filter-button-hover-bg: #F9FAFB;
--filter-button-hover-border: #9CA3AF;
--filter-button-active-bg: #F3F4F6;
--filter-active-bg: #EBF8FF;
--filter-active-text: #1E40AF;
--filter-active-border: #3B82F6;
--filter-active-hover-bg: #DBEAFE;
--filter-active-hover-border: #2563EB;
--filter-chevron-color: #6B7280;
--filter-content-bg: #F9FAFB;
--filter-content-border: #E5E7EB;
--filter-reset-divider: #E5E7EB;
/* Reset button colors */
--reset-button-bg: white;
--reset-button-text: #DC2626;
--reset-button-border: #DC2626;
--reset-button-hover-bg: #FEF2F2;
--reset-button-hover-text: #B91C1C;
--reset-button-hover-border: #B91C1C;
--reset-button-active-bg: #FEE2E2;
/* Additional UI colors */
--border-light: #E7E7E7;
--border-medium: #d6d6d6;
--text-disabled: #D4D4D4;
--divider-color: #d8d8d8;
--button-mobile-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
--modal-footer-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
/* Modal specific colors */
--modal-border: rgba(255, 255, 255, 0.18);
--modal-shadow: 0 32px 64px rgba(0, 0, 0, 0.12), 0 16px 32px rgba(0, 0, 0, 0.08), 0 8px 16px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.15);
--modal-dialog-bg: rgba(255, 255, 255, 0.95);
--modal-close-hover-bg: #f3f4f6;
/* Dropdown chevron */
--dropdown-chevron-color: #888;
/* Date picker chevron buttons */
--chevron-button-color: #111;
--chevron-button-disabled-color: #666;
}
/* === DARK MODE === */
[data-theme="dark"] {
/* Primary Colors - keep same for consistency */
--color-primary: #007AFF;
--color-primary-hover: #0056b3;
--color-primary-light: rgba(0, 122, 255, 0.2);
/* iPhone/iPod Style Colors - darker variants */
--color-iphone-blue: #4d90fe;
--color-iphone-blue-dark: #3e7ce0;
--color-iphone-blue-border: #2d5aa0;
/* Grayscale - inverted for dark mode */
--color-black: #fff;
--color-gray-900: #f9fafb;
--color-gray-800: #f3f4f6;
--color-gray-700: #e5e7eb;
--color-gray-600: #d1d5db;
--color-gray-500: #9ca3af;
--color-gray-400: #6b7280;
--color-gray-300: #4b5563;
--color-gray-200: #374151;
--color-gray-100: #1f2937;
--color-gray-50: #111827;
--color-white: #1a1a1a;
/* Text Colors */
--text-primary: #e5e5e5;
--text-secondary: #b0b0b0;
--text-tertiary: #888;
--text-muted: #666;
/* Background Colors */
--bg-primary: #1a1a1a;
--bg-secondary: #2a2a2a;
--bg-tertiary: #333;
--bg-muted: #3a3a3a;
/* iPhone/iPod Style Backgrounds - dark variants */
--bg-iphone-gradient: linear-gradient(to bottom, #2a2a2a, #1a1a1a);
--bg-iphone-header: linear-gradient(to bottom, #333, #2a2a2a);
--bg-iphone-section: linear-gradient(to bottom, #2a2a2a, #222);
--bg-iphone-day-divider: linear-gradient(to bottom, #333, #2a2a2a);
--bg-iphone-hover: linear-gradient(to bottom, #3a4a6a, #2a3a5a);
/* Activity Type Colors - darker backgrounds, brighter text */
--activity-lecture-bg: #1e3a8a;
--activity-lecture-text: #93c5fd;
--activity-lesson-bg: #166534;
--activity-lesson-text: #86efac;
--activity-guidance-bg: #92400e;
--activity-guidance-text: #fcd34d;
--activity-presentation-bg: #581c87;
--activity-presentation-text: #c4b5fd;
--activity-exam-bg: #991b1b;
--activity-exam-text: #fca5a5;
--activity-project-bg: #166534;
--activity-project-text: #86efac;
--activity-info-bg: #92400e;
--activity-info-text: #fcd34d;
--activity-laboration-bg: #047857;
--activity-laboration-text: #6ee7b7;
--activity-workshop-bg: #991b1b;
--activity-workshop-text: #fca5a5;
--activity-seminar-bg: #1d4ed8;
--activity-seminar-text: #93c5fd;
--activity-default-bg: #374151;
--activity-default-text: #d1d5db;
/* Border Colors */
--border-light: #404040;
--border-medium: #555;
--border-dark: #666;
--border-divider: #444;
/* Shadows - adjusted for dark mode */
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
--shadow-md: 0 2px 4px rgba(0, 0, 0, 0.4);
--shadow-lg: 0 2px 8px rgba(0, 0, 0, 0.5);
--shadow-xl: 0 4px 16px rgba(0, 0, 0, 0.6);
--shadow-2xl: 0 10px 30px rgba(0, 0, 0, 0.7);
/* iPhone/iPod Style Shadows - adjusted */
--shadow-iphone-inset: inset 0 1px 0 rgba(255, 255, 255, 0.1);
--shadow-iphone-outer: 0 1px 3px rgba(0, 0, 0, 0.7);
--shadow-iphone-button: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 2px rgba(0, 0, 0, 0.7);
--shadow-iphone-day-divider: inset 0 1px 0 rgba(255, 255, 255, 0.05);
/* Text Shadows */
--text-shadow-light: 0 1px 0 rgba(0, 0, 0, 0.5);
--text-shadow-dark: 0 -1px 0 rgba(255, 255, 255, 0.1);
/* Special states - dark mode */
--state-today-bg: linear-gradient(135deg, #92400e, #78350f);
--state-today-border: #f59e0b;
--state-important-bg: linear-gradient(135deg, #991b1b, #7f1d1d);
--state-important-border: #ef4444;
--state-cancelled-bg: linear-gradient(135deg, #374151, #1f2937);
--state-cancelled-border: #9ca3af;
--state-cancelled-text: #9ca3af;
/* Room category colors - dark mode */
--room-green-bg: #166534;
--room-green-text: #86efac;
--room-red-bg: #991b1b;
--room-red-text: #fca5a5;
--room-blue-bg: #1e40af;
--room-blue-text: #93c5fd;
--room-yellow-bg: #92400e;
--room-yellow-text: #fcd34d;
/* Notification colors - dark mode */
--notification-success-bg: #164e36;
--notification-success-border: #22c55e;
--notification-success-icon-bg: #22c55e;
--notification-success-icon-text: white;
--notification-success-title: #86efac;
--notification-success-details: #4ade80;
--notification-error-bg: #991b1b;
--notification-error-border: #f87171;
--notification-error-icon-bg: #ef4444;
--notification-error-icon-text: white;
--notification-error-title: #fca5a5;
--notification-error-details: #f87171;
--notification-warning-bg: #92400e;
--notification-warning-border: #f59e0b;
--notification-warning-icon: #fbbf24;
--notification-close-button: #9ca3af;
--notification-close-button-hover: #d1d5db;
--notification-close-button-bg-hover: rgba(156, 163, 175, 0.1);
--tooltip-bg: #1f2937;
--tooltip-text: #e5e7eb;
/* Button colors - dark mode */
--button-bg: #374151;
--button-secondary-bg: #374151;
--button-secondary-text: #e5e7eb;
--button-secondary-hover-bg: #4b5563;
--button-secondary-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
--button-secondary-hover-shadow: 0 4px 12px rgba(0, 0, 0, 0.6);
/* Input colors - dark mode */
--input-bg: #374151;
--input-text: #e5e7eb;
--input-border: #4b5563;
--input-placeholder: #9ca3af;
/* Chip colors - dark mode */
--chip-bg: #1e3a8a;
--chip-text: #93c5fd;
--chip-border: #3b82f6;
--chip-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
--chip-hover-bg: #1e40af;
--chip-hover-border: #60a5fa;
--chip-active-bg: #1d4ed8;
--chip-default-bg: #4b5563;
--chip-default-text: #d1d5db;
--chip-default-border: #6b7280;
/* Dropdown colors - dark mode */
--dropdown-bg: #374151;
--dropdown-border: #4b5563;
--dropdown-shadow: 0 4px 6px rgba(0, 0, 0, 0.3), 0 1px 3px rgba(0, 0, 0, 0.2);
--dropdown-divider: #4b5563;
--dropdown-hover-bg: #4b5563;
--dropdown-active-bg: #1e3a8a;
--dropdown-selected-bg: #1e3a8a;
--dropdown-selected-hover-bg: #1e40af;
/* TimeCard colors - dark mode */
--timecard-bg: #374151;
--timecard-text: #e5e7eb;
--timecard-border: #4b5563;
--timecard-hover-bg: #4b5563;
--timecard-active-bg: #6b7280;
--timecard-active-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
--timecard-selected-shadow: 0 2px 8px rgba(59, 130, 246, 0.5);
--timecard-muted-text: #9ca3af;
--timecard-secondary-text: #d1d5db;
--timecard-unavailable-bg: #4b5563;
--timecard-unavailable-text: #6b7280;
--timecard-unavailable-border: #6b7280;
/* Modal colors - dark mode */
--modal-bg: #1f2937;
--modal-display-bg: #374151;
--modal-display-border: #4b5563;
--modal-cancel-bg: #374151;
--modal-cancel-text: #d1d5db;
--modal-cancel-border: #4b5563;
--modal-cancel-hover-bg: #4b5563;
--modal-cancel-hover-border: #6b7280;
--modal-cancel-active-bg: #6b7280;
--modal-save-bg: #059669;
--modal-save-text: white;
--modal-save-border: #047857;
--modal-save-shadow: 0 2px 4px rgba(5, 150, 105, 0.3);
--modal-save-hover-bg: #047857;
--modal-save-hover-shadow: 0 4px 8px rgba(5, 150, 105, 0.4);
--modal-save-active-bg: #065f46;
--modal-save-active-shadow: 0 1px 2px rgba(5, 150, 105, 0.3);
/* Button disabled states - dark mode */
--button-disabled-bg: #374151;
--button-disabled-text: #6b7280;
--button-disabled-border: #4b5563;
/* Additional color variants - dark mode */
--color-primary-dark: #1d4ed8;
--color-white-transparent: rgba(255, 255, 255, 0.8);
--color-white-transparent-low: rgba(255, 255, 255, 0.2);
/* Header colors - dark mode */
--header-brand-color: #ffffff;
/* Filter colors - dark mode */
--filter-button-bg: #374151;
--filter-button-text: #d1d5db;
--filter-button-border: #4b5563;
--filter-button-hover-bg: #4b5563;
--filter-button-hover-border: #6b7280;
--filter-button-active-bg: #6b7280;
--filter-active-bg: #1e3a8a;
--filter-active-text: #93c5fd;
--filter-active-border: #3b82f6;
--filter-active-hover-bg: #1e40af;
--filter-active-hover-border: #60a5fa;
--filter-chevron-color: #9ca3af;
--filter-content-bg: #374151;
--filter-content-border: #4b5563;
--filter-reset-divider: #4b5563;
/* Reset button colors - dark mode */
--reset-button-bg: #374151;
--reset-button-text: #f87171;
--reset-button-border: #dc2626;
--reset-button-hover-bg: #450a0a;
--reset-button-hover-text: #fca5a5;
--reset-button-hover-border: #b91c1c;
--reset-button-active-bg: #7f1d1d;
/* Additional UI colors - dark mode */
--border-light: #4b5563;
--border-medium: #6b7280;
--text-disabled: #6b7280;
--divider-color: #4b5563;
--button-mobile-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
--modal-footer-shadow: 0 -2px 8px rgba(0, 0, 0, 0.3);
/* Modal specific colors - dark mode */
--modal-border: rgba(75, 85, 99, 0.4);
--modal-shadow: 0 32px 64px rgba(0, 0, 0, 0.4), 0 16px 32px rgba(0, 0, 0, 0.3), 0 8px 16px rgba(0, 0, 0, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.05);
--modal-dialog-bg: rgba(31, 41, 55, 0.95);
--modal-close-hover-bg: #4b5563;
/* Dropdown chevron - dark mode */
--dropdown-chevron-color: #9ca3af;
/* Date picker chevron buttons - dark mode */
--chevron-button-color: #e5e7eb;
--chevron-button-disabled-color: #6b7280;
}