refactor(calendar,contacts): replace inline SVGs with Phosphor icons

Migrate all inline SVG icon paths to Phosphor components from
@manacore/shared-icons across 38 files. Only spinners (loading
animations) and brand logos (Google) remain as inline SVGs.

Calendar: 0 inline icon SVGs remaining
Contacts: 6 remaining (3 spinners, 1 spinner, 2 Google logos)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-03-31 12:01:18 +02:00
parent e68e5c6e5f
commit 9f6e463eae
38 changed files with 365 additions and 1717 deletions

View file

@ -14,6 +14,7 @@
import { toDate } from '$lib/utils/eventDateHelpers';
import type { CalendarEvent, Calendar, CreateEventInput } from '@calendar/shared';
import { ContextMenu, type ContextMenuItem } from '@manacore/shared-ui';
import { CalendarBlank, MapPin, CaretRight } from '@manacore/shared-icons';
import { _ } from 'svelte-i18n';
interface Props {
@ -195,14 +196,7 @@
<div class="agenda-view">
{#if groupedEvents.length === 0}
<div class="empty-state">
<svg class="empty-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.5"
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>
<CalendarBlank size={64} class="empty-icon" />
<p>Keine Termine in diesem Zeitraum</p>
</div>
{:else}
@ -256,25 +250,7 @@
</span>
{#if event.location}
<div class="event-location">
<svg
class="location-icon"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
<MapPin size={14} class="location-icon" />
{event.location}
</div>
{/if}
@ -285,14 +261,7 @@
title="Details öffnen"
aria-label="Details öffnen"
>
<svg class="chevron-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
/>
</svg>
<CaretRight size={16} class="chevron-icon" />
</button>
</div>
{/each}

View file

@ -6,6 +6,7 @@
import type { Calendar } from '@calendar/shared';
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
import { Plus, ArrowsClockwise } from '@manacore/shared-icons';
// Get calendars from layout context (live query)
const calendarsCtx: { readonly value: Calendar[] } = getContext('calendars');
@ -33,9 +34,7 @@
<div class="section-header">
<h3 class="section-title">Meine Kalender</h3>
<button class="add-btn" onclick={handleAddCalendar} aria-label="Kalender hinzufügen">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
<Plus size={16} />
</button>
</div>
@ -64,14 +63,7 @@
<div class="section-header external-header">
<h3 class="section-title">Externe Kalender</h3>
<button class="add-btn" onclick={() => goto('/settings/sync')} aria-label="Sync verwalten">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
/>
</svg>
<ArrowsClockwise size={16} />
</button>
</div>

View file

@ -13,6 +13,7 @@
subMonths,
} from 'date-fns';
import { de } from 'date-fns/locale';
import { CaretLeft, CaretRight } from '@manacore/shared-icons';
interface Props {
selectedDate: Date;
@ -51,15 +52,11 @@
<div class="mini-calendar card">
<div class="calendar-header">
<button class="nav-btn" onclick={previousMonth} aria-label="Vorheriger Monat">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
<CaretLeft size={16} />
</button>
<span class="month-label">{format(currentMonth, 'MMMM yyyy', { locale: de })}</span>
<button class="nav-btn" onclick={nextMonth} aria-label="Nächster Monat">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
<CaretRight size={16} />
</button>
</div>

View file

@ -3,6 +3,7 @@
import { calendarsStore } from '$lib/stores/calendars.svelte';
import type { Calendar } from '@calendar/shared';
import { goto } from '$app/navigation';
import { CalendarBlank, CaretDown, Plus } from '@manacore/shared-icons';
// Get calendars from layout context (live query)
const calendarsCtx: { readonly value: Calendar[] } = getContext('calendars');
@ -74,24 +75,9 @@
class:glass-pill={!embedded}
class:embedded-btn={embedded}
>
<svg class="pill-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
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>
<CalendarBlank size={16} class="pill-icon" />
<span class="pill-label">{visibleCount}/{totalCount}</span>
<svg
class="chevron-icon"
class:rotated={isOpen}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
<CaretDown size={12} class="chevron-icon {isOpen ? 'rotated' : ''}" />
</button>
{#if isOpen}
@ -116,14 +102,7 @@
<div class="dropdown-header">
<span class="header-title">Kalender</span>
<button class="add-btn" onclick={handleAddCalendar} aria-label="Kalender hinzufügen">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 4v16m8-8H4"
/>
</svg>
<Plus size={16} />
</button>
</div>

View file

@ -1,5 +1,6 @@
<script lang="ts">
import { getContext, type Snippet } from 'svelte';
import { Stack, X } from '@manacore/shared-icons';
import { QuickInputBar, TagStrip } from '@manacore/shared-ui';
import type { QuickInputItem, CreatePreview } from '@manacore/shared-ui';
import { unifiedBarStore } from '$lib/stores/unified-bar.svelte';
@ -180,20 +181,7 @@
<!-- Layers Menu FAB (only on calendar main page) -->
{#if showCalendarLayers}
<button class="layers-fab" onclick={toggleOverlay} title="Leisten anpassen">
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 2L2 7l10 5 10-5-10-5z" />
<path d="M2 17l10 5 10-5" />
<path d="M2 12l10 5 10-5" />
</svg>
<Stack size={20} />
</button>
{/if}
</div>
@ -208,19 +196,7 @@
<div class="overlay-header">
<h3>Leisten</h3>
<button class="overlay-close-btn" onclick={toggleOverlay} aria-label="Schließen">
<svg
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
<X size={18} weight="bold" />
</button>
</div>

View file

@ -1,5 +1,6 @@
<script lang="ts">
import { viewStore } from '$lib/stores/view.svelte';
import { Columns, CalendarBlank, List } from '@manacore/shared-icons';
import type { CalendarViewType } from '@calendar/shared';
interface Props {
isToolbarExpanded?: boolean;
@ -33,27 +34,11 @@
title={viewTitles[view]}
>
{#if view === 'week'}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-width="2"
d="M3 4v16M6.5 4v16M10 4v16M13.5 4v16M17 4v16M20.5 4v16M24 4v16"
/>
</svg>
<Columns size={18} />
{:else if view === 'month'}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<rect x="3" y="4" width="18" height="16" rx="2" stroke-width="2" />
<path stroke-linecap="round" stroke-width="2" d="M3 9h18M8 4v4M16 4v4" />
<path
stroke-linecap="round"
stroke-width="1.5"
d="M7 13h2M11 13h2M15 13h2M7 17h2M11 17h2"
/>
</svg>
<CalendarBlank size={18} />
{:else if view === 'agenda'}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h12" />
</svg>
<List size={18} />
{/if}
</button>
{/each}

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { viewStore } from '$lib/stores/view.svelte';
import type { CalendarViewType } from '@calendar/shared';
import { SquaresFour } from '@manacore/shared-icons';
interface Props {
/** Bottom offset from viewport bottom (default: '70px') */
bottomOffset?: string;
@ -41,14 +42,7 @@
<div class="views-bar" style="--bottom-offset: {bottomOffset}">
<div class="views-container">
<div class="views-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z"
/>
</svg>
<SquaresFour size={20} />
</div>
<div class="views-buttons">

View file

@ -8,6 +8,21 @@
import RecurrenceEditDialog from './RecurrenceEditDialog.svelte';
import ReminderSelector from './ReminderSelector.svelte';
import { TagBadge, toastStore as toast, focusTrap } from '@manacore/shared-ui';
import {
PencilSimple,
Trash,
X,
Clock,
ArrowsClockwise,
Bell,
MapPin,
VideoCamera,
ArrowSquareOut,
Link,
TextAlignLeft,
Tag,
UsersThree,
} from '@manacore/shared-icons';
import type { CalendarEvent, UpdateEventInput, Reminder } from '@calendar/shared';
import { describeRecurrence, parseRRule } from '@calendar/shared';
import * as api from '$lib/api/events';
@ -215,37 +230,16 @@
<div class="modal-actions">
{#if !isEditing}
<button class="btn btn-ghost" onclick={() => (isEditing = true)}>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"
/>
</svg>
<PencilSimple size={16} />
Bearbeiten
</button>
<button class="btn btn-ghost text-destructive" onclick={handleDelete}>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
<Trash size={16} />
Löschen
</button>
{/if}
<button class="btn btn-ghost btn-close" onclick={onClose} aria-label="Schließen">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<X size={20} />
</button>
</div>
</div>
@ -271,14 +265,7 @@
<!-- Zeit -->
<div class="detail-row">
<span class="detail-icon">
<svg class="w-5 h-5" 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>
<Clock size={20} />
</span>
<div class="detail-content">
<span class="detail-label">Zeit</span>
@ -290,14 +277,7 @@
{#if event.recurrenceRule}
<div class="detail-row">
<span class="detail-icon">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
/>
</svg>
<ArrowsClockwise size={20} />
</span>
<div class="detail-content">
<span class="detail-label">Wiederholung</span>
@ -309,14 +289,7 @@
<!-- Erinnerungen -->
<div class="detail-row">
<span class="detail-icon">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"
/>
</svg>
<Bell size={20} />
</span>
<div class="detail-content" style="flex: 1;">
<ReminderSelector
@ -336,20 +309,7 @@
{#if event.location || event.metadata?.locationDetails}
<div class="detail-row">
<span class="detail-icon">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
<MapPin size={20} />
</span>
<div class="detail-content">
<span class="detail-label">Ort</span>
@ -380,14 +340,7 @@
{#if event.metadata?.conferenceUrl}
<div class="detail-row">
<span class="detail-icon">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"
/>
</svg>
<VideoCamera size={20} />
</span>
<div class="detail-content">
<span class="detail-label">Videokonferenz</span>
@ -398,14 +351,7 @@
class="detail-link"
>
Beitreten
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
/>
</svg>
<ArrowSquareOut size={14} />
</a>
</div>
</div>
@ -415,14 +361,7 @@
{#if event.metadata?.url}
<div class="detail-row">
<span class="detail-icon">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"
/>
</svg>
<Link size={20} />
</span>
<div class="detail-content">
<span class="detail-label">Link</span>
@ -433,14 +372,7 @@
class="detail-link"
>
{new URL(event.metadata.url).hostname}
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
/>
</svg>
<ArrowSquareOut size={14} />
</a>
</div>
</div>
@ -450,14 +382,7 @@
{#if event.description}
<div class="detail-row">
<span class="detail-icon">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 6h16M4 12h16M4 18h7"
/>
</svg>
<TextAlignLeft size={20} />
</span>
<div class="detail-content">
<span class="detail-label">Beschreibung</span>
@ -470,14 +395,7 @@
{#if event.tags && event.tags.length > 0}
<div class="detail-row">
<span class="detail-icon">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"
/>
</svg>
<Tag size={20} />
</span>
<div class="detail-content">
<span class="detail-label">Tags</span>
@ -494,14 +412,7 @@
{#if event.metadata?.attendees && event.metadata.attendees.length > 0}
<div class="detail-row">
<span class="detail-icon">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"
/>
</svg>
<UsersThree size={20} />
</span>
<div class="detail-content">
<span class="detail-label">Teilnehmer ({event.metadata.attendees.length})</span>

View file

@ -8,6 +8,7 @@
// Live data from layout context
const calendarsCtx: { readonly value: Calendar[] } = getContext('calendars');
const tagsCtx: { readonly value: SharedTag[] } = getContext('tags');
import { CaretRight } from '@manacore/shared-icons';
import {
TagSelector,
FilterDropdown,
@ -391,16 +392,7 @@
aria-expanded={showLocationDetails}
aria-controls="location-details"
>
<svg
class="w-4 h-4 transition-transform"
class:rotate-90={showLocationDetails}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
<CaretRight size={16} class="transition-transform {showLocationDetails ? 'rotate-90' : ''}" />
{showLocationDetails ? 'Adressdetails ausblenden' : 'Adressdetails hinzufügen'}
</button>

View file

@ -22,7 +22,17 @@
toastStore as toast,
type FilterDropdownOption,
} from '@manacore/shared-ui';
import { Users } from '@manacore/shared-icons';
import {
Users,
Trash,
X,
Clock,
CalendarBlank,
ArrowsClockwise,
MapPin,
CaretRight,
TextAlignLeft,
} from '@manacore/shared-icons';
import { format, addMinutes } from 'date-fns';
import { de } from 'date-fns/locale';
import { toDate } from '$lib/utils/eventDateHelpers';
@ -867,26 +877,12 @@
placement="bottom"
>
<button type="button" class="delete-btn" disabled={submitting} aria-label="Löschen">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
<Trash size={16} />
</button>
</ConfirmationPopover>
{/if}
<button type="button" class="close-btn" onclick={onClose} aria-label="Schließen">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<X size={16} />
</button>
</div>
</div>
@ -928,14 +924,7 @@
<!-- Time display under title -->
<div class="time-display">
<svg class="icon" 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>
<Clock class="icon" size={18} />
<span>
{format(draftStart(), 'EEEE, d. MMMM yyyy', { locale: de })}
{#if !isAllDay}
@ -1078,14 +1067,7 @@
aria-label="Ganztägig"
>
<div class="row-icon">
<svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
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>
<CalendarBlank class="icon" size={18} />
</div>
<div class="row-content toggle-content">
<span>Ganztägig</span>
@ -1119,14 +1101,7 @@
<!-- Recurrence -->
<div class="form-row">
<div class="row-icon">
<svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
/>
</svg>
<ArrowsClockwise class="icon" size={18} />
</div>
<div class="row-content">
<RecurrenceSelector
@ -1143,14 +1118,7 @@
<!-- Start date/time -->
<div class="form-row">
<div class="row-icon">
<svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
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>
<CalendarBlank class="icon" size={18} />
</div>
<div class="row-content datetime-row">
<div class="datetime-field">
@ -1181,20 +1149,7 @@
<!-- End date/time -->
<div class="form-row">
<div class="row-icon">
<svg
class="icon"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
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>
<CalendarBlank class="icon" size={18} />
</div>
<div class="row-content datetime-row">
<div class="datetime-field">
@ -1225,20 +1180,7 @@
<!-- Location -->
<div class="form-row">
<div class="row-icon">
<svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
<MapPin class="icon" size={18} />
</div>
<div class="row-content">
<input
@ -1255,20 +1197,7 @@
onclick={() => (showLocationDetails = !showLocationDetails)}
aria-expanded={showLocationDetails}
>
<svg
class="toggle-chevron"
class:rotated={showLocationDetails}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
/>
</svg>
<CaretRight class="toggle-chevron {showLocationDetails ? 'rotated' : ''}" size={14} />
{showLocationDetails ? 'Adressdetails ausblenden' : 'Adressdetails hinzufügen'}
</button>
</div>
@ -1324,14 +1253,7 @@
<!-- Description -->
<div class="form-row">
<div class="row-icon">
<svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 6h16M4 12h16M4 18h7"
/>
</svg>
<TextAlignLeft class="icon" size={18} />
</div>
<div class="row-content">
<textarea

View file

@ -2,7 +2,7 @@
import type { ResponsiblePerson } from '@calendar/shared';
import type { ContactSummary, ContactOrManual, ManualContactEntry } from '@manacore/shared-types';
import { ContactSelector, ContactAvatar } from '@manacore/shared-ui';
import { X, ArrowSquareOut } from '@manacore/shared-icons';
import { X, ArrowSquareOut, User } from '@manacore/shared-icons';
import { contactsStore } from '$lib/stores/contacts.svelte';
interface Props {
@ -196,14 +196,7 @@
"
{disabled}
>
<svg class="w-4 h-4" 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>
<User size={16} />
Verantwortliche Person hinzufügen
</button>
{/if}

View file

@ -16,7 +16,16 @@
FilterDropdown,
type FilterDropdownOption,
} from '@manacore/shared-ui';
import { X } from '@manacore/shared-icons';
import {
X,
CalendarBlank,
Plus,
Eye,
Clock,
Microphone,
Cake,
User,
} from '@manacore/shared-icons';
import { focusTrap } from '@manacore/shared-ui';
import type { CalendarViewType, Calendar } from '@calendar/shared';
@ -237,14 +246,7 @@
<!-- Meine Kalender -->
<SettingsSection title="Meine Kalender">
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
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>
<CalendarBlank size={24} />
{/snippet}
<SettingsCard>
<div class="p-4">
@ -253,14 +255,7 @@
class="inline-flex items-center gap-2 px-3 py-1.5 text-sm font-medium rounded-lg transition-colors bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] hover:bg-[hsl(var(--primary))]/90"
onclick={() => (showNewCalendarForm = true)}
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 4v16m8-8H4"
/>
</svg>
<Plus size={16} />
Neuer Kalender
</button>
</div>
@ -409,20 +404,7 @@
<!-- Kalender-Ansicht -->
<SettingsSection title="Kalender-Ansicht">
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
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"
/>
</svg>
<Eye size={24} />
{/snippet}
<SettingsCard>
<div class="p-4 space-y-3">
@ -577,14 +559,7 @@
<!-- Termin-Einstellungen -->
<SettingsSection title="Termine">
{#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>
<Clock size={24} />
{/snippet}
<SettingsCard>
<div class="p-4 space-y-3">
@ -620,14 +595,7 @@
<!-- Spracheingabe -->
<SettingsSection title="Spracheingabe">
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z"
/>
</svg>
<Microphone size={24} />
{/snippet}
<SettingsCard>
<div class="p-4 space-y-3">
@ -662,14 +630,7 @@
<!-- Geburtstage -->
<SettingsSection title="Geburtstage">
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M21 15.546c-.523 0-1.046.151-1.5.454a2.704 2.704 0 01-3 0 2.704 2.704 0 00-3 0 2.704 2.704 0 01-3 0 2.704 2.704 0 00-3 0 2.704 2.704 0 01-3 0 2.701 2.701 0 00-1.5-.454M9 6v2m3-2v2m3-2v2M9 3h.01M12 3h.01M15 3h.01M21 21v-7a2 2 0 00-2-2H5a2 2 0 00-2 2v7h18zm-3-9v-2a2 2 0 00-2-2H8a2 2 0 00-2 2v2h12z"
/>
</svg>
<Cake size={24} />
{/snippet}
<SettingsCard>
<div class="p-4 space-y-3">
@ -714,14 +675,7 @@
<!-- Konto -->
<SettingsSection title="Konto">
{#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>
<User size={24} />
{/snippet}
<SettingsCard>
<div class="p-4 space-y-3">

View file

@ -1,5 +1,6 @@
<script lang="ts">
import { voiceRecordingStore } from '$lib/stores/voice-recording.svelte';
import { Microphone } from '@manacore/shared-icons';
interface Props {
/** Called when voice recording completes with transcription */
@ -79,20 +80,7 @@
<div class="recording-dot"></div>
{:else}
<!-- Microphone icon -->
<svg
class="mic-icon"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z"
/>
</svg>
<Microphone class="mic-icon" />
{/if}
</button>

View file

@ -2,6 +2,7 @@
import { voiceRecordingStore } from '$lib/stores/voice-recording.svelte';
import { fade, scale } from 'svelte/transition';
import { focusTrap } from '@manacore/shared-ui';
import { WarningCircle, X, Check } from '@manacore/shared-icons';
interface Props {
/** Called when recording completes with transcription */
@ -87,14 +88,7 @@
<!-- Error state -->
<div class="modal-content error">
<div class="error-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<WarningCircle size={48} />
</div>
<p class="error-message">{errorMessage}</p>
<button class="btn btn-primary" onclick={handleCancel}>Schließen</button>
@ -120,14 +114,7 @@
<!-- Controls -->
<div class="controls">
<button class="btn btn-icon btn-cancel" onclick={handleCancel} title="Abbrechen (Esc)">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<X size={24} />
</button>
<button
@ -135,14 +122,7 @@
onclick={handleStop}
title="Aufnahme beenden (Enter)"
>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 13l4 4L19 7"
/>
</svg>
<Check size={24} />
</button>
</div>

View file

@ -14,6 +14,7 @@
import type { CalendarEvent, Calendar } from '@calendar/shared';
import { addMinutes } from 'date-fns';
import { browser } from '$app/environment';
import { CaretDoubleLeft } from '@manacore/shared-icons';
// Get calendars from layout context (live query)
const calendarsCtx: { readonly value: Calendar[] } = getContext('calendars');
@ -186,14 +187,7 @@
onclick={() => settingsStore.toggleSidebar()}
title={$_('calendar.hideSidebar')}
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M11 19l-7-7 7-7m8 14l-7-7 7-7"
/>
</svg>
<CaretDoubleLeft size={16} />
</button>
<TodoSidebarSection maxItems={5} />

View file

@ -19,6 +19,17 @@
} from '@manacore/shared-ui';
import { APP_VERSION } from '$lib/version';
import type { CalendarViewType, Calendar } from '@calendar/shared';
import {
CalendarBlank,
Plus,
ArrowsClockwise,
UsersThree,
Eye,
Clock,
Cake,
User,
Microphone,
} from '@manacore/shared-icons';
// Get calendars from layout context (live query)
const calendarsCtx: { readonly value: Calendar[] } = getContext('calendars');
@ -210,14 +221,7 @@
<!-- Meine Kalender -->
<SettingsSection title={$_('settings.myCalendars')}>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
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>
<CalendarBlank size={24} />
{/snippet}
<SettingsCard>
<div class="p-5">
@ -226,14 +230,7 @@
class="inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg transition-colors bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] hover:bg-[hsl(var(--primary))]/90"
onclick={() => (showNewCalendarForm = true)}
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 4v16m8-8H4"
/>
</svg>
<Plus size={16} />
{$_('settings.newCalendar')}
</button>
</div>
@ -370,14 +367,7 @@
<!-- Externe Kalender -->
<SettingsSection title={$_('settings.externalCalendars')}>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
/>
</svg>
<ArrowsClockwise size={24} />
{/snippet}
<SettingsCard>
<div class="flex flex-col gap-3">
@ -397,14 +387,7 @@
<!-- Kalender-Freigaben -->
<SettingsSection title={$_('settings.shares')}>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
/>
</svg>
<UsersThree size={24} />
{/snippet}
<SettingsCard>
<div class="flex flex-col gap-3">
@ -432,20 +415,7 @@
<!-- Kalender-Ansicht -->
<SettingsSection title={$_('settings.calendarView')}>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
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"
/>
</svg>
<Eye size={24} />
{/snippet}
<SettingsCard>
<div class="p-5 space-y-4">
@ -588,14 +558,7 @@
<!-- Termin-Einstellungen -->
<SettingsSection title={$_('settings.events')}>
{#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>
<Clock size={24} />
{/snippet}
<SettingsCard>
<div class="p-5 space-y-4">
@ -648,14 +611,7 @@
<!-- Geburtstage -->
<SettingsSection title={$_('settings.birthdays')}>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M21 15.546c-.523 0-1.046.151-1.5.454a2.704 2.704 0 01-3 0 2.704 2.704 0 00-3 0 2.704 2.704 0 01-3 0 2.704 2.704 0 00-3 0 2.704 2.704 0 01-3 0 2.701 2.701 0 00-1.5-.454M9 6v2m3-2v2m3-2v2M9 3h.01M12 3h.01M15 3h.01M21 21v-7a2 2 0 00-2-2H5a2 2 0 00-2 2v7h18zm-3-9v-2a2 2 0 00-2-2H8a2 2 0 00-2 2v2h12z"
/>
</svg>
<Cake size={24} />
{/snippet}
<SettingsCard>
<div class="p-5 space-y-4">
@ -696,14 +652,7 @@
<!-- Konto -->
<SettingsSection title={$_('settings.account')}>
{#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>
<User size={24} />
{/snippet}
<SettingsCard>
<div class="p-5 space-y-4">

View file

@ -8,6 +8,23 @@
import SocialMediaFields from './forms/SocialMediaFields.svelte';
import DateFields from './forms/DateFields.svelte';
import SocialMediaLinks from './SocialMediaLinks.svelte';
import {
X,
PencilSimple,
Trash,
Warning,
Camera,
Heart,
Phone,
Envelope,
ChatCircle,
User,
Briefcase,
MapPin,
CalendarBlank,
Check,
Note,
} from '@manacore/shared-icons';
interface Props {
contactId: string;
@ -288,14 +305,7 @@
<!-- Header -->
<header class="modal-header">
<button onclick={onClose} class="back-button" aria-label="Schließen">
<svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<X size={24} class="icon" />
</button>
<h1 id="modal-title" class="title">{editing ? 'Bearbeiten' : 'Kontakt'}</h1>
{#if contact && !editing && !loading}
@ -308,14 +318,7 @@
class="action-btn"
aria-label="Bearbeiten"
>
<svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
/>
</svg>
<PencilSimple size={24} class="icon" />
</button>
{#if !contact?.isSelf}
<button
@ -324,14 +327,7 @@
class="action-btn action-btn-danger"
aria-label="Löschen"
>
<svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
<Trash size={24} class="icon" />
</button>
{/if}
</div>
@ -347,14 +343,7 @@
{:else if error && !contact}
<div class="error-container">
<div class="error-icon-wrapper">
<svg class="error-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<Warning size={48} class="error-icon" />
</div>
<p class="error-text">{error}</p>
<button onclick={onClose} class="btn btn-secondary">Schließen</button>
@ -362,14 +351,7 @@
{:else if contact}
{#if error}
<div class="error-banner" role="alert">
<svg class="icon-sm" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<Warning size={20} class="icon-sm" />
<span>{error}</span>
</div>
{/if}
@ -399,14 +381,7 @@
<section class="form-section">
<div class="section-header">
<div class="section-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>
<User size={20} />
</div>
<h2 class="section-title">Name</h2>
</div>
@ -426,14 +401,7 @@
<section class="form-section">
<div class="section-header">
<div class="section-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
/>
</svg>
<Envelope size={20} />
</div>
<h2 class="section-title">Kontakt</h2>
</div>
@ -457,14 +425,7 @@
<section class="form-section">
<div class="section-header">
<div class="section-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v2m4 6h.01M5 20h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
/>
</svg>
<Briefcase size={20} />
</div>
<h2 class="section-title">Arbeit</h2>
</div>
@ -482,20 +443,7 @@
<section class="form-section">
<div class="section-header">
<div class="section-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
<MapPin size={20} />
</div>
<h2 class="section-title">Adresse</h2>
</div>
@ -523,14 +471,7 @@
<section class="form-section">
<div class="section-header">
<div class="section-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
/>
</svg>
<PencilSimple size={20} />
</div>
<h2 class="section-title">Notizen</h2>
</div>
@ -588,14 +529,7 @@
</svg>
<span>Speichern...</span>
{:else}
<svg class="icon-sm" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 13l4 4L19 7"
/>
</svg>
<Check size={16} class="icon-sm" />
<span>Speichern</span>
{/if}
</button>
@ -645,14 +579,7 @@
/>
</svg>
{:else}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<X size={16} />
{/if}
</button>
{:else}
@ -683,20 +610,7 @@
{:else}
<span class="avatar-initials">{initials()}</span>
<span class="avatar-upload-overlay">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 13a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
<Camera size={24} />
</span>
{/if}
</button>
@ -709,20 +623,9 @@
: 'Zu Favoriten hinzufügen'}
>
{#if contact.isFavorite}
<svg class="favorite-icon favorite-active" viewBox="0 0 24 24">
<path
d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"
/>
</svg>
<Heart size={24} weight="fill" class="favorite-icon favorite-active" />
{:else}
<svg class="favorite-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
/>
</svg>
<Heart size={24} class="favorite-icon" />
{/if}
</button>
</div>
@ -739,14 +642,7 @@
{#if contact.phone}
<a href="tel:{contact.phone}" class="quick-action">
<div class="quick-action-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"
/>
</svg>
<Phone size={24} />
</div>
<span>Anrufen</span>
</a>
@ -754,14 +650,7 @@
{#if contact.email}
<a href="mailto:{contact.email}" class="quick-action">
<div class="quick-action-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
/>
</svg>
<Envelope size={24} />
</div>
<span>E-Mail</span>
</a>
@ -769,14 +658,7 @@
{#if contact.mobile}
<a href="sms:{contact.mobile}" class="quick-action">
<div class="quick-action-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
/>
</svg>
<ChatCircle size={24} />
</div>
<span>Nachricht</span>
</a>
@ -789,14 +671,7 @@
<section class="detail-section">
<div class="section-header">
<div class="section-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
/>
</svg>
<Envelope size={20} />
</div>
<h3 class="section-title">Kontakt</h3>
</div>
@ -833,14 +708,7 @@
<section class="detail-section">
<div class="section-header">
<div class="section-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v2m4 6h.01M5 20h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
/>
</svg>
<Briefcase size={20} />
</div>
<h3 class="section-title">Arbeit</h3>
</div>
@ -869,20 +737,7 @@
<section class="detail-section">
<div class="section-header">
<div class="section-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
<MapPin size={20} />
</div>
<h3 class="section-title">Adresse</h3>
</div>
@ -902,14 +757,7 @@
<section class="detail-section">
<div class="section-header">
<div class="section-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
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>
<CalendarBlank size={20} />
</div>
<h3 class="section-title">Daten</h3>
</div>
@ -952,14 +800,7 @@
<section class="detail-section">
<div class="section-header">
<div class="section-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
/>
</svg>
<Note size={20} />
</div>
<h3 class="section-title">Notizen</h3>
</div>

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { getContext } from 'svelte';
import { _ } from 'svelte-i18n';
import { Heart, Archive, Trash } from '@manacore/shared-icons';
import { contactsStore } from '$lib/stores/contacts.svelte';
import { viewModeStore } from '$lib/stores/view-mode.svelte';
import { contactsFilterStore } from '$lib/stores/filter.svelte';
@ -261,14 +262,7 @@
class="batch-btn"
title="Zu Favoriten hinzufügen"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
/>
</svg>
<Heart size={20} />
<span class="hidden sm:inline">Favoriten</span>
</button>
<button
@ -278,14 +272,7 @@
class="batch-btn"
title="Archivieren"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"
/>
</svg>
<Archive size={20} />
<span class="hidden sm:inline">Archivieren</span>
</button>
<button
@ -295,14 +282,7 @@
class="batch-btn batch-btn-danger"
title="Löschen"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
<Trash size={20} />
<span class="hidden sm:inline">Löschen</span>
</button>
</div>

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { onMount } from 'svelte';
import { _ } from 'svelte-i18n';
import { NotePencil, Plus, PushPin, PencilSimple, Trash } from '@manacore/shared-icons';
import { notesApi, type ContactNote } from '$lib/api/contacts';
import { ContactNotesSkeleton } from '$lib/components/skeletons';
@ -137,14 +138,7 @@
<section class="notes-section">
<div class="section-header">
<div class="section-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
<NotePencil size={16} />
</div>
<h3 class="section-title">{$_('notes.title')}</h3>
<button
@ -152,9 +146,7 @@
class="add-note-btn"
aria-label={$_('notes.add')}
>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
<Plus size={16} />
</button>
</div>
@ -233,9 +225,7 @@
<div class="note-content">
{#if note.isPinned}
<span class="pin-badge">
<svg fill="currentColor" viewBox="0 0 24 24">
<path d="M16 12V4h1V2H7v2h1v8l-2 2v2h5.2v6h1.6v-6H18v-2l-2-2z" />
</svg>
<PushPin size={14} weight="fill" />
</span>
{/if}
<p class="note-text">{note.content}</p>
@ -249,18 +239,7 @@
aria-label={note.isPinned ? $_('notes.unpin') : $_('notes.pin')}
title={note.isPinned ? $_('notes.unpin') : $_('notes.pin')}
>
<svg
fill={note.isPinned ? 'currentColor' : 'none'}
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M16 12V4h1V2H7v2h1v8l-2 2v2h5.2v6h1.6v-6H18v-2l-2-2z"
/>
</svg>
<PushPin size={14} weight={note.isPinned ? 'fill' : 'regular'} />
</button>
<button
onclick={() => startEditing(note)}
@ -268,14 +247,7 @@
aria-label={$_('actions.edit')}
title={$_('actions.edit')}
>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
/>
</svg>
<PencilSimple size={14} />
</button>
<button
onclick={() => handleDelete(note.id)}
@ -283,14 +255,7 @@
aria-label={$_('actions.delete')}
title={$_('actions.delete')}
>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
<Trash size={14} />
</button>
</div>
{/if}

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { onMount } from 'svelte';
import { _ } from 'svelte-i18n';
import { ClipboardText, Warning, Check } from '@manacore/shared-icons';
import { todosStore } from '$lib/stores/todos.svelte';
import { PRIORITY_COLORS, type Task } from '$lib/api/todos';
@ -100,14 +101,7 @@
<section class="tasks-section">
<div class="section-header">
<div class="section-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"
/>
</svg>
<ClipboardText size={16} />
</div>
<h3 class="section-title">{$_('contact.tasks.title')}</h3>
<label class="show-completed-toggle">
@ -126,14 +120,7 @@
</div>
{:else if !todosStore.serviceAvailable}
<div class="service-unavailable">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<Warning size={24} />
<p>{$_('contact.tasks.serviceUnavailable')}</p>
</div>
{:else if totalTasks === 0}
@ -161,9 +148,7 @@
style="--priority-color: {PRIORITY_COLORS[task.priority]}"
>
{#if task.isCompleted}
<svg fill="currentColor" viewBox="0 0 24 24">
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z" />
</svg>
<Check size={12} weight="bold" class="text-white" />
{/if}
</button>
<div class="task-content">
@ -209,9 +194,7 @@
style="--priority-color: {PRIORITY_COLORS[task.priority]}"
>
{#if task.isCompleted}
<svg fill="currentColor" viewBox="0 0 24 24">
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z" />
</svg>
<Check size={12} weight="bold" class="text-white" />
{/if}
</button>
<div class="task-content">

View file

@ -5,6 +5,7 @@
import { contactsFilterStore } from '$lib/stores/filter.svelte';
import { viewModeStore } from '$lib/stores/view-mode.svelte';
import type { Contact } from '$lib/api/contacts';
import { SquaresFour, SortAscending } from '@manacore/shared-icons';
interface Props {
contacts: Contact[];
@ -39,14 +40,7 @@
onclick={() => viewModeStore.setMode('grid')}
title={$_('views.grid')}
>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 5a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM14 5a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1V5zM4 15a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1H5a1 1 0 01-1-1v-4zM14 15a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1v-4z"
/>
</svg>
<SquaresFour />
</button>
<button
type="button"
@ -55,14 +49,7 @@
onclick={() => viewModeStore.setMode('alphabet')}
title={$_('views.alphabet')}
>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 4h13M3 8h9m-9 4h6m4 0l4-4m0 0l4 4m-4-4v12"
/>
</svg>
<SortAscending />
</button>
</div>

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { _ } from 'svelte-i18n';
import { onMount } from 'svelte';
import { Funnel, X } from '@manacore/shared-icons';
import { fly } from 'svelte/transition';
import { tagsApi, type ContactTag, type Contact } from '$lib/api/contacts';
@ -125,14 +126,7 @@
onclick={toggleFilters}
title={$_('filters.title')}
>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"
/>
</svg>
<Funnel size={16} />
{#if activeFilterCount > 0}
<span class="filter-badge-embedded">{activeFilterCount}</span>
{/if}
@ -236,14 +230,7 @@
class:active={showFilters || activeFilterCount > 0}
onclick={() => (showFilters = !showFilters)}
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"
/>
</svg>
<Funnel size={16} />
<span>{$_('filters.title')}</span>
{#if activeFilterCount > 0}
<span class="filter-badge">{activeFilterCount}</span>
@ -259,54 +246,26 @@
<button type="button" class="filter-pill" onclick={() => onTagChange(null)}>
<span class="pill-color" style="background: {tag.color || '#6366f1'}"></span>
{tag.name}
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<X size={12} />
</button>
{/if}
{/if}
{#if contactFilter !== 'all' && contactFilter !== 'favorites'}
<button type="button" class="filter-pill" onclick={() => onContactFilterChange('all')}>
{$_(`filters.contact.${contactFilter}`)}
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<X size={12} />
</button>
{/if}
{#if birthdayFilter !== 'all'}
<button type="button" class="filter-pill" onclick={() => onBirthdayFilterChange('all')}>
{$_(`filters.birthday.${birthdayFilter}`)}
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<X size={12} />
</button>
{/if}
{#if selectedCompany}
<button type="button" class="filter-pill" onclick={() => onCompanyChange(null)}>
{selectedCompany}
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<X size={12} />
</button>
{/if}
<button type="button" class="clear-all-btn" onclick={clearAllFilters}>

View file

@ -2,6 +2,7 @@
import { locale } from 'svelte-i18n';
import { setLocale, supportedLocales } from '$lib/i18n';
import type { SupportedLocale } from '$lib/i18n';
import { CaretDown } from '@manacore/shared-icons';
const languageLabels: Record<SupportedLocale, string> = {
de: 'Deutsch',
@ -25,9 +26,7 @@
class="flex items-center gap-2 px-3 py-2 text-sm font-medium text-muted-foreground hover:text-foreground transition-colors"
>
{languageLabels[$locale as SupportedLocale] || 'Language'}
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
<CaretDown size={16} />
</button>
{#if isOpen}

View file

@ -8,6 +8,17 @@
import { parseContactInput, formatParsedContactPreview } from '$lib/utils/contact-parser';
import { findDuplicates, type DuplicateMatch } from '$lib/utils/duplicate-detector';
import { contactCollection } from '$lib/data/local-store';
import {
X,
Warning,
Camera,
User,
Envelope,
Briefcase,
MapPin,
PencilSimple,
Check,
} from '@manacore/shared-icons';
interface Props {
onClose: () => void;
@ -309,14 +320,7 @@
<!-- Header -->
<header class="modal-header">
<button onclick={onClose} class="back-button" aria-label="Schließen">
<svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<X size={24} class="icon" />
</button>
<h1 id="modal-title" class="title">Neuer Kontakt</h1>
<div class="header-spacer"></div>
@ -343,14 +347,7 @@
<!-- Duplicate Warning -->
{#if duplicates.length > 0}
<div class="duplicate-warning" role="alert">
<svg class="icon-sm" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<Warning size={20} class="icon-sm" />
<div class="duplicate-info">
<span class="duplicate-label">Mögliches Duplikat:</span>
{#each duplicates.slice(0, 3) as dup}
@ -367,14 +364,7 @@
{#if error}
<div class="error-banner" role="alert">
<svg class="icon-sm" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<Warning size={20} class="icon-sm" />
<span>{error}</span>
</div>
{/if}
@ -398,37 +388,11 @@
{#if photoPreview}
<img src={photoPreview} alt="Vorschau" class="avatar-image" />
<div class="avatar-overlay">
<svg class="camera-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 13a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
<Camera size={24} class="camera-icon" />
</div>
{:else}
<div class="avatar-circle empty">
<svg class="add-photo-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.5"
d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.5"
d="M15 13a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
<Camera size={24} class="add-photo-icon" />
</div>
{/if}
</button>
@ -439,14 +403,7 @@
onclick={removePhoto}
title="Foto entfernen"
>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<X size={16} />
</button>
{/if}
</div>
@ -467,14 +424,7 @@
<section class="form-section">
<div class="section-header">
<div class="section-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>
<User size={20} />
</div>
<h2 class="section-title">Name</h2>
</div>
@ -507,14 +457,7 @@
<section class="form-section">
<div class="section-header">
<div class="section-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
/>
</svg>
<Envelope size={20} />
</div>
<h2 class="section-title">Kontakt</h2>
</div>
@ -556,14 +499,7 @@
<section class="form-section">
<div class="section-header">
<div class="section-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M21 13.255A23.931 23.931 0 0112 15c-3.183 0-6.22-.62-9-1.745M16 6V4a2 2 0 00-2-2h-4a2 2 0 00-2 2v2m4 6h.01M5 20h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
/>
</svg>
<Briefcase size={20} />
</div>
<h2 class="section-title">Arbeit</h2>
</div>
@ -593,20 +529,7 @@
<section class="form-section">
<div class="section-header">
<div class="section-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
<MapPin size={20} />
</div>
<h2 class="section-title">Adresse</h2>
</div>
@ -652,14 +575,7 @@
<section class="form-section">
<div class="section-header">
<div class="section-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
/>
</svg>
<PencilSimple size={20} />
</div>
<h2 class="section-title">Notizen</h2>
</div>
@ -714,14 +630,7 @@
</svg>
<span>Speichern...</span>
{:else}
<svg class="icon-sm" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 13l4 4L19 7"
/>
</svg>
<Check size={20} class="icon-sm" />
<span>Kontakt erstellen</span>
{/if}
</button>

View file

@ -3,6 +3,7 @@
import { contactsApi, type Contact } from '$lib/api/contacts';
import { newContactModalStore } from '$lib/stores/new-contact-modal.svelte';
import { ContactsEvents } from '@manacore/shared-utils/analytics';
import { MagnifyingGlass, Heart, Plus, Tag, Upload } from '@manacore/shared-icons';
interface Props {
open: boolean;
@ -123,14 +124,7 @@
<div class="search-modal">
<!-- Search Input -->
<div class="search-input-wrapper">
<svg class="search-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
/>
</svg>
<MagnifyingGlass class="search-icon" />
<input
bind:this={inputElement}
type="text"
@ -186,11 +180,7 @@
</div>
</div>
{#if contact.isFavorite}
<svg class="result-favorite" fill="currentColor" viewBox="0 0 24 24">
<path
d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"
/>
</svg>
<Heart class="result-favorite" weight="fill" />
{/if}
</button>
{/each}
@ -207,37 +197,16 @@
newContactModalStore.open();
}}
>
<svg class="quick-action-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 4v16m8-8H4"
/>
</svg>
<Plus size={20} class="quick-action-icon" />
<span>Neuen Kontakt erstellen</span>
<kbd>N</kbd>
</button>
<a href="/tags" class="quick-action" onclick={onClose}>
<svg class="quick-action-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"
/>
</svg>
<Tag size={20} class="quick-action-icon" />
<span>Tags verwalten</span>
</a>
<a href="/data?tab=import" class="quick-action" onclick={onClose}>
<svg class="quick-action-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"
/>
</svg>
<Upload size={20} class="quick-action-icon" />
<span>Kontakte importieren</span>
</a>
</div>

View file

@ -6,6 +6,7 @@
import { getSocialMediaEntries, hasSocialMedia } from '$lib/config/social-media';
import type { Contact } from '$lib/api/contacts';
import { ChatCircle } from '@manacore/shared-icons';
interface Props {
contact: Contact;
@ -21,14 +22,7 @@
<section class="detail-section">
<div class="section-header">
<div class="section-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z"
/>
</svg>
<ChatCircle size={16} />
</div>
<h3 class="section-title">Social Media</h3>
</div>

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { _ } from 'svelte-i18n';
import { viewModeStore, type ViewMode } from '$lib/stores/view-mode.svelte';
import { SquaresFour, SortAscending } from '@manacore/shared-icons';
const modes: { id: ViewMode; icon: string; label: string }[] = [
{ id: 'alphabet', icon: 'alphabet', label: 'views.alphabet' },
@ -18,23 +19,9 @@
title={$_(mode.label)}
>
{#if mode.icon === 'grid'}
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 5a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM14 5a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1V5zM4 15a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1H5a1 1 0 01-1-1v-4zM14 15a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1v-4z"
/>
</svg>
<SquaresFour size={20} />
{:else if mode.icon === 'alphabet'}
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 4h13M3 8h9m-9 4h6m4 0l4-4m0 0l4 4m-4-4v12"
/>
</svg>
<SortAscending size={20} />
{/if}
</button>
{/each}

View file

@ -2,6 +2,7 @@
import { _ } from 'svelte-i18n';
import { exportApi, type ExportFormat } from '$lib/api/export';
import { ContactsEvents } from '@manacore/shared-utils/analytics';
import { X, AddressBook, ChartBar, Upload } from '@manacore/shared-icons';
interface Props {
isOpen: boolean;
@ -65,14 +66,7 @@
onclick={onClose}
class="text-muted-foreground hover:text-foreground transition-colors"
>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<X size={24} />
</button>
</div>
@ -105,19 +99,7 @@
: 'border-border hover:border-muted-foreground'}"
>
<div class="flex items-center gap-3">
<svg
class="w-8 h-8 text-primary"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 6H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V8a2 2 0 00-2-2h-5m-4 0V5a2 2 0 114 0v1m-4 0a2 2 0 104 0m-5 8a2 2 0 100-4 2 2 0 000 4zm0 0c1.306 0 2.417.835 2.83 2M9 14a3.001 3.001 0 00-2.83 2M15 11h3m-3 4h2"
/>
</svg>
<AddressBook size={32} class="text-primary" />
<div>
<div class="font-medium text-foreground">vCard</div>
<div class="text-xs text-muted-foreground">.vcf</div>
@ -131,19 +113,7 @@
{format === 'csv' ? 'border-primary bg-primary/10' : 'border-border hover:border-muted-foreground'}"
>
<div class="flex items-center gap-3">
<svg
class="w-8 h-8 text-green-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
<ChartBar size={32} class="text-green-500" />
<div>
<div class="font-medium text-foreground">CSV</div>
<div class="text-xs text-muted-foreground">.csv</div>
@ -180,14 +150,7 @@
</span>
{:else}
<span class="inline-flex items-center gap-2">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"
/>
</svg>
<Upload size={20} />
{$_('export.button')}
</span>
{/if}

View file

@ -1,4 +1,6 @@
<script lang="ts">
import { CalendarBlank, CaretDown, Cake, Trash, Plus } from '@manacore/shared-icons';
interface CustomDate {
id: string;
label: string;
@ -53,25 +55,10 @@
onclick={() => (isOpen = !isOpen)}
>
<div class="section-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
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>
<CalendarBlank size={18} />
</div>
<h2 class="section-title">Daten</h2>
<svg
class="chevron-icon"
class:chevron-open={isOpen}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
<CaretDown size={20} class="chevron-icon {isOpen ? 'chevron-open' : ''}" />
</button>
{#if isOpen}
<div class="dates-container">
@ -79,14 +66,7 @@
<div class="form-field birthday-field">
<label for="birthday" class="label date-label">
<span class="date-icon-label">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M21 15.546c-.523 0-1.046.151-1.5.454a2.704 2.704 0 01-3 0 2.704 2.704 0 00-3 0 2.704 2.704 0 01-3 0 2.704 2.704 0 00-3 0 2.704 2.704 0 01-3 0A2.704 2.704 0 003 15.546V20a1 1 0 001 1h16a1 1 0 001-1v-4.454zM3 15.546V12a2 2 0 012-2h14a2 2 0 012 2v3.546M9 10V4a2 2 0 012-2h2a2 2 0 012 2v6"
/>
</svg>
<Cake size={14} />
</span>
Geburtstag
</label>
@ -123,28 +103,14 @@
onclick={() => removeCustomDate(customDate.id)}
aria-label="Datum entfernen"
>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
<Trash size={18} />
</button>
</div>
{/each}
<!-- Add button -->
<button type="button" class="add-button" onclick={addCustomDate}>
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 4v16m8-8H4"
/>
</svg>
<Plus size={16} />
Datum hinzufügen
</button>
</div>

View file

@ -1,4 +1,6 @@
<script lang="ts">
import { ChatCircle, CaretDown } from '@manacore/shared-icons';
interface Props {
linkedin: string;
twitter: string;
@ -64,25 +66,10 @@
onclick={() => (isOpen = !isOpen)}
>
<div class="section-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z"
/>
</svg>
<ChatCircle size={18} />
</div>
<h2 class="section-title">Social Media</h2>
<svg
class="chevron-icon"
class:chevron-open={isOpen}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
<CaretDown size={20} class="chevron-icon {isOpen ? 'chevron-open' : ''}" />
</button>
{#if isOpen}
<div class="social-grid">

View file

@ -1,5 +1,6 @@
<script lang="ts">
import { _ } from 'svelte-i18n';
import { CloudArrowUp, File, Table } from '@manacore/shared-icons';
interface Props {
onFileSelect: (file: File) => void;
@ -81,20 +82,7 @@
<div class="flex flex-col items-center gap-4">
<div class="w-16 h-16 rounded-full bg-primary/10 flex items-center justify-center text-primary">
<svg
class="w-8 h-8"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
/>
</svg>
<CloudArrowUp size={32} />
</div>
<div>
@ -104,25 +92,11 @@
<div class="flex items-center gap-4 text-sm text-muted-foreground">
<span class="flex items-center gap-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
<File size={16} />
vCard (.vcf)
</span>
<span class="flex items-center gap-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"
/>
</svg>
<Table size={16} />
CSV (.csv)
</span>
</div>

View file

@ -12,6 +12,7 @@
// contactsStore removed — live queries auto-update
import { GoogleImportSkeleton } from '$lib/components/skeletons';
import { ContactsEvents } from '@manacore/shared-utils/analytics';
import { Check } from '@manacore/shared-icons';
type Step = 'connect' | 'select' | 'result';
@ -345,14 +346,7 @@
<div
class="w-20 h-20 mx-auto rounded-full bg-green-500/10 flex items-center justify-center text-green-500"
>
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 13l4 4L19 7"
/>
</svg>
<Check size={40} />
</div>
<div>

View file

@ -1,4 +1,5 @@
<script lang="ts">
import { Plus, Check, Heart, Phone, Envelope, TextAa, CaretDown } from '@manacore/shared-icons';
import { _ } from 'svelte-i18n';
import type { Contact } from '$lib/api/contacts';
import type { SortField } from '$lib/components/SortToggle.svelte';
@ -173,14 +174,7 @@
>
<!-- Plus Avatar -->
<div class="avatar-sm new-contact-avatar">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 4v16m8-8H4"
/>
</svg>
<Plus size={18} />
</div>
<!-- Text -->
@ -220,9 +214,7 @@
aria-label={selectedIds.has(contact.id) ? 'Auswahl aufheben' : 'Auswählen'}
>
{#if selectedIds.has(contact.id)}
<svg class="w-5 h-5 text-primary" fill="currentColor" viewBox="0 0 24 24">
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" />
</svg>
<Check size={20} class="text-primary" />
{:else}
<div class="w-5 h-5 rounded border-2 border-border"></div>
{/if}
@ -262,11 +254,7 @@
{getDisplayName(contact)}
</span>
{#if contact.isFavorite}
<svg class="favorite-badge" fill="currentColor" viewBox="0 0 24 24">
<path
d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"
/>
</svg>
<Heart size={13} weight="fill" class="favorite-badge" />
{/if}
{#if contact.company}
<span class="contact-company-inline">@ {contact.company}</span>
@ -297,26 +285,12 @@
class="action-chip"
title={contact.mobile || contact.phone}
>
<svg class="action-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"
/>
</svg>
<Phone size={16} class="action-icon" />
</a>
{/if}
{#if contact.email}
<a href="mailto:{contact.email}" class="action-chip" title={contact.email}>
<svg class="action-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
/>
</svg>
<Envelope size={16} class="action-icon" />
</a>
{/if}
</div>
@ -339,22 +313,11 @@
? 'Alphabet-Navigation öffnen (Rechtsklick für Optionen)'
: 'Alphabet-Navigation schließen (Rechtsklick für Optionen)'}
>
<svg class="fab-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
{#if isAlphabetNavCollapsed}
<!-- ABC/Alphabet icon -->
<text x="3" y="17" font-size="12" font-weight="bold" fill="currentColor" stroke="none"
>AZ</text
>
{:else}
<!-- Chevron down icon -->
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 9l-7 7-7-7"
/>
{/if}
</svg>
{#if isAlphabetNavCollapsed}
<TextAa size={24} class="fab-icon" />
{:else}
<CaretDown size={24} class="fab-icon" />
{/if}
</button>
</div>

View file

@ -1,4 +1,5 @@
<script lang="ts">
import { Plus, Check, Heart, Phone, Envelope } from '@manacore/shared-icons';
import { _ } from 'svelte-i18n';
import type { Contact } from '$lib/api/contacts';
import { newContactModalStore } from '$lib/stores/new-contact-modal.svelte';
@ -118,14 +119,7 @@
>
<!-- Plus Avatar -->
<div class="grid-avatar new-contact-avatar">
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 4v16m8-8H4"
/>
</svg>
<Plus size={40} />
</div>
<!-- Info -->
@ -154,9 +148,7 @@
aria-label={selectedIds.has(contact.id) ? 'Auswahl aufheben' : 'Auswählen'}
>
{#if selectedIds.has(contact.id)}
<svg class="w-5 h-5 text-primary" fill="currentColor" viewBox="0 0 24 24">
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" />
</svg>
<Check size={20} class="text-primary" />
{:else}
<div class="w-5 h-5 rounded border-2 border-border"></div>
{/if}
@ -170,25 +162,9 @@
title={contact.isFavorite ? $_('contacts.unfavorite') : $_('contacts.favorite')}
>
{#if contact.isFavorite}
<svg class="w-5 h-5 text-red-500 fill-current" viewBox="0 0 24 24">
<path
d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"
/>
</svg>
<Heart size={20} weight="fill" class="text-red-500" />
{:else}
<svg
class="w-5 h-5 text-muted-foreground"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
/>
</svg>
<Heart size={20} class="text-muted-foreground" />
{/if}
</button>
@ -225,14 +201,7 @@
class="action-btn"
title={$_('contacts.call')}
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"
/>
</svg>
<Phone size={16} />
</a>
{/if}
{#if contact.email}
@ -242,14 +211,7 @@
class="action-btn"
title={$_('contacts.email')}
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
/>
</svg>
<Envelope size={16} />
</a>
{/if}
</div>

View file

@ -5,6 +5,16 @@
import type { Contact } from '$lib/api/contacts';
import { ContactListSkeleton } from '$lib/components/skeletons';
import '$lib/i18n';
import {
CaretLeft,
Archive,
MagnifyingGlass,
Warning,
Users,
Info,
ArrowCounterClockwise,
Trash,
} from '@manacore/shared-icons';
let loading = $state(true);
let contacts = $state<Contact[]>([]);
@ -88,34 +98,18 @@
<!-- Header -->
<header class="header">
<a href="/" class="back-button" aria-label="Zurück">
<svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
<CaretLeft size={20} />
</a>
<h1 class="title">Archiv</h1>
<div class="title-icon">
<svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"
/>
</svg>
<Archive size={20} />
</div>
</header>
<!-- Search -->
{#if contacts.length > 0}
<div class="search-wrapper">
<svg class="search-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
/>
</svg>
<MagnifyingGlass class="search-icon" size={20} />
<input
type="text"
placeholder="Archiv durchsuchen..."
@ -127,14 +121,7 @@
{#if error}
<div class="error-banner" role="alert">
<svg class="icon-sm" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
<Warning size={16} />
<span>{error}</span>
<button onclick={() => (error = null)} class="dismiss-btn">&times;</button>
</div>
@ -145,14 +132,7 @@
{:else if contacts.length === 0}
<div class="empty-state">
<div class="empty-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"
/>
</svg>
<Archive size={40} />
</div>
<h2 class="empty-title">Archiv ist leer</h2>
<p class="empty-description">
@ -160,42 +140,21 @@
löschen.
</p>
<a href="/" class="btn btn-primary">
<svg class="icon-sm" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"
/>
</svg>
<Users size={16} />
Zu Kontakten
</a>
</div>
{:else if filteredContacts().length === 0}
<div class="empty-state">
<div class="empty-icon">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
/>
</svg>
<MagnifyingGlass size={40} />
</div>
<h2 class="empty-title">Keine Ergebnisse</h2>
<p class="empty-description">Keine archivierten Kontakte gefunden für "{searchQuery}"</p>
</div>
{:else}
<div class="info-banner">
<svg class="icon-sm" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<Info size={16} />
<span>Archivierte Kontakte können wiederhergestellt oder endgültig gelöscht werden.</span>
</div>
@ -238,14 +197,7 @@
aria-label="Wiederherstellen"
title="Wiederherstellen"
>
<svg class="icon-sm" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
/>
</svg>
<ArrowCounterClockwise size={16} />
</button>
<button
onclick={(e) => handleDelete(e, contact)}
@ -253,14 +205,7 @@
aria-label="Endgültig löschen"
title="Endgültig löschen"
>
<svg class="icon-sm" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
<Trash size={16} />
</button>
</div>
</div>

View file

@ -10,6 +10,14 @@
// contactsStore removed — live queries auto-update
import { ImportPreviewSkeleton } from '$lib/components/skeletons';
import { ContactsEvents } from '@manacore/shared-utils/analytics';
import {
Upload,
DownloadSimple,
File,
FileText,
AddressBook,
Check,
} from '@manacore/shared-icons';
import '$lib/i18n';
type Tab = 'import' | 'export';
@ -187,14 +195,7 @@
? 'bg-card text-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground'}"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"
/>
</svg>
<Upload size={20} />
Import
</button>
<button
@ -205,14 +206,7 @@
? 'bg-card text-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground'}"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
/>
</svg>
<DownloadSimple size={20} />
Export
</button>
</div>
@ -230,14 +224,7 @@
: 'border-transparent text-muted-foreground hover:text-foreground'}"
>
<span class="flex items-center gap-2">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
<File size={20} />
Datei
</span>
</button>
@ -292,14 +279,7 @@
<div
class="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center text-primary flex-shrink-0"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 6H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V8a2 2 0 00-2-2h-5m-4 0V5a2 2 0 114 0v1m-4 0a2 2 0 104 0"
/>
</svg>
<AddressBook size={20} />
</div>
<div>
<div class="font-medium text-foreground">vCard (.vcf)</div>
@ -312,14 +292,7 @@
<div
class="w-10 h-10 rounded-lg bg-green-500/10 flex items-center justify-center text-green-500 flex-shrink-0"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
<FileText size={20} />
</div>
<div>
<div class="font-medium text-foreground">CSV (.csv)</div>
@ -336,14 +309,7 @@
onclick={handleDownloadTemplate}
class="text-primary hover:underline text-sm inline-flex items-center gap-2"
>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
/>
</svg>
<DownloadSimple size={16} />
CSV-Vorlage herunterladen
</button>
</div>
@ -366,14 +332,7 @@
<div
class="w-20 h-20 mx-auto rounded-full bg-green-500/10 flex items-center justify-center text-green-500"
>
<svg class="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 13l4 4L19 7"
/>
</svg>
<Check size={40} />
</div>
<div>
@ -433,14 +392,7 @@
<div
class="bg-green-500/10 border border-green-500/20 rounded-lg p-4 text-green-600 dark:text-green-400 flex items-center gap-3"
>
<svg class="w-5 h-5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 13l4 4L19 7"
/>
</svg>
<Check size={20} class="flex-shrink-0" />
Export erfolgreich! Die Datei wurde heruntergeladen.
</div>
{/if}
@ -468,14 +420,7 @@
<div
class="w-12 h-12 rounded-lg bg-primary/10 flex items-center justify-center text-primary"
>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 6H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V8a2 2 0 00-2-2h-5m-4 0V5a2 2 0 114 0v1m-4 0a2 2 0 104 0m-5 8a2 2 0 100-4 2 2 0 000 4zm0 0c1.306 0 2.417.835 2.83 2M9 14a3.001 3.001 0 00-2.83 2M15 11h3m-3 4h2"
/>
</svg>
<AddressBook size={24} />
</div>
<div>
<div class="font-medium text-foreground">vCard</div>
@ -495,14 +440,7 @@
<div
class="w-12 h-12 rounded-lg bg-green-500/10 flex items-center justify-center text-green-500"
>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
<FileText size={24} />
</div>
<div>
<div class="font-medium text-foreground">CSV</div>
@ -592,14 +530,7 @@
</span>
{:else}
<span class="inline-flex items-center gap-2">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
/>
</svg>
<DownloadSimple size={20} />
Kontakte exportieren
</span>
{/if}

View file

@ -5,6 +5,7 @@
import MergeModal from '$lib/components/duplicates/MergeModal.svelte';
import { DuplicateListSkeleton } from '$lib/components/skeletons';
import { toastStore } from '@manacore/shared-ui';
import { ArrowsClockwise } from '@manacore/shared-icons';
let duplicates = $state<DuplicateGroup[]>([]);
let loading = $state(true);
@ -118,14 +119,7 @@
{#if loading}
<span class="animate-spin mr-2"></span>
{:else}
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
/>
</svg>
<ArrowsClockwise size={20} class="mr-2" />
{/if}
Erneut suchen
</button>

View file

@ -23,6 +23,37 @@
SettingsDangerButton,
GlobalSettingsSection,
} from '@manacore/shared-ui';
import {
User,
Envelope,
ShieldCheck,
Layout,
List,
SortAscending,
ArrowsDownUp,
Image,
Buildings,
Hash,
AddressBook,
Calendar,
Cake,
Bell,
ArrowsLeftRight,
Upload,
Download,
Archive,
Copy,
ClipboardText,
Sliders,
Lock,
EyeSlash,
ShareNetwork,
Trash,
Info,
Tag,
ArrowCounterClockwise,
SignOut,
} from '@manacore/shared-icons';
// Options for selects
const viewOptions = [
@ -102,40 +133,19 @@
<!-- Account Section -->
<SettingsSection title="Konto">
{#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>
<User size={20} />
{/snippet}
<SettingsCard>
<SettingsRow label="E-Mail" description={authStore.user?.email || 'Nicht angemeldet'}>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
/>
</svg>
<Envelope size={20} />
{/snippet}
</SettingsRow>
<SettingsRow label="Konto-Status" description="Dein aktueller Kontostatus" border={false}>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"
/>
</svg>
<ShieldCheck size={20} />
{/snippet}
<span
class="rounded-full bg-green-100 px-3 py-1 text-xs font-medium text-green-800 dark:bg-green-900/20 dark:text-green-400"
@ -152,14 +162,7 @@
<!-- Display Settings Section -->
<SettingsSection title="Anzeige">
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z"
/>
</svg>
<Layout size={20} />
{/snippet}
<SettingsCard>
@ -172,14 +175,7 @@
contactsSettings.set('defaultView', v as ContactView)}
>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 6h16M4 10h16M4 14h16M4 18h16"
/>
</svg>
<List size={20} />
{/snippet}
</SettingsSelect>
@ -191,14 +187,7 @@
onchange={(v: string | number | null) => contactsSettings.set('sortBy', v as ContactSortBy)}
>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M3 4h13M3 8h9m-9 4h6m4 0l4-4m0 0l4 4m-4-4v12"
/>
</svg>
<SortAscending size={20} />
{/snippet}
</SettingsSelect>
@ -211,14 +200,7 @@
contactsSettings.set('sortOrder', v as ContactSortOrder)}
>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"
/>
</svg>
<ArrowsDownUp size={20} />
{/snippet}
</SettingsSelect>
@ -229,14 +211,7 @@
onToggle={(v) => contactsSettings.set('showPhotos', v)}
>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
<Image size={20} />
{/snippet}
</SettingsToggle>
@ -247,14 +222,7 @@
onToggle={(v) => contactsSettings.set('showCompany', v)}
>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"
/>
</svg>
<Buildings size={20} />
{/snippet}
</SettingsToggle>
@ -268,14 +236,7 @@
border={false}
>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M7 20l4-16m2 16l4-16M6 9h14M4 15h14"
/>
</svg>
<Hash size={20} />
{/snippet}
</SettingsNumberInput>
</SettingsCard>
@ -284,14 +245,7 @@
<!-- Contact Format Section -->
<SettingsSection title="Kontakt-Darstellung">
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 6H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V8a2 2 0 00-2-2h-5m-4 0V5a2 2 0 114 0v1m-4 0a2 2 0 104 0m-5 8a2 2 0 100-4 2 2 0 000 4zm0 0c1.306 0 2.417.835 2.83 2M9 14a3.001 3.001 0 00-2.83 2M15 11h3m-3 4h2"
/>
</svg>
<AddressBook size={20} />
{/snippet}
<SettingsCard>
@ -304,14 +258,7 @@
contactsSettings.set('nameFormat', v as 'first-last' | 'last-first')}
>
{#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>
<User size={20} />
{/snippet}
</SettingsSelect>
@ -324,14 +271,7 @@
contactsSettings.set('dateFormat', v as DateFormat)}
>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
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>
<Calendar size={20} />
{/snippet}
</SettingsSelect>
@ -342,14 +282,7 @@
onToggle={(v) => contactsSettings.set('showBirthdayReminders', v)}
>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M21 15.546c-.523 0-1.046.151-1.5.454a2.704 2.704 0 01-3 0 2.704 2.704 0 00-3 0 2.704 2.704 0 01-3 0 2.704 2.704 0 00-3 0 2.704 2.704 0 01-3 0 2.701 2.701 0 00-1.5-.454M9 6v2m3-2v2m3-2v2M9 3h.01M12 3h.01M15 3h.01M21 21v-7a2 2 0 00-2-2H5a2 2 0 00-2 2v7h18zm-3-9v-2a2 2 0 00-2-2H8a2 2 0 00-2 2v2h12z"
/>
</svg>
<Cake size={20} />
{/snippet}
</SettingsToggle>
@ -363,14 +296,7 @@
border={false}
>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"
/>
</svg>
<Bell size={20} />
{/snippet}
</SettingsNumberInput>
</SettingsCard>
@ -379,14 +305,7 @@
<!-- Import/Export Section -->
<SettingsSection title="Daten">
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4"
/>
</svg>
<ArrowsLeftRight size={20} />
{/snippet}
<SettingsCard>
@ -396,14 +315,7 @@
href="/data?tab=import"
>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"
/>
</svg>
<Upload size={20} />
{/snippet}
</SettingsRow>
@ -413,14 +325,7 @@
href="/data?tab=export"
>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
/>
</svg>
<Download size={20} />
{/snippet}
</SettingsRow>
@ -431,14 +336,7 @@
border={false}
>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"
/>
</svg>
<Archive size={20} />
{/snippet}
</SettingsRow>
</SettingsCard>
@ -447,14 +345,7 @@
<!-- Duplicates Section -->
<SettingsSection title="Duplikate">
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
/>
</svg>
<Copy size={20} />
{/snippet}
<SettingsCard>
@ -465,14 +356,7 @@
onToggle={(v) => contactsSettings.set('autoDetectDuplicates', v)}
>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"
/>
</svg>
<ClipboardText size={20} />
{/snippet}
</SettingsToggle>
@ -486,14 +370,7 @@
border={false}
>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"
/>
</svg>
<Sliders size={20} />
{/snippet}
</SettingsSelect>
</SettingsCard>
@ -502,14 +379,7 @@
<!-- Privacy Section -->
<SettingsSection title="Datenschutz">
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
/>
</svg>
<Lock size={20} />
{/snippet}
<SettingsCard>
@ -520,14 +390,7 @@
onToggle={(v) => contactsSettings.set('privacyMode', v)}
>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"
/>
</svg>
<EyeSlash size={20} />
{/snippet}
</SettingsToggle>
@ -538,14 +401,7 @@
onToggle={(v) => contactsSettings.set('confirmBeforeSharing', v)}
>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z"
/>
</svg>
<ShareNetwork size={20} />
{/snippet}
</SettingsToggle>
@ -557,14 +413,7 @@
border={false}
>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
<Trash size={20} />
{/snippet}
</SettingsToggle>
</SettingsCard>
@ -573,27 +422,13 @@
<!-- About Section -->
<SettingsSection title="Über">
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<Info size={20} />
{/snippet}
<SettingsCard>
<SettingsRow label="Version" border={false}>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"
/>
</svg>
<Tag size={20} />
{/snippet}
<span class="text-[hsl(var(--muted-foreground))]">1.0.0</span>
</SettingsRow>
@ -609,14 +444,7 @@
onclick={handleResetSettings}
>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
/>
</svg>
<ArrowCounterClockwise size={20} />
{/snippet}
</SettingsDangerButton>
@ -628,14 +456,7 @@
border={false}
>
{#snippet icon()}
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"
/>
</svg>
<SignOut size={20} />
{/snippet}
</SettingsDangerButton>
</SettingsDangerZone>