mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 20:21:09 +02:00
✨ feat(a11y): add accessibility settings and theme improvements
Add comprehensive accessibility support across shared packages: - A11y store with contrast, colorblind mode, and reduce motion settings - A11yQuickToggles and A11ySettings UI components - PillNavigation and PillDropdown components in shared-ui - Calendar app updates to integrate new theme/a11y features 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
6cc9f70a4a
commit
02c82c7547
33 changed files with 1474 additions and 143 deletions
|
|
@ -19,7 +19,14 @@ export class CalendarController {
|
|||
|
||||
@Get()
|
||||
async findAll(@CurrentUser() user: CurrentUserData) {
|
||||
const calendars = await this.calendarService.findAll(user.userId);
|
||||
let calendars = await this.calendarService.findAll(user.userId);
|
||||
|
||||
// Lazy creation: if no calendars exist, create a default one
|
||||
if (calendars.length === 0) {
|
||||
const defaultCalendar = await this.calendarService.getOrCreateDefaultCalendar(user.userId);
|
||||
calendars = [defaultCalendar];
|
||||
}
|
||||
|
||||
return { calendars };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ export class CalendarService {
|
|||
|
||||
// Create a new default calendar
|
||||
return this.create(userId, {
|
||||
name: 'My Calendar',
|
||||
name: 'Mein Kalender',
|
||||
isDefault: true,
|
||||
color: '#3B82F6',
|
||||
});
|
||||
|
|
|
|||
|
|
@ -40,18 +40,18 @@
|
|||
/* Hour slot in day/week view */
|
||||
.hour-slot {
|
||||
height: var(--hour-height);
|
||||
border-bottom: 1px solid hsl(var(--border) / 0.5);
|
||||
border-bottom: 1px solid hsl(var(--color-border) / 0.5);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.hour-slot:hover {
|
||||
background-color: hsl(var(--muted) / 0.3);
|
||||
background-color: hsl(var(--color-muted) / 0.3);
|
||||
}
|
||||
|
||||
/* Event card in calendar */
|
||||
.event-card {
|
||||
background-color: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
background-color: hsl(var(--color-primary));
|
||||
color: hsl(var(--color-primary-foreground));
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 2px 6px;
|
||||
font-size: 0.75rem;
|
||||
|
|
@ -68,17 +68,17 @@
|
|||
/* Day cell in month view */
|
||||
.day-cell {
|
||||
min-height: 100px;
|
||||
border: 1px solid hsl(var(--border));
|
||||
border: 1px solid hsl(var(--color-border));
|
||||
padding: var(--spacing-xs);
|
||||
transition: background-color var(--transition-fast);
|
||||
}
|
||||
|
||||
.day-cell:hover {
|
||||
background-color: hsl(var(--muted) / 0.3);
|
||||
background-color: hsl(var(--color-muted) / 0.3);
|
||||
}
|
||||
|
||||
.day-cell.today {
|
||||
background-color: hsl(var(--primary) / 0.1);
|
||||
background-color: hsl(var(--color-primary) / 0.1);
|
||||
}
|
||||
|
||||
.day-cell.other-month {
|
||||
|
|
@ -91,7 +91,7 @@
|
|||
left: 0;
|
||||
right: 0;
|
||||
height: 2px;
|
||||
background-color: hsl(var(--destructive));
|
||||
background-color: hsl(var(--color-error));
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
|
|
@ -103,7 +103,7 @@
|
|||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background-color: hsl(var(--destructive));
|
||||
background-color: hsl(var(--color-error));
|
||||
}
|
||||
|
||||
/* Mini calendar */
|
||||
|
|
@ -123,24 +123,24 @@
|
|||
}
|
||||
|
||||
.mini-calendar .day:hover {
|
||||
background-color: hsl(var(--muted));
|
||||
background-color: hsl(var(--color-muted));
|
||||
}
|
||||
|
||||
.mini-calendar .day.today {
|
||||
background-color: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
background-color: hsl(var(--color-primary));
|
||||
color: hsl(var(--color-primary-foreground));
|
||||
}
|
||||
|
||||
.mini-calendar .day.selected {
|
||||
border: 2px solid hsl(var(--primary));
|
||||
border: 2px solid hsl(var(--color-primary));
|
||||
}
|
||||
|
||||
/* Card styles */
|
||||
.card {
|
||||
background-color: hsl(var(--card));
|
||||
background-color: hsl(var(--color-surface));
|
||||
border-radius: var(--radius-lg);
|
||||
padding: var(--spacing-lg);
|
||||
border: 1px solid hsl(var(--border));
|
||||
border: 1px solid hsl(var(--color-border));
|
||||
}
|
||||
|
||||
/* Button styles */
|
||||
|
|
@ -159,12 +159,12 @@
|
|||
}
|
||||
|
||||
.btn-primary {
|
||||
background: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
background: hsl(var(--color-primary));
|
||||
color: hsl(var(--color-primary-foreground));
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: hsl(var(--primary) / 0.9);
|
||||
background: hsl(var(--color-primary) / 0.9);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
|
|
@ -173,21 +173,21 @@
|
|||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: hsl(var(--secondary));
|
||||
color: hsl(var(--secondary-foreground));
|
||||
background: hsl(var(--color-secondary));
|
||||
color: hsl(var(--color-secondary-foreground));
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: hsl(var(--secondary) / 0.8);
|
||||
background: hsl(var(--color-secondary) / 0.8);
|
||||
}
|
||||
|
||||
.btn-ghost {
|
||||
background: transparent;
|
||||
color: hsl(var(--foreground));
|
||||
color: hsl(var(--color-foreground));
|
||||
}
|
||||
|
||||
.btn-ghost:hover {
|
||||
background: hsl(var(--muted));
|
||||
background: hsl(var(--color-muted));
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
|
|
@ -204,21 +204,21 @@
|
|||
display: block;
|
||||
width: 100%;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border: 2px solid hsl(var(--border));
|
||||
border: 2px solid hsl(var(--color-border));
|
||||
border-radius: var(--radius-md);
|
||||
background-color: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
background-color: hsl(var(--color-background));
|
||||
color: hsl(var(--color-foreground));
|
||||
font-size: 0.875rem;
|
||||
transition: border-color var(--transition-fast);
|
||||
}
|
||||
|
||||
.input:focus {
|
||||
outline: none;
|
||||
border-color: hsl(var(--primary));
|
||||
border-color: hsl(var(--color-primary));
|
||||
}
|
||||
|
||||
.input::placeholder {
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
}
|
||||
|
||||
/* Select styling */
|
||||
|
|
@ -233,7 +233,7 @@ select.input {
|
|||
|
||||
/* Text colors */
|
||||
.text-destructive {
|
||||
color: hsl(var(--destructive));
|
||||
color: hsl(var(--color-error));
|
||||
}
|
||||
|
||||
/* Scrollbar styling */
|
||||
|
|
|
|||
|
|
@ -85,8 +85,8 @@
|
|||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 1rem 1.5rem;
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
background: hsl(var(--card));
|
||||
border-bottom: 1px solid hsl(var(--color-border));
|
||||
background: hsl(var(--color-surface));
|
||||
}
|
||||
|
||||
.header-left {
|
||||
|
|
@ -103,7 +103,7 @@
|
|||
.header-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: hsl(var(--foreground));
|
||||
color: hsl(var(--color-foreground));
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +115,7 @@
|
|||
|
||||
.view-selector {
|
||||
display: flex;
|
||||
background: hsl(var(--muted));
|
||||
background: hsl(var(--color-muted));
|
||||
border-radius: var(--radius-md);
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
|
@ -127,18 +127,18 @@
|
|||
border-radius: var(--radius-sm);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
cursor: pointer;
|
||||
transition: all 150ms ease;
|
||||
}
|
||||
|
||||
.view-btn:hover {
|
||||
color: hsl(var(--foreground));
|
||||
color: hsl(var(--color-foreground));
|
||||
}
|
||||
|
||||
.view-btn.active {
|
||||
background: hsl(var(--background));
|
||||
color: hsl(var(--foreground));
|
||||
background: hsl(var(--color-background));
|
||||
color: hsl(var(--color-foreground));
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,9 +43,9 @@
|
|||
|
||||
<style>
|
||||
.calendar-sidebar-section {
|
||||
background: hsl(var(--card));
|
||||
background: hsl(var(--color-surface));
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid hsl(var(--border));
|
||||
border: 1px solid hsl(var(--color-border));
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +59,7 @@
|
|||
.section-title {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: hsl(var(--foreground));
|
||||
color: hsl(var(--color-foreground));
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
|
@ -69,12 +69,12 @@
|
|||
background: transparent;
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
}
|
||||
|
||||
.add-btn:hover {
|
||||
background: hsl(var(--muted));
|
||||
color: hsl(var(--foreground));
|
||||
background: hsl(var(--color-muted));
|
||||
color: hsl(var(--color-foreground));
|
||||
}
|
||||
|
||||
.calendar-list {
|
||||
|
|
@ -93,7 +93,7 @@
|
|||
}
|
||||
|
||||
.calendar-item:hover {
|
||||
background: hsl(var(--muted) / 0.5);
|
||||
background: hsl(var(--color-muted) / 0.5);
|
||||
}
|
||||
|
||||
.calendar-item input {
|
||||
|
|
@ -110,12 +110,12 @@
|
|||
|
||||
.calendar-name {
|
||||
font-size: 0.875rem;
|
||||
color: hsl(var(--foreground));
|
||||
color: hsl(var(--color-foreground));
|
||||
}
|
||||
|
||||
.empty-message {
|
||||
font-size: 0.875rem;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,13 +137,13 @@
|
|||
|
||||
.all-day-section {
|
||||
display: flex;
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
border-bottom: 1px solid hsl(var(--color-border));
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
|
||||
.all-day-label {
|
||||
font-size: 0.75rem;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
}
|
||||
|
||||
.all-day-events {
|
||||
|
|
@ -179,7 +179,7 @@
|
|||
padding-right: 0.5rem;
|
||||
text-align: right;
|
||||
font-size: 0.75rem;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
position: relative;
|
||||
top: -0.5em;
|
||||
}
|
||||
|
|
@ -194,11 +194,11 @@
|
|||
.day-column {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
border-left: 1px solid hsl(var(--border));
|
||||
border-left: 1px solid hsl(var(--color-border));
|
||||
}
|
||||
|
||||
.day-column.today {
|
||||
background: hsl(var(--primary) / 0.05);
|
||||
background: hsl(var(--color-primary) / 0.05);
|
||||
}
|
||||
|
||||
.event-card {
|
||||
|
|
|
|||
|
|
@ -103,12 +103,12 @@
|
|||
background: transparent;
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
}
|
||||
|
||||
.nav-btn:hover {
|
||||
background: hsl(var(--muted));
|
||||
color: hsl(var(--foreground));
|
||||
background: hsl(var(--color-muted));
|
||||
color: hsl(var(--color-foreground));
|
||||
}
|
||||
|
||||
.weekday-row {
|
||||
|
|
@ -120,7 +120,7 @@
|
|||
.weekday {
|
||||
text-align: center;
|
||||
font-size: 0.75rem;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
|
|
@ -141,25 +141,25 @@
|
|||
font-size: 0.75rem;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: hsl(var(--foreground));
|
||||
color: hsl(var(--color-foreground));
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.day:hover {
|
||||
background: hsl(var(--muted));
|
||||
background: hsl(var(--color-muted));
|
||||
}
|
||||
|
||||
.day.other-month {
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.day.today {
|
||||
background: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
background: hsl(var(--color-primary));
|
||||
color: hsl(var(--color-primary-foreground));
|
||||
}
|
||||
|
||||
.day.selected {
|
||||
border: 2px solid hsl(var(--primary));
|
||||
border: 2px solid hsl(var(--color-primary));
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@
|
|||
.weekday-headers {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
border-bottom: 1px solid hsl(var(--color-border));
|
||||
}
|
||||
|
||||
.weekday-header {
|
||||
|
|
@ -134,7 +134,7 @@
|
|||
text-align: center;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
|
|
@ -152,7 +152,7 @@
|
|||
}
|
||||
|
||||
.day-cell {
|
||||
border: 1px solid hsl(var(--border));
|
||||
border: 1px solid hsl(var(--color-border));
|
||||
border-top: none;
|
||||
border-left: none;
|
||||
padding: var(--spacing-xs);
|
||||
|
|
@ -165,15 +165,15 @@
|
|||
}
|
||||
|
||||
.day-cell:first-child {
|
||||
border-left: 1px solid hsl(var(--border));
|
||||
border-left: 1px solid hsl(var(--color-border));
|
||||
}
|
||||
|
||||
.day-cell:hover {
|
||||
background-color: hsl(var(--muted) / 0.3);
|
||||
background-color: hsl(var(--color-muted) / 0.3);
|
||||
}
|
||||
|
||||
.day-cell.today {
|
||||
background-color: hsl(var(--primary) / 0.1);
|
||||
background-color: hsl(var(--color-primary) / 0.1);
|
||||
}
|
||||
|
||||
.day-cell.other-month {
|
||||
|
|
@ -193,8 +193,8 @@
|
|||
}
|
||||
|
||||
.day-number.today {
|
||||
background: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
background: hsl(var(--color-primary));
|
||||
color: hsl(var(--color-primary-foreground));
|
||||
}
|
||||
|
||||
.day-events {
|
||||
|
|
@ -232,7 +232,7 @@
|
|||
|
||||
.more-events {
|
||||
font-size: 0.75rem;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
|
|
@ -241,6 +241,6 @@
|
|||
}
|
||||
|
||||
.more-events:hover {
|
||||
color: hsl(var(--primary));
|
||||
color: hsl(var(--color-primary));
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@
|
|||
|
||||
.all-day-row {
|
||||
display: flex;
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
border-bottom: 1px solid hsl(var(--color-border));
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
|
|
@ -172,7 +172,7 @@
|
|||
flex-wrap: wrap;
|
||||
gap: 2px;
|
||||
padding: 4px;
|
||||
border-left: 1px solid hsl(var(--border));
|
||||
border-left: 1px solid hsl(var(--color-border));
|
||||
}
|
||||
|
||||
.all-day-event {
|
||||
|
|
@ -189,7 +189,7 @@
|
|||
|
||||
.day-headers {
|
||||
display: flex;
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
border-bottom: 1px solid hsl(var(--color-border));
|
||||
}
|
||||
|
||||
.time-gutter {
|
||||
|
|
@ -203,12 +203,12 @@
|
|||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 0.5rem;
|
||||
border-left: 1px solid hsl(var(--border));
|
||||
border-left: 1px solid hsl(var(--color-border));
|
||||
}
|
||||
|
||||
.day-name {
|
||||
font-size: 0.75rem;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
|
|
@ -224,8 +224,8 @@
|
|||
}
|
||||
|
||||
.day-number.today {
|
||||
background: hsl(var(--primary));
|
||||
color: hsl(var(--primary-foreground));
|
||||
background: hsl(var(--color-primary));
|
||||
color: hsl(var(--color-primary-foreground));
|
||||
}
|
||||
|
||||
.time-grid {
|
||||
|
|
@ -244,7 +244,7 @@
|
|||
padding-right: 0.5rem;
|
||||
text-align: right;
|
||||
font-size: 0.75rem;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
position: relative;
|
||||
top: -0.5em;
|
||||
}
|
||||
|
|
@ -257,11 +257,11 @@
|
|||
.day-column {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
border-left: 1px solid hsl(var(--border));
|
||||
border-left: 1px solid hsl(var(--color-border));
|
||||
}
|
||||
|
||||
.day-column.today {
|
||||
background: hsl(var(--primary) / 0.05);
|
||||
background: hsl(var(--color-primary) / 0.05);
|
||||
}
|
||||
|
||||
.event-card {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,14 @@
|
|||
let description = $state(event?.description || '');
|
||||
let location = $state(event?.location || '');
|
||||
let isAllDay = $state(event?.isAllDay || false);
|
||||
let calendarId = $state(event?.calendarId || calendarsStore.defaultCalendar?.id || '');
|
||||
let calendarId = $state(event?.calendarId || '');
|
||||
|
||||
// Set default calendar when calendars are loaded
|
||||
$effect(() => {
|
||||
if (!calendarId && calendarsStore.defaultCalendar?.id) {
|
||||
calendarId = calendarsStore.defaultCalendar.id;
|
||||
}
|
||||
});
|
||||
|
||||
// Date/time handling
|
||||
let startDate = $state('');
|
||||
|
|
@ -58,6 +65,7 @@
|
|||
e.preventDefault();
|
||||
|
||||
if (!title.trim()) return;
|
||||
if (!calendarId) return;
|
||||
|
||||
const startDateTime = new Date(`${startDate}T${isAllDay ? '00:00' : startTime}`);
|
||||
const endDateTime = new Date(`${endDate}T${isAllDay ? '23:59' : endTime}`);
|
||||
|
|
@ -158,7 +166,7 @@
|
|||
<button type="button" class="px-4 py-2 rounded-lg font-medium text-foreground bg-transparent hover:bg-muted transition-colors" onclick={onCancel}>
|
||||
Abbrechen
|
||||
</button>
|
||||
<button type="submit" class="px-4 py-2 rounded-lg font-medium text-primary-foreground bg-primary hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed transition-colors" disabled={submitting || !title.trim()}>
|
||||
<button type="submit" class="px-4 py-2 rounded-lg font-medium text-primary-foreground bg-primary hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed transition-colors" disabled={submitting || !title.trim() || !calendarId}>
|
||||
{mode === 'create' ? 'Erstellen' : 'Speichern'}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -56,7 +56,9 @@ export const calendarsStore = {
|
|||
error = result.error.message;
|
||||
calendars = [];
|
||||
} else {
|
||||
calendars = result.data || [];
|
||||
// API returns { calendars: [...] }
|
||||
const data = result.data as { calendars: Calendar[] } | null;
|
||||
calendars = data?.calendars || [];
|
||||
}
|
||||
|
||||
loading = false;
|
||||
|
|
@ -70,7 +72,9 @@ export const calendarsStore = {
|
|||
const result = await api.createCalendar(data);
|
||||
|
||||
if (result.data) {
|
||||
calendars = [...calendars, result.data];
|
||||
// API returns { calendar: {...} }
|
||||
const responseData = result.data as { calendar: Calendar };
|
||||
calendars = [...calendars, responseData.calendar];
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -83,7 +87,9 @@ export const calendarsStore = {
|
|||
const result = await api.updateCalendar(id, data);
|
||||
|
||||
if (result.data) {
|
||||
calendars = getCalendarsArray().map((c) => (c.id === id ? result.data! : c));
|
||||
// API returns { calendar: {...} }
|
||||
const responseData = result.data as { calendar: Calendar };
|
||||
calendars = getCalendarsArray().map((c) => (c.id === id ? responseData.calendar : c));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ let error = $state<string | null>(null);
|
|||
let loadedRange = $state<{ start: Date; end: Date } | null>(null);
|
||||
|
||||
export const eventsStore = {
|
||||
// Getters
|
||||
// Getters - always return safe values
|
||||
get events() {
|
||||
return events;
|
||||
return events ?? [];
|
||||
},
|
||||
get loading() {
|
||||
return loading;
|
||||
|
|
@ -40,7 +40,9 @@ export const eventsStore = {
|
|||
if (result.error) {
|
||||
error = result.error.message;
|
||||
} else {
|
||||
events = result.data || [];
|
||||
// API returns { events: [...] }
|
||||
const data = result.data as { events: CalendarEvent[] } | null;
|
||||
events = data?.events || [];
|
||||
loadedRange = { start: startDate, end: endDate };
|
||||
}
|
||||
|
||||
|
|
@ -94,7 +96,9 @@ export const eventsStore = {
|
|||
const result = await api.createEvent(data);
|
||||
|
||||
if (result.data) {
|
||||
events = [...events, result.data];
|
||||
// API returns { event: {...} }
|
||||
const responseData = result.data as { event: CalendarEvent };
|
||||
events = [...events, responseData.event];
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -107,7 +111,9 @@ export const eventsStore = {
|
|||
const result = await api.updateEvent(id, data);
|
||||
|
||||
if (result.data) {
|
||||
events = events.map((e) => (e.id === id ? result.data! : e));
|
||||
// API returns { event: {...} }
|
||||
const responseData = result.data as { event: CalendarEvent };
|
||||
events = events.map((e) => (e.id === id ? responseData.event : e));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@
|
|||
const navItems: PillNavItem[] = [
|
||||
{ href: '/', label: 'Kalender', icon: 'calendar' },
|
||||
{ href: '/agenda', label: 'Agenda', icon: 'list' },
|
||||
{ href: '/calendars', label: 'Meine Kalender', icon: 'document' },
|
||||
{ href: '/calendars', label: 'Meine Kalender', icon: 'folder' },
|
||||
{ href: '/settings', label: 'Einstellungen', icon: 'settings' },
|
||||
{ href: '/feedback', label: 'Feedback', icon: 'chat' },
|
||||
];
|
||||
|
|
|
|||
|
|
@ -107,9 +107,9 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
background: hsl(var(--card));
|
||||
background: hsl(var(--color-surface));
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid hsl(var(--border));
|
||||
border: 1px solid hsl(var(--color-border));
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,9 +11,13 @@
|
|||
|
||||
// Group events by date
|
||||
let groupedEvents = $derived.by(() => {
|
||||
const groups: Map<string, typeof eventsStore.events> = new Map();
|
||||
// Safety check: ensure events is an array
|
||||
const currentEvents = eventsStore.events ?? [];
|
||||
if (!Array.isArray(currentEvents)) return [];
|
||||
|
||||
for (const event of eventsStore.events) {
|
||||
const groups: Map<string, typeof currentEvents> = new Map();
|
||||
|
||||
for (const event of currentEvents) {
|
||||
const start = typeof event.startTime === 'string' ? parseISO(event.startTime) : event.startTime;
|
||||
const dateKey = format(start, 'yyyy-MM-dd');
|
||||
|
||||
|
|
@ -132,19 +136,19 @@
|
|||
.page-header h1 {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
color: hsl(var(--foreground));
|
||||
color: hsl(var(--color-foreground));
|
||||
margin: 0 0 0.25rem 0;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
|
|
@ -153,7 +157,7 @@
|
|||
}
|
||||
|
||||
.empty-state p {
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
|
|
@ -172,7 +176,7 @@
|
|||
.date-header {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin: 0;
|
||||
|
|
@ -180,7 +184,7 @@
|
|||
}
|
||||
|
||||
.date-header.today {
|
||||
color: hsl(var(--primary));
|
||||
color: hsl(var(--color-primary));
|
||||
}
|
||||
|
||||
.event-item {
|
||||
|
|
@ -211,13 +215,13 @@
|
|||
|
||||
.event-time {
|
||||
font-size: 0.75rem;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.event-title {
|
||||
font-weight: 500;
|
||||
color: hsl(var(--foreground));
|
||||
color: hsl(var(--color-foreground));
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
|
@ -225,7 +229,7 @@
|
|||
|
||||
.event-location {
|
||||
font-size: 0.875rem;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@
|
|||
width: 48px;
|
||||
height: 42px;
|
||||
padding: 4px;
|
||||
border: 2px solid hsl(var(--border));
|
||||
border: 2px solid hsl(var(--color-border));
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
@ -263,9 +263,9 @@
|
|||
.badge {
|
||||
font-size: 0.75rem;
|
||||
padding: 0.125rem 0.5rem;
|
||||
background: hsl(var(--muted));
|
||||
background: hsl(var(--color-muted));
|
||||
border-radius: var(--radius-sm);
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
}
|
||||
|
||||
.calendar-actions {
|
||||
|
|
@ -279,12 +279,12 @@
|
|||
}
|
||||
|
||||
.text-destructive {
|
||||
color: hsl(var(--destructive));
|
||||
color: hsl(var(--color-error));
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@
|
|||
.page-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: hsl(var(--foreground));
|
||||
color: hsl(var(--color-foreground));
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
|
@ -171,7 +171,7 @@
|
|||
.loading {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
}
|
||||
|
||||
.event-details {
|
||||
|
|
@ -189,15 +189,15 @@
|
|||
.label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 1rem;
|
||||
color: hsl(var(--foreground));
|
||||
color: hsl(var(--color-foreground));
|
||||
}
|
||||
|
||||
.text-destructive {
|
||||
color: hsl(var(--destructive));
|
||||
color: hsl(var(--color-error));
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -68,6 +68,6 @@
|
|||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 1.5rem;
|
||||
color: hsl(var(--foreground));
|
||||
color: hsl(var(--color-foreground));
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -134,12 +134,12 @@
|
|||
font-weight: 600;
|
||||
margin: 0 0 1rem 0;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
border-bottom: 1px solid hsl(var(--color-border));
|
||||
}
|
||||
|
||||
.setting-item {
|
||||
padding: 1rem 0;
|
||||
border-bottom: 1px solid hsl(var(--border) / 0.5);
|
||||
border-bottom: 1px solid hsl(var(--color-border) / 0.5);
|
||||
}
|
||||
|
||||
.setting-item:last-child {
|
||||
|
|
@ -156,17 +156,17 @@
|
|||
|
||||
.setting-label {
|
||||
font-weight: 500;
|
||||
color: hsl(var(--foreground));
|
||||
color: hsl(var(--color-foreground));
|
||||
}
|
||||
|
||||
.setting-description {
|
||||
font-size: 0.875rem;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
}
|
||||
|
||||
.setting-value {
|
||||
font-size: 0.875rem;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
}
|
||||
|
||||
.theme-options {
|
||||
|
|
@ -179,22 +179,22 @@
|
|||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border: 2px solid hsl(var(--border));
|
||||
border: 2px solid hsl(var(--color-border));
|
||||
border-radius: var(--radius-md);
|
||||
background: transparent;
|
||||
color: hsl(var(--foreground));
|
||||
color: hsl(var(--color-foreground));
|
||||
font-size: 0.875rem;
|
||||
cursor: pointer;
|
||||
transition: all 150ms ease;
|
||||
}
|
||||
|
||||
.theme-option:hover {
|
||||
border-color: hsl(var(--primary) / 0.5);
|
||||
border-color: hsl(var(--color-primary) / 0.5);
|
||||
}
|
||||
|
||||
.theme-option.active {
|
||||
border-color: hsl(var(--primary));
|
||||
background: hsl(var(--primary) / 0.1);
|
||||
border-color: hsl(var(--color-primary));
|
||||
background: hsl(var(--color-primary) / 0.1);
|
||||
}
|
||||
|
||||
.variant-grid {
|
||||
|
|
@ -209,7 +209,7 @@
|
|||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
padding: 0.75rem;
|
||||
border: 2px solid hsl(var(--border));
|
||||
border: 2px solid hsl(var(--color-border));
|
||||
border-radius: var(--radius-md);
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
|
|
@ -217,12 +217,12 @@
|
|||
}
|
||||
|
||||
.variant-option:hover {
|
||||
border-color: hsl(var(--primary) / 0.5);
|
||||
border-color: hsl(var(--color-primary) / 0.5);
|
||||
}
|
||||
|
||||
.variant-option.active {
|
||||
border-color: hsl(var(--primary));
|
||||
background: hsl(var(--primary) / 0.1);
|
||||
border-color: hsl(var(--color-primary));
|
||||
background: hsl(var(--color-primary) / 0.1);
|
||||
}
|
||||
|
||||
.variant-icon {
|
||||
|
|
@ -231,10 +231,10 @@
|
|||
|
||||
.variant-label {
|
||||
font-size: 0.75rem;
|
||||
color: hsl(var(--muted-foreground));
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
}
|
||||
|
||||
.text-destructive {
|
||||
color: hsl(var(--destructive));
|
||||
color: hsl(var(--color-error));
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue