mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-28 07:37:43 +02:00
feat(calendar): replace calendar dropdown with horizontal pills
- Replace select dropdown with horizontal scrolling pill buttons in QuickEventOverlay - Each pill shows colored dot + calendar name for quick visual selection - Add setAsDefault method to calendars store - Improve settings page calendar editing with SettingsSection/SettingsCard components 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
5190b1449a
commit
b720f64d2f
3 changed files with 683 additions and 347 deletions
|
|
@ -361,15 +361,6 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update draft when calendar changes
|
|
||||||
function handleCalendarChange(e: Event) {
|
|
||||||
const target = e.target as HTMLSelectElement;
|
|
||||||
calendarId = target.value;
|
|
||||||
if (!isEditMode) {
|
|
||||||
eventsStore.updateDraftEvent({ calendarId: target.value });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update draft when all-day changes
|
// Update draft when all-day changes
|
||||||
function handleAllDayToggle() {
|
function handleAllDayToggle() {
|
||||||
isAllDay = !isAllDay;
|
isAllDay = !isAllDay;
|
||||||
|
|
@ -711,26 +702,31 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Calendar select -->
|
<!-- Calendar pills -->
|
||||||
<div class="form-row">
|
<div class="calendar-pills-container">
|
||||||
<div class="row-icon">
|
{#if calendarsStore.calendars.length > 0}
|
||||||
<div
|
<div class="calendar-pills-scroll">
|
||||||
class="calendar-dot"
|
{#each calendarsStore.calendars as cal}
|
||||||
style="background-color: {calendarsStore.getColor(calendarId)}"
|
<button
|
||||||
></div>
|
type="button"
|
||||||
</div>
|
class="calendar-pill"
|
||||||
<div class="row-content">
|
class:active={calendarId === cal.id}
|
||||||
<span class="field-label">Kalender</span>
|
onclick={() => {
|
||||||
{#if calendarsStore.calendars.length > 0}
|
calendarId = cal.id;
|
||||||
<select class="field-select" value={calendarId} onchange={handleCalendarChange}>
|
if (!isEditMode) {
|
||||||
{#each calendarsStore.calendars as cal}
|
eventsStore.updateDraftEvent({ calendarId: cal.id });
|
||||||
<option value={cal.id}>{cal.name}</option>
|
}
|
||||||
{/each}
|
}}
|
||||||
</select>
|
>
|
||||||
{:else}
|
<span class="calendar-pill-dot" style="background-color: {cal.color || '#3b82f6'}"
|
||||||
<span class="field-placeholder">Standardkalender wird erstellt</span>
|
></span>
|
||||||
{/if}
|
<span class="calendar-pill-name">{cal.name}</span>
|
||||||
</div>
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<span class="field-placeholder">Standardkalender wird erstellt</span>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- People (compact) -->
|
<!-- People (compact) -->
|
||||||
|
|
@ -1225,6 +1221,63 @@
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Calendar pills */
|
||||||
|
.calendar-pills-container {
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
border-bottom: 1px solid hsl(var(--color-border));
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-pills-scroll {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
scrollbar-width: none;
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
padding: 0 1.25rem 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-pills-scroll::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-pill {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.375rem;
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
border: 1px solid hsl(var(--color-border));
|
||||||
|
border-radius: 9999px;
|
||||||
|
background: transparent;
|
||||||
|
color: hsl(var(--color-muted-foreground));
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
font-weight: 500;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 150ms;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-pill:hover {
|
||||||
|
background: hsl(var(--color-muted) / 0.3);
|
||||||
|
color: hsl(var(--color-foreground));
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-pill.active {
|
||||||
|
background: hsl(var(--color-primary) / 0.1);
|
||||||
|
border-color: hsl(var(--color-primary) / 0.3);
|
||||||
|
color: hsl(var(--color-primary));
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-pill-dot {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-pill-name {
|
||||||
|
}
|
||||||
|
|
||||||
.row-content {
|
.row-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,23 @@ export const calendarsStore = {
|
||||||
return this.updateCalendar(id, { isVisible: !calendar.isVisible });
|
return this.updateCalendar(id, { isVisible: !calendar.isVisible });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a calendar as the default
|
||||||
|
*/
|
||||||
|
async setAsDefault(id: string) {
|
||||||
|
const result = await api.updateCalendar(id, { isDefault: true });
|
||||||
|
|
||||||
|
if (result.data) {
|
||||||
|
// Update local state: set this one as default, remove default from others
|
||||||
|
calendars = getCalendarsArray().map((c) => ({
|
||||||
|
...c,
|
||||||
|
isDefault: c.id === id,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get calendar by ID
|
* Get calendar by ID
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,32 @@
|
||||||
import type { TimeFormat, AllDayDisplayMode } from '$lib/stores/settings.svelte';
|
import type { TimeFormat, AllDayDisplayMode } from '$lib/stores/settings.svelte';
|
||||||
import { calendarsStore } from '$lib/stores/calendars.svelte';
|
import { calendarsStore } from '$lib/stores/calendars.svelte';
|
||||||
import { toast } from '$lib/stores/toast.svelte';
|
import { toast } from '$lib/stores/toast.svelte';
|
||||||
import { GlobalSettingsSection } from '@manacore/shared-ui';
|
import { GlobalSettingsSection, SettingsSection, SettingsCard } from '@manacore/shared-ui';
|
||||||
import type { CalendarViewType, Calendar } from '@calendar/shared';
|
import type { CalendarViewType, Calendar } from '@calendar/shared';
|
||||||
|
|
||||||
// Calendar management state
|
// Calendar management state
|
||||||
let editingCalendar = $state<Calendar | null>(null);
|
let editingCalendar = $state<Calendar | null>(null);
|
||||||
|
let editName = $state('');
|
||||||
|
let editColor = $state('');
|
||||||
|
let editIsDefault = $state(false);
|
||||||
let showNewCalendarForm = $state(false);
|
let showNewCalendarForm = $state(false);
|
||||||
let newCalendarName = $state('');
|
let newCalendarName = $state('');
|
||||||
let newCalendarColor = $state('#3b82f6');
|
let newCalendarColor = $state('#3b82f6');
|
||||||
|
|
||||||
|
function startEditing(calendar: Calendar) {
|
||||||
|
editingCalendar = calendar;
|
||||||
|
editName = calendar.name;
|
||||||
|
editColor = calendar.color || '#3b82f6';
|
||||||
|
editIsDefault = calendar.isDefault || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelEditing() {
|
||||||
|
editingCalendar = null;
|
||||||
|
editName = '';
|
||||||
|
editColor = '';
|
||||||
|
editIsDefault = false;
|
||||||
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (!authStore.isAuthenticated) {
|
if (!authStore.isAuthenticated) {
|
||||||
goto('/login');
|
goto('/login');
|
||||||
|
|
@ -59,8 +76,23 @@
|
||||||
toast.success('Kalender gelöscht');
|
toast.success('Kalender gelöscht');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleUpdateCalendar(calendar: Calendar, name: string, color: string) {
|
async function handleUpdateCalendar() {
|
||||||
const result = await calendarsStore.updateCalendar(calendar.id, { name, color });
|
if (!editingCalendar || !editName.trim()) return;
|
||||||
|
|
||||||
|
// If setting as default and it wasn't before, use setAsDefault
|
||||||
|
if (editIsDefault && !editingCalendar.isDefault) {
|
||||||
|
const defaultResult = await calendarsStore.setAsDefault(editingCalendar.id);
|
||||||
|
if (defaultResult?.error) {
|
||||||
|
toast.error(`Fehler: ${defaultResult.error.message}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update name and color
|
||||||
|
const result = await calendarsStore.updateCalendar(editingCalendar.id, {
|
||||||
|
name: editName.trim(),
|
||||||
|
color: editColor,
|
||||||
|
});
|
||||||
|
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
toast.error(`Fehler: ${result.error.message}`);
|
toast.error(`Fehler: ${result.error.message}`);
|
||||||
|
|
@ -68,7 +100,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
toast.success('Kalender aktualisiert');
|
toast.success('Kalender aktualisiert');
|
||||||
editingCalendar = null;
|
cancelEditing();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleViewChange(view: CalendarViewType) {
|
function handleViewChange(view: CalendarViewType) {
|
||||||
|
|
@ -124,335 +156,422 @@
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Meine Kalender -->
|
<!-- Meine Kalender -->
|
||||||
<section class="settings-section card">
|
<SettingsSection title="Meine Kalender">
|
||||||
<div class="calendars-header">
|
{#snippet icon()}
|
||||||
<h2>Meine Kalender</h2>
|
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<button class="btn btn-primary btn-sm" onclick={() => (showNewCalendarForm = true)}>
|
<path
|
||||||
Neuer Kalender
|
stroke-linecap="round"
|
||||||
</button>
|
stroke-linejoin="round"
|
||||||
</div>
|
stroke-width="2"
|
||||||
|
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{/snippet}
|
||||||
|
<SettingsCard>
|
||||||
|
<div class="p-5">
|
||||||
|
<div class="calendars-toolbar">
|
||||||
|
<button class="btn btn-primary btn-sm" onclick={() => (showNewCalendarForm = true)}>
|
||||||
|
Neuer Kalender
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
{#if showNewCalendarForm}
|
{#if showNewCalendarForm}
|
||||||
<div class="new-calendar-form">
|
<div class="new-calendar-form">
|
||||||
<form
|
|
||||||
onsubmit={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
handleCreateCalendar();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div class="calendar-form-row">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="input"
|
|
||||||
placeholder="Kalender Name"
|
|
||||||
bind:value={newCalendarName}
|
|
||||||
/>
|
|
||||||
<input type="color" class="color-input" bind:value={newCalendarColor} />
|
|
||||||
</div>
|
|
||||||
<div class="calendar-form-actions">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-ghost"
|
|
||||||
onclick={() => (showNewCalendarForm = false)}
|
|
||||||
>
|
|
||||||
Abbrechen
|
|
||||||
</button>
|
|
||||||
<button type="submit" class="btn btn-primary" disabled={!newCalendarName.trim()}>
|
|
||||||
Erstellen
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<div class="calendar-list">
|
|
||||||
{#each calendarsStore.calendars as calendar}
|
|
||||||
<div class="calendar-card">
|
|
||||||
{#if editingCalendar?.id === calendar.id}
|
|
||||||
<form
|
<form
|
||||||
onsubmit={(e) => {
|
onsubmit={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const form = e.target as HTMLFormElement;
|
handleCreateCalendar();
|
||||||
const name = (form.elements.namedItem('name') as HTMLInputElement).value;
|
|
||||||
const color = (form.elements.namedItem('color') as HTMLInputElement).value;
|
|
||||||
handleUpdateCalendar(calendar, name, color);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="calendar-form-row">
|
<div class="calendar-form-row">
|
||||||
<input type="text" name="name" class="input" value={calendar.name} />
|
<input
|
||||||
<input type="color" name="color" class="color-input" value={calendar.color} />
|
type="text"
|
||||||
|
class="input"
|
||||||
|
placeholder="Kalender Name"
|
||||||
|
bind:value={newCalendarName}
|
||||||
|
/>
|
||||||
|
<input type="color" class="color-input" bind:value={newCalendarColor} />
|
||||||
</div>
|
</div>
|
||||||
<div class="calendar-form-actions">
|
<div class="calendar-form-actions">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-ghost"
|
class="btn btn-ghost"
|
||||||
onclick={() => (editingCalendar = null)}
|
onclick={() => (showNewCalendarForm = false)}
|
||||||
>
|
>
|
||||||
Abbrechen
|
Abbrechen
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" class="btn btn-primary"> Speichern </button>
|
<button type="submit" class="btn btn-primary" disabled={!newCalendarName.trim()}>
|
||||||
|
Erstellen
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{:else}
|
</div>
|
||||||
<div class="calendar-info">
|
{/if}
|
||||||
<span class="color-dot" style="background-color: {calendar.color}"></span>
|
|
||||||
<span class="calendar-name">{calendar.name}</span>
|
<div class="calendar-list">
|
||||||
{#if calendar.isDefault}
|
{#each calendarsStore.calendars as calendar}
|
||||||
<span class="badge">Standard</span>
|
{#if editingCalendar?.id === calendar.id}
|
||||||
{/if}
|
<div class="calendar-edit-form">
|
||||||
</div>
|
<form
|
||||||
<div class="calendar-actions">
|
onsubmit={(e) => {
|
||||||
<button class="btn btn-ghost btn-sm" onclick={() => (editingCalendar = calendar)}>
|
e.preventDefault();
|
||||||
Bearbeiten
|
handleUpdateCalendar();
|
||||||
</button>
|
}}
|
||||||
{#if !calendar.isDefault}
|
|
||||||
<button
|
|
||||||
class="btn btn-ghost btn-sm text-destructive"
|
|
||||||
onclick={() => handleDeleteCalendar(calendar)}
|
|
||||||
>
|
>
|
||||||
Löschen
|
<div class="edit-form-row">
|
||||||
</button>
|
<div class="edit-form-group edit-form-group--name">
|
||||||
{/if}
|
<label for="edit-name" class="edit-label">Name</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="edit-name"
|
||||||
|
class="edit-input"
|
||||||
|
placeholder="Kalender Name"
|
||||||
|
bind:value={editName}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="edit-form-group edit-form-group--color">
|
||||||
|
<label for="edit-color" class="edit-label">Farbe</label>
|
||||||
|
<div class="edit-color-wrapper">
|
||||||
|
<input
|
||||||
|
type="color"
|
||||||
|
id="edit-color"
|
||||||
|
class="edit-color-input"
|
||||||
|
bind:value={editColor}
|
||||||
|
/>
|
||||||
|
<span class="edit-color-value">{editColor}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label class="edit-checkbox">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
bind:checked={editIsDefault}
|
||||||
|
disabled={editingCalendar.isDefault}
|
||||||
|
/>
|
||||||
|
<span class="edit-checkbox-text">
|
||||||
|
Als Standardkalender verwenden
|
||||||
|
{#if editingCalendar.isDefault}
|
||||||
|
<span class="edit-checkbox-hint">(aktueller Standard)</span>
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="edit-form-actions">
|
||||||
|
<button type="button" class="btn btn-ghost" onclick={cancelEditing}>
|
||||||
|
Abbrechen
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="btn btn-primary" disabled={!editName.trim()}>
|
||||||
|
Speichern
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="calendar-card">
|
||||||
|
<div class="calendar-info">
|
||||||
|
<span class="color-dot" style="background-color: {calendar.color}"></span>
|
||||||
|
<span class="calendar-name">{calendar.name}</span>
|
||||||
|
{#if calendar.isDefault}
|
||||||
|
<span class="badge badge-primary">Standard</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="calendar-actions">
|
||||||
|
<button class="btn btn-ghost btn-sm" onclick={() => startEditing(calendar)}>
|
||||||
|
Bearbeiten
|
||||||
|
</button>
|
||||||
|
{#if !calendar.isDefault}
|
||||||
|
<button
|
||||||
|
class="btn btn-ghost btn-sm text-destructive"
|
||||||
|
onclick={() => handleDeleteCalendar(calendar)}
|
||||||
|
>
|
||||||
|
Löschen
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
{#if calendarsStore.calendars.length === 0}
|
||||||
|
<div class="empty-state">
|
||||||
|
<p>Keine Kalender vorhanden</p>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
</div>
|
||||||
|
</SettingsCard>
|
||||||
{#if calendarsStore.calendars.length === 0}
|
</SettingsSection>
|
||||||
<div class="empty-state">
|
|
||||||
<p>Keine Kalender vorhanden</p>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Global App Settings (synced across all apps) -->
|
<!-- Global App Settings (synced across all apps) -->
|
||||||
<section class="settings-section">
|
<GlobalSettingsSection
|
||||||
<GlobalSettingsSection
|
{userSettings}
|
||||||
{userSettings}
|
appId="calendar"
|
||||||
appId="calendar"
|
title="App-Einstellungen"
|
||||||
title="App-Einstellungen"
|
description="Diese Einstellungen werden mit allen Mana Apps synchronisiert"
|
||||||
description="Diese Einstellungen werden mit allen Mana Apps synchronisiert"
|
/>
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Kalender-Ansicht -->
|
<!-- Kalender-Ansicht -->
|
||||||
<section class="settings-section card">
|
<SettingsSection title="Kalender-Ansicht">
|
||||||
<h2>Kalender-Ansicht</h2>
|
{#snippet icon()}
|
||||||
|
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<div class="setting-item">
|
<path
|
||||||
<div class="setting-info">
|
stroke-linecap="round"
|
||||||
<span class="setting-label">Standard-Ansicht</span>
|
stroke-linejoin="round"
|
||||||
<span class="setting-description">Ansicht beim Öffnen des Kalenders</span>
|
stroke-width="2"
|
||||||
</div>
|
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||||
<select
|
|
||||||
class="select-input"
|
|
||||||
value={settingsStore.defaultView}
|
|
||||||
onchange={(e) => handleViewChange(e.currentTarget.value as CalendarViewType)}
|
|
||||||
>
|
|
||||||
{#each Object.entries(viewLabels) as [value, label]}
|
|
||||||
<option {value}>{label}</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="setting-info">
|
|
||||||
<span class="setting-label">Zeitformat</span>
|
|
||||||
<span class="setting-description">Anzeige der Uhrzeiten</span>
|
|
||||||
</div>
|
|
||||||
<div class="button-group">
|
|
||||||
<button
|
|
||||||
class="group-button"
|
|
||||||
class:active={settingsStore.timeFormat === '24h'}
|
|
||||||
onclick={() => handleTimeFormatChange('24h')}
|
|
||||||
>
|
|
||||||
24h (14:00)
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="group-button"
|
|
||||||
class:active={settingsStore.timeFormat === '12h'}
|
|
||||||
onclick={() => handleTimeFormatChange('12h')}
|
|
||||||
>
|
|
||||||
12h (2:00 PM)
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-item">
|
|
||||||
<label class="toggle-setting">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={settingsStore.showOnlyWeekdays}
|
|
||||||
onchange={() => settingsStore.set('showOnlyWeekdays', !settingsStore.showOnlyWeekdays)}
|
|
||||||
/>
|
/>
|
||||||
<div class="toggle-info">
|
<path
|
||||||
<span class="setting-label">Nur Werktage anzeigen</span>
|
stroke-linecap="round"
|
||||||
<span class="setting-description">Wochenenden in der Kalenderansicht ausblenden</span>
|
stroke-linejoin="round"
|
||||||
</div>
|
stroke-width="2"
|
||||||
</label>
|
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-item">
|
|
||||||
<label class="toggle-setting">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={settingsStore.showWeekNumbers}
|
|
||||||
onchange={() => settingsStore.set('showWeekNumbers', !settingsStore.showWeekNumbers)}
|
|
||||||
/>
|
/>
|
||||||
<div class="toggle-info">
|
</svg>
|
||||||
<span class="setting-label">Wochennummern anzeigen</span>
|
{/snippet}
|
||||||
<span class="setting-description">Kalenderwoche (KW) in der Ansicht anzeigen</span>
|
<SettingsCard>
|
||||||
</div>
|
<div class="p-5 space-y-4">
|
||||||
</label>
|
<div class="setting-item">
|
||||||
</div>
|
<div class="setting-info">
|
||||||
|
<span class="setting-label">Standard-Ansicht</span>
|
||||||
<div class="setting-item">
|
<span class="setting-description">Ansicht beim Öffnen des Kalenders</span>
|
||||||
<label class="toggle-setting">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={settingsStore.filterHoursEnabled}
|
|
||||||
onchange={() =>
|
|
||||||
settingsStore.set('filterHoursEnabled', !settingsStore.filterHoursEnabled)}
|
|
||||||
/>
|
|
||||||
<div class="toggle-info">
|
|
||||||
<span class="setting-label">Stunden filtern</span>
|
|
||||||
<span class="setting-description"
|
|
||||||
>Nur bestimmte Stunden in der Tages-/Wochenansicht anzeigen</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if settingsStore.filterHoursEnabled}
|
|
||||||
<div class="setting-item hour-range-setting">
|
|
||||||
<div class="setting-info">
|
|
||||||
<span class="setting-label">Sichtbare Stunden</span>
|
|
||||||
<span class="setting-description"
|
|
||||||
>Zeitbereich der in der Kalenderansicht angezeigt wird</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="hour-range-inputs">
|
|
||||||
<div class="hour-input-group">
|
|
||||||
<label for="start-hour">Von</label>
|
|
||||||
<select
|
|
||||||
id="start-hour"
|
|
||||||
class="select-input hour-select"
|
|
||||||
value={settingsStore.dayStartHour}
|
|
||||||
onchange={(e) => settingsStore.set('dayStartHour', Number(e.currentTarget.value))}
|
|
||||||
>
|
|
||||||
{#each Array.from({ length: 24 }, (_, i) => i) as hour}
|
|
||||||
{#if hour < settingsStore.dayEndHour}
|
|
||||||
<option value={hour}>{hour.toString().padStart(2, '0')}:00</option>
|
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
<span class="hour-separator">–</span>
|
<select
|
||||||
<div class="hour-input-group">
|
class="select-input"
|
||||||
<label for="end-hour">Bis</label>
|
value={settingsStore.defaultView}
|
||||||
<select
|
onchange={(e) => handleViewChange(e.currentTarget.value as CalendarViewType)}
|
||||||
id="end-hour"
|
>
|
||||||
class="select-input hour-select"
|
{#each Object.entries(viewLabels) as [value, label]}
|
||||||
value={settingsStore.dayEndHour}
|
<option {value}>{label}</option>
|
||||||
onchange={(e) => settingsStore.set('dayEndHour', Number(e.currentTarget.value))}
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-info">
|
||||||
|
<span class="setting-label">Zeitformat</span>
|
||||||
|
<span class="setting-description">Anzeige der Uhrzeiten</span>
|
||||||
|
</div>
|
||||||
|
<div class="button-group">
|
||||||
|
<button
|
||||||
|
class="group-button"
|
||||||
|
class:active={settingsStore.timeFormat === '24h'}
|
||||||
|
onclick={() => handleTimeFormatChange('24h')}
|
||||||
>
|
>
|
||||||
{#each Array.from({ length: 24 }, (_, i) => i + 1) as hour}
|
24h (14:00)
|
||||||
{#if hour > settingsStore.dayStartHour}
|
</button>
|
||||||
<option value={hour}>{hour.toString().padStart(2, '0')}:00</option>
|
<button
|
||||||
{/if}
|
class="group-button"
|
||||||
{/each}
|
class:active={settingsStore.timeFormat === '12h'}
|
||||||
</select>
|
onclick={() => handleTimeFormatChange('12h')}
|
||||||
|
>
|
||||||
|
12h (2:00 PM)
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<label class="toggle-setting">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={settingsStore.showOnlyWeekdays}
|
||||||
|
onchange={() =>
|
||||||
|
settingsStore.set('showOnlyWeekdays', !settingsStore.showOnlyWeekdays)}
|
||||||
|
/>
|
||||||
|
<div class="toggle-info">
|
||||||
|
<span class="setting-label">Nur Werktage anzeigen</span>
|
||||||
|
<span class="setting-description">Wochenenden in der Kalenderansicht ausblenden</span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<label class="toggle-setting">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={settingsStore.showWeekNumbers}
|
||||||
|
onchange={() => settingsStore.set('showWeekNumbers', !settingsStore.showWeekNumbers)}
|
||||||
|
/>
|
||||||
|
<div class="toggle-info">
|
||||||
|
<span class="setting-label">Wochennummern anzeigen</span>
|
||||||
|
<span class="setting-description">Kalenderwoche (KW) in der Ansicht anzeigen</span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<label class="toggle-setting">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={settingsStore.filterHoursEnabled}
|
||||||
|
onchange={() =>
|
||||||
|
settingsStore.set('filterHoursEnabled', !settingsStore.filterHoursEnabled)}
|
||||||
|
/>
|
||||||
|
<div class="toggle-info">
|
||||||
|
<span class="setting-label">Stunden filtern</span>
|
||||||
|
<span class="setting-description"
|
||||||
|
>Nur bestimmte Stunden in der Tages-/Wochenansicht anzeigen</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if settingsStore.filterHoursEnabled}
|
||||||
|
<div class="setting-item hour-range-setting">
|
||||||
|
<div class="setting-info">
|
||||||
|
<span class="setting-label">Sichtbare Stunden</span>
|
||||||
|
<span class="setting-description"
|
||||||
|
>Zeitbereich der in der Kalenderansicht angezeigt wird</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="hour-range-inputs">
|
||||||
|
<div class="hour-input-group">
|
||||||
|
<label for="start-hour">Von</label>
|
||||||
|
<select
|
||||||
|
id="start-hour"
|
||||||
|
class="select-input hour-select"
|
||||||
|
value={settingsStore.dayStartHour}
|
||||||
|
onchange={(e) => settingsStore.set('dayStartHour', Number(e.currentTarget.value))}
|
||||||
|
>
|
||||||
|
{#each Array.from({ length: 24 }, (_, i) => i) as hour}
|
||||||
|
{#if hour < settingsStore.dayEndHour}
|
||||||
|
<option value={hour}>{hour.toString().padStart(2, '0')}:00</option>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<span class="hour-separator">–</span>
|
||||||
|
<div class="hour-input-group">
|
||||||
|
<label for="end-hour">Bis</label>
|
||||||
|
<select
|
||||||
|
id="end-hour"
|
||||||
|
class="select-input hour-select"
|
||||||
|
value={settingsStore.dayEndHour}
|
||||||
|
onchange={(e) => settingsStore.set('dayEndHour', Number(e.currentTarget.value))}
|
||||||
|
>
|
||||||
|
{#each Array.from({ length: 24 }, (_, i) => i + 1) as hour}
|
||||||
|
{#if hour > settingsStore.dayStartHour}
|
||||||
|
<option value={hour}>{hour.toString().padStart(2, '0')}:00</option>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-info">
|
||||||
|
<span class="setting-label">Ganztägige Termine</span>
|
||||||
|
<span class="setting-description">Wie sollen ganztägige Termine angezeigt werden?</span>
|
||||||
|
</div>
|
||||||
|
<div class="button-group">
|
||||||
|
<button
|
||||||
|
class="group-button"
|
||||||
|
class:active={settingsStore.allDayDisplayMode === 'header'}
|
||||||
|
onclick={() => settingsStore.set('allDayDisplayMode', 'header' as AllDayDisplayMode)}
|
||||||
|
>
|
||||||
|
In Kopfzeile
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="group-button"
|
||||||
|
class:active={settingsStore.allDayDisplayMode === 'block'}
|
||||||
|
onclick={() => settingsStore.set('allDayDisplayMode', 'block' as AllDayDisplayMode)}
|
||||||
|
>
|
||||||
|
Als Tagesblock
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
</SettingsCard>
|
||||||
|
</SettingsSection>
|
||||||
<div class="setting-item">
|
|
||||||
<div class="setting-info">
|
|
||||||
<span class="setting-label">Ganztägige Termine</span>
|
|
||||||
<span class="setting-description">Wie sollen ganztägige Termine angezeigt werden?</span>
|
|
||||||
</div>
|
|
||||||
<div class="button-group">
|
|
||||||
<button
|
|
||||||
class="group-button"
|
|
||||||
class:active={settingsStore.allDayDisplayMode === 'header'}
|
|
||||||
onclick={() => settingsStore.set('allDayDisplayMode', 'header' as AllDayDisplayMode)}
|
|
||||||
>
|
|
||||||
In Kopfzeile
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="group-button"
|
|
||||||
class:active={settingsStore.allDayDisplayMode === 'block'}
|
|
||||||
onclick={() => settingsStore.set('allDayDisplayMode', 'block' as AllDayDisplayMode)}
|
|
||||||
>
|
|
||||||
Als Tagesblock
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Termin-Einstellungen -->
|
<!-- Termin-Einstellungen -->
|
||||||
<section class="settings-section card">
|
<SettingsSection title="Termine">
|
||||||
<h2>Termine</h2>
|
{#snippet icon()}
|
||||||
|
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{/snippet}
|
||||||
|
<SettingsCard>
|
||||||
|
<div class="p-5 space-y-4">
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-info">
|
||||||
|
<span class="setting-label">Standard-Dauer</span>
|
||||||
|
<span class="setting-description">Voreingestellte Dauer für neue Termine</span>
|
||||||
|
</div>
|
||||||
|
<select
|
||||||
|
class="select-input"
|
||||||
|
value={settingsStore.defaultEventDuration}
|
||||||
|
onchange={(e) => handleEventDurationChange(Number(e.currentTarget.value))}
|
||||||
|
>
|
||||||
|
{#each durationOptions as duration}
|
||||||
|
<option value={duration}>
|
||||||
|
{duration >= 60
|
||||||
|
? `${duration / 60} Stunde${duration > 60 ? 'n' : ''}`
|
||||||
|
: `${duration} Minuten`}
|
||||||
|
</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
<div class="setting-info">
|
<div class="setting-info">
|
||||||
<span class="setting-label">Standard-Dauer</span>
|
<span class="setting-label">Standard-Erinnerung</span>
|
||||||
<span class="setting-description">Voreingestellte Dauer für neue Termine</span>
|
<span class="setting-description">Voreingestellte Erinnerung für neue Termine</span>
|
||||||
|
</div>
|
||||||
|
<select
|
||||||
|
class="select-input"
|
||||||
|
value={settingsStore.defaultReminder}
|
||||||
|
onchange={(e) => handleReminderChange(Number(e.currentTarget.value))}
|
||||||
|
>
|
||||||
|
{#each reminderOptions as option}
|
||||||
|
<option value={option.value}>{option.label}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<select
|
</SettingsCard>
|
||||||
class="select-input"
|
</SettingsSection>
|
||||||
value={settingsStore.defaultEventDuration}
|
|
||||||
onchange={(e) => handleEventDurationChange(Number(e.currentTarget.value))}
|
|
||||||
>
|
|
||||||
{#each durationOptions as duration}
|
|
||||||
<option value={duration}>
|
|
||||||
{duration >= 60
|
|
||||||
? `${duration / 60} Stunde${duration > 60 ? 'n' : ''}`
|
|
||||||
: `${duration} Minuten`}
|
|
||||||
</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="setting-info">
|
|
||||||
<span class="setting-label">Standard-Erinnerung</span>
|
|
||||||
<span class="setting-description">Voreingestellte Erinnerung für neue Termine</span>
|
|
||||||
</div>
|
|
||||||
<select
|
|
||||||
class="select-input"
|
|
||||||
value={settingsStore.defaultReminder}
|
|
||||||
onchange={(e) => handleReminderChange(Number(e.currentTarget.value))}
|
|
||||||
>
|
|
||||||
{#each reminderOptions as option}
|
|
||||||
<option value={option.value}>{option.label}</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Konto -->
|
<!-- Konto -->
|
||||||
<section class="settings-section card">
|
<SettingsSection title="Konto">
|
||||||
<h2>Konto</h2>
|
{#snippet icon()}
|
||||||
|
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{/snippet}
|
||||||
|
<SettingsCard>
|
||||||
|
<div class="p-5 space-y-4">
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-info">
|
||||||
|
<span class="setting-label">E-Mail</span>
|
||||||
|
<span class="setting-value">{authStore.user?.email || '-'}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
<div class="setting-info">
|
<button
|
||||||
<span class="setting-label">E-Mail</span>
|
class="btn btn-ghost text-destructive"
|
||||||
<span class="setting-value">{authStore.user?.email || '-'}</span>
|
onclick={() => authStore.signOut().then(() => goto('/login'))}
|
||||||
|
>
|
||||||
|
Abmelden
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</SettingsCard>
|
||||||
|
</SettingsSection>
|
||||||
<div class="setting-item">
|
|
||||||
<button
|
|
||||||
class="btn btn-ghost text-destructive"
|
|
||||||
onclick={() => authStore.signOut().then(() => goto('/login'))}
|
|
||||||
>
|
|
||||||
Abmelden
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
@ -472,16 +591,10 @@
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-section {
|
.calendars-toolbar {
|
||||||
margin-bottom: 1.5rem;
|
display: flex;
|
||||||
}
|
justify-content: flex-end;
|
||||||
|
margin-bottom: 1rem;
|
||||||
.settings-section h2 {
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 600;
|
|
||||||
margin: 0 0 1rem 0;
|
|
||||||
padding-bottom: 0.75rem;
|
|
||||||
border-bottom: 1px solid hsl(var(--color-border));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting-item {
|
.setting-item {
|
||||||
|
|
@ -634,21 +747,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calendar management styles */
|
/* Calendar management styles */
|
||||||
.calendars-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
padding-bottom: 0.75rem;
|
|
||||||
border-bottom: 1px solid hsl(var(--color-border));
|
|
||||||
}
|
|
||||||
|
|
||||||
.calendars-header h2 {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.new-calendar-form {
|
.new-calendar-form {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
|
@ -696,6 +794,168 @@
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Edit form styles */
|
||||||
|
.calendar-edit-form {
|
||||||
|
padding: 1.25rem;
|
||||||
|
background: hsl(var(--color-card));
|
||||||
|
border: 1px solid hsl(var(--color-border));
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
box-shadow: 0 2px 8px hsl(var(--color-foreground) / 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-form-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-form-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-form-group--name {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-form-group--color {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-label {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: hsl(var(--color-foreground));
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.025em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.625rem 0.875rem;
|
||||||
|
font-size: 0.9375rem;
|
||||||
|
color: hsl(var(--color-foreground));
|
||||||
|
background: hsl(var(--color-background));
|
||||||
|
border: 2px solid hsl(var(--color-border));
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
outline: none;
|
||||||
|
transition:
|
||||||
|
border-color 150ms ease,
|
||||||
|
box-shadow 150ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-input:hover {
|
||||||
|
border-color: hsl(var(--color-muted-foreground) / 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-input:focus {
|
||||||
|
border-color: hsl(var(--color-primary));
|
||||||
|
box-shadow: 0 0 0 3px hsl(var(--color-primary) / 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-input::placeholder {
|
||||||
|
color: hsl(var(--color-muted-foreground) / 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-color-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.625rem;
|
||||||
|
padding: 0.375rem 0.625rem 0.375rem 0.375rem;
|
||||||
|
background: hsl(var(--color-background));
|
||||||
|
border: 2px solid hsl(var(--color-border));
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
transition: border-color 150ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-color-wrapper:hover {
|
||||||
|
border-color: hsl(var(--color-muted-foreground) / 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-color-wrapper:focus-within {
|
||||||
|
border-color: hsl(var(--color-primary));
|
||||||
|
box-shadow: 0 0 0 3px hsl(var(--color-primary) / 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-color-input {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
cursor: pointer;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-color-input::-webkit-color-swatch-wrapper {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-color-input::-webkit-color-swatch {
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-color-input::-moz-color-swatch {
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-color-value {
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
font-family: monospace;
|
||||||
|
color: hsl(var(--color-muted-foreground));
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-checkbox {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.75rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
background: hsl(var(--color-muted) / 0.3);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 150ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-checkbox:hover {
|
||||||
|
background: hsl(var(--color-muted) / 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-checkbox input[type='checkbox'] {
|
||||||
|
width: 1.125rem;
|
||||||
|
height: 1.125rem;
|
||||||
|
accent-color: hsl(var(--color-primary));
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-checkbox input[type='checkbox']:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-checkbox-text {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: hsl(var(--color-foreground));
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-checkbox-hint {
|
||||||
|
color: hsl(var(--color-muted-foreground));
|
||||||
|
font-style: italic;
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-form-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding-top: 1rem;
|
||||||
|
border-top: 1px solid hsl(var(--color-border));
|
||||||
|
}
|
||||||
|
|
||||||
.calendar-info {
|
.calendar-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -720,6 +980,12 @@
|
||||||
color: hsl(var(--color-muted-foreground));
|
color: hsl(var(--color-muted-foreground));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.badge-primary {
|
||||||
|
background: hsl(var(--color-primary) / 0.15);
|
||||||
|
color: hsl(var(--color-primary));
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
.calendar-actions {
|
.calendar-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue