mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-15 01:41:08 +02:00
refactor(ui): unify LanguageSelector, ConfirmDialog, and AppSlider across apps
- contacts, zitare: migrate LanguageSelector to shared PillDropdown pattern - context, times: replace local ConfirmDialog with ConfirmationModal from @manacore/shared-ui - delete local ConfirmDialog.svelte in both apps - map open→visible, onCancel→onClose, remove destructive prop (default in shared-ui) - calendar, chat, contacts, presi, todo: switch AppSlider from static MANA_APPS to getActiveManaApps() to filter inactive apps consistently Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3ea2c03ab2
commit
5e4518b418
16 changed files with 62 additions and 228 deletions
|
|
@ -1,10 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { AppSlider } from '@manacore/shared-ui';
|
||||
import type { AppItem } from '@manacore/shared-ui';
|
||||
import { MANA_APPS, APP_STATUS_LABELS, APP_SLIDER_LABELS } from '@manacore/shared-branding';
|
||||
import { getActiveManaApps, APP_STATUS_LABELS, APP_SLIDER_LABELS } from '@manacore/shared-branding';
|
||||
|
||||
// Convert MANA_APPS to AppItem format (German)
|
||||
const apps: AppItem[] = MANA_APPS.map((app) => ({
|
||||
// Convert active apps to AppItem format (German)
|
||||
const apps: AppItem[] = getActiveManaApps().map((app) => ({
|
||||
name: app.name,
|
||||
description: app.description.de,
|
||||
longDescription: app.longDescription.de,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { AppSlider } from '@manacore/shared-ui';
|
||||
import type { AppItem } from '@manacore/shared-ui';
|
||||
import { MANA_APPS, APP_STATUS_LABELS, APP_SLIDER_LABELS } from '@manacore/shared-branding';
|
||||
import { getActiveManaApps, APP_STATUS_LABELS, APP_SLIDER_LABELS } from '@manacore/shared-branding';
|
||||
|
||||
// Convert MANA_APPS to AppItem format (German)
|
||||
const apps: AppItem[] = MANA_APPS.map((app) => ({
|
||||
// Convert active apps to AppItem format (German)
|
||||
const apps: AppItem[] = getActiveManaApps().map((app) => ({
|
||||
name: app.name,
|
||||
description: app.description.de,
|
||||
longDescription: app.longDescription.de,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { AppSlider } from '@manacore/shared-ui';
|
||||
import type { AppItem } from '@manacore/shared-ui';
|
||||
import { MANA_APPS, APP_STATUS_LABELS, APP_SLIDER_LABELS } from '@manacore/shared-branding';
|
||||
import { getActiveManaApps, APP_STATUS_LABELS, APP_SLIDER_LABELS } from '@manacore/shared-branding';
|
||||
|
||||
// Convert MANA_APPS to AppItem format (German)
|
||||
const apps: AppItem[] = MANA_APPS.map((app) => ({
|
||||
// Convert active apps to AppItem format (German)
|
||||
const apps: AppItem[] = getActiveManaApps().map((app) => ({
|
||||
name: app.name,
|
||||
description: app.description.de,
|
||||
longDescription: app.longDescription.de,
|
||||
|
|
|
|||
|
|
@ -1,45 +1,19 @@
|
|||
<script lang="ts">
|
||||
import { locale } from 'svelte-i18n';
|
||||
import { PillDropdown } from '@manacore/shared-ui';
|
||||
import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-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',
|
||||
en: 'English',
|
||||
it: 'Italiano',
|
||||
fr: 'Français',
|
||||
es: 'Español',
|
||||
};
|
||||
let currentLocale = $derived($locale || 'de');
|
||||
|
||||
let isOpen = $state(false);
|
||||
|
||||
function handleSelect(lang: SupportedLocale) {
|
||||
setLocale(lang);
|
||||
isOpen = false;
|
||||
function handleLocaleChange(newLocale: string) {
|
||||
setLocale(newLocale as any);
|
||||
}
|
||||
|
||||
let languageItems = $derived(
|
||||
getLanguageDropdownItems(supportedLocales, currentLocale, handleLocaleChange)
|
||||
);
|
||||
let currentLabel = $derived(getCurrentLanguageLabel(currentLocale));
|
||||
</script>
|
||||
|
||||
<div class="relative">
|
||||
<button
|
||||
onclick={() => (isOpen = !isOpen)}
|
||||
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'}
|
||||
<CaretDown size={16} />
|
||||
</button>
|
||||
|
||||
{#if isOpen}
|
||||
<div class="absolute right-0 mt-2 w-40 rounded-md border border-border bg-card shadow-lg z-50">
|
||||
{#each supportedLocales as lang}
|
||||
<button
|
||||
onclick={() => handleSelect(lang)}
|
||||
class="w-full px-4 py-2 text-left text-sm hover:bg-accent transition-colors first:rounded-t-md last:rounded-b-md"
|
||||
class:bg-accent={$locale === lang}
|
||||
>
|
||||
{languageLabels[lang]}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<PillDropdown items={languageItems} label={currentLabel} direction="down" />
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
<script lang="ts">
|
||||
interface Props {
|
||||
open: boolean;
|
||||
title: string;
|
||||
message: string;
|
||||
confirmLabel?: string;
|
||||
cancelLabel?: string;
|
||||
destructive?: boolean;
|
||||
onConfirm: () => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
let {
|
||||
open,
|
||||
title,
|
||||
message,
|
||||
confirmLabel = 'Bestätigen',
|
||||
cancelLabel = 'Abbrechen',
|
||||
destructive = false,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
{#if open}
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center" role="dialog" aria-modal="true">
|
||||
<button class="absolute inset-0 bg-black/50" onclick={onCancel} aria-label="Schließen"></button>
|
||||
<div
|
||||
class="relative bg-card border border-border rounded-xl p-6 max-w-sm w-full mx-4 shadow-xl"
|
||||
>
|
||||
<h3 class="text-lg font-semibold text-foreground">{title}</h3>
|
||||
<p class="text-sm text-muted-foreground mt-2">{message}</p>
|
||||
<div class="flex justify-end gap-2 mt-6">
|
||||
<button class="btn btn-secondary text-sm" onclick={onCancel}>
|
||||
{cancelLabel}
|
||||
</button>
|
||||
<button
|
||||
class="btn text-sm text-white {destructive
|
||||
? 'bg-destructive hover:bg-destructive/90'
|
||||
: 'btn-primary'}"
|
||||
onclick={onConfirm}
|
||||
>
|
||||
{confirmLabel}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
getAllDocumentTags,
|
||||
} from '$lib/data/queries';
|
||||
import DocumentCard from '$lib/components/DocumentCard.svelte';
|
||||
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
|
||||
import { ConfirmationModal } from '@manacore/shared-ui';
|
||||
import type { DocumentType } from '$lib/types';
|
||||
|
||||
let deleteTarget = $state<string | null>(null);
|
||||
|
|
@ -190,12 +190,11 @@
|
|||
{/if}
|
||||
</div>
|
||||
|
||||
<ConfirmDialog
|
||||
open={deleteTarget !== null}
|
||||
<ConfirmationModal
|
||||
visible={deleteTarget !== null}
|
||||
title="Dokument löschen?"
|
||||
message="Das Dokument wird unwiderruflich gelöscht."
|
||||
confirmLabel="Löschen"
|
||||
destructive
|
||||
onConfirm={handleDeleteConfirm}
|
||||
onCancel={() => (deleteTarget = null)}
|
||||
onClose={() => (deleteTarget = null)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
import { tokensStore } from '$lib/stores/tokens.svelte';
|
||||
import DocumentEditor from '$lib/components/DocumentEditor.svelte';
|
||||
import AIToolbar from '$lib/components/AIToolbar.svelte';
|
||||
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
|
||||
import { ConfirmationModal } from '@manacore/shared-ui';
|
||||
import type { Document, DocumentType } from '$lib/types';
|
||||
import type { InsertionMode } from '$lib/services/ai';
|
||||
|
||||
|
|
@ -164,12 +164,11 @@
|
|||
{/if}
|
||||
</div>
|
||||
|
||||
<ConfirmDialog
|
||||
open={showDeleteConfirm}
|
||||
<ConfirmationModal
|
||||
visible={showDeleteConfirm}
|
||||
title="Dokument löschen?"
|
||||
message="Das Dokument wird unwiderruflich gelöscht."
|
||||
confirmLabel="Löschen"
|
||||
destructive
|
||||
onConfirm={handleDelete}
|
||||
onCancel={() => (showDeleteConfirm = false)}
|
||||
onClose={() => (showDeleteConfirm = false)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
import { useAllSpaces } from '$lib/data/queries';
|
||||
import SpaceCard from '$lib/components/SpaceCard.svelte';
|
||||
import CreateSpaceModal from '$lib/components/CreateSpaceModal.svelte';
|
||||
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
|
||||
import { ConfirmationModal } from '@manacore/shared-ui';
|
||||
import type { Space } from '$lib/types';
|
||||
|
||||
let searchQuery = $state('');
|
||||
|
|
@ -131,12 +131,11 @@
|
|||
onClose={() => (showCreateModal = false)}
|
||||
/>
|
||||
|
||||
<ConfirmDialog
|
||||
open={deleteTarget !== null}
|
||||
<ConfirmationModal
|
||||
visible={deleteTarget !== null}
|
||||
title="Space löschen?"
|
||||
message="Alle Dokumente in diesem Space werden ebenfalls gelöscht. Diese Aktion kann nicht rückgängig gemacht werden."
|
||||
confirmLabel="Löschen"
|
||||
destructive
|
||||
onConfirm={handleDeleteConfirm}
|
||||
onCancel={() => (deleteTarget = null)}
|
||||
onClose={() => (deleteTarget = null)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
findSpaceById,
|
||||
} from '$lib/data/queries';
|
||||
import DocumentCard from '$lib/components/DocumentCard.svelte';
|
||||
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
|
||||
import { ConfirmationModal } from '@manacore/shared-ui';
|
||||
import BatchCreateModal from '$lib/components/BatchCreateModal.svelte';
|
||||
import type { Space, DocumentType } from '$lib/types';
|
||||
|
||||
|
|
@ -270,14 +270,13 @@
|
|||
{/if}
|
||||
</div>
|
||||
|
||||
<ConfirmDialog
|
||||
open={deleteTarget !== null}
|
||||
<ConfirmationModal
|
||||
visible={deleteTarget !== null}
|
||||
title="Dokument löschen?"
|
||||
message="Das Dokument wird unwiderruflich gelöscht."
|
||||
confirmLabel="Löschen"
|
||||
destructive
|
||||
onConfirm={handleDeleteConfirm}
|
||||
onCancel={() => (deleteTarget = null)}
|
||||
onClose={() => (deleteTarget = null)}
|
||||
/>
|
||||
|
||||
<BatchCreateModal
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { AppSlider } from '@manacore/shared-ui';
|
||||
import type { AppItem } from '@manacore/shared-ui';
|
||||
import { MANA_APPS, APP_STATUS_LABELS, APP_SLIDER_LABELS } from '@manacore/shared-branding';
|
||||
import { getActiveManaApps, APP_STATUS_LABELS, APP_SLIDER_LABELS } from '@manacore/shared-branding';
|
||||
|
||||
// Convert MANA_APPS to AppItem format (English)
|
||||
const apps: AppItem[] = MANA_APPS.map((app) => ({
|
||||
// Convert active apps to AppItem format (English)
|
||||
const apps: AppItem[] = getActiveManaApps().map((app) => ({
|
||||
name: app.name,
|
||||
description: app.description.en,
|
||||
longDescription: app.longDescription.en,
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
<script lang="ts">
|
||||
import { _ } from 'svelte-i18n';
|
||||
|
||||
let {
|
||||
visible = false,
|
||||
title = '',
|
||||
message = '',
|
||||
confirmLabel,
|
||||
cancelLabel,
|
||||
destructive = true,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
}: {
|
||||
visible: boolean;
|
||||
title: string;
|
||||
message?: string;
|
||||
confirmLabel?: string;
|
||||
cancelLabel?: string;
|
||||
destructive?: boolean;
|
||||
onConfirm: () => void;
|
||||
onCancel: () => void;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
{#if visible}
|
||||
<div
|
||||
class="fixed inset-0 z-[100] flex items-center justify-center bg-black/50 backdrop-blur-sm"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<div
|
||||
class="mx-4 w-full max-w-sm rounded-2xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-6 shadow-xl"
|
||||
>
|
||||
<h3 class="text-lg font-semibold text-[hsl(var(--foreground))]">{title}</h3>
|
||||
{#if message}
|
||||
<p class="mt-2 text-sm text-[hsl(var(--muted-foreground))]">{message}</p>
|
||||
{/if}
|
||||
<div class="mt-5 flex gap-2">
|
||||
<button
|
||||
onclick={onCancel}
|
||||
class="flex-1 rounded-lg border border-[hsl(var(--border))] py-2.5 text-sm text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]"
|
||||
>
|
||||
{cancelLabel || $_('common.cancel')}
|
||||
</button>
|
||||
<button
|
||||
onclick={onConfirm}
|
||||
class="flex-1 rounded-lg py-2.5 text-sm font-medium transition-colors {destructive
|
||||
? 'bg-red-500 text-white hover:bg-red-600'
|
||||
: 'bg-[hsl(var(--primary))] text-[hsl(var(--primary-foreground))] hover:opacity-90'}"
|
||||
>
|
||||
{confirmLabel || $_('common.delete')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
import { formatDurationCompact } from '$lib/data/queries';
|
||||
import { CurrencyDollar } from '@manacore/shared-icons';
|
||||
import type { TimeEntry, Project, Client } from '@times/shared';
|
||||
import ConfirmDialog from './ConfirmDialog.svelte';
|
||||
import { ConfirmationModal } from '@manacore/shared-ui';
|
||||
|
||||
let {
|
||||
entry,
|
||||
|
|
@ -200,10 +200,10 @@
|
|||
{/if}
|
||||
</div>
|
||||
|
||||
<ConfirmDialog
|
||||
<ConfirmationModal
|
||||
visible={showDeleteConfirm}
|
||||
title={$_('common.delete')}
|
||||
message={$_('entry.deleteConfirm')}
|
||||
onConfirm={confirmDelete}
|
||||
onCancel={() => (showDeleteConfirm = false)}
|
||||
onClose={() => (showDeleteConfirm = false)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
import { getTotalDuration, formatDurationCompact } from '$lib/data/queries';
|
||||
import type { Client, Project, TimeEntry } from '@times/shared';
|
||||
import { PROJECT_COLORS } from '@times/shared/constants';
|
||||
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
|
||||
import { ConfirmationModal } from '@manacore/shared-ui';
|
||||
import { CaretRight } from '@manacore/shared-icons';
|
||||
|
||||
const allClients = getContext<{ value: Client[] }>('clients');
|
||||
|
|
@ -348,10 +348,10 @@
|
|||
{/if}
|
||||
</div>
|
||||
|
||||
<ConfirmDialog
|
||||
<ConfirmationModal
|
||||
visible={deleteConfirmId !== null}
|
||||
title={$_('common.delete')}
|
||||
message={$_('client.deleteConfirm')}
|
||||
onConfirm={confirmDelete}
|
||||
onCancel={() => (deleteConfirmId = null)}
|
||||
onClose={() => (deleteConfirmId = null)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
import { getTotalDuration, formatDurationCompact } from '$lib/data/queries';
|
||||
import type { Project, Client, TimeEntry } from '@times/shared';
|
||||
import { PROJECT_COLORS } from '@times/shared/constants';
|
||||
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
|
||||
import { ConfirmationModal } from '@manacore/shared-ui';
|
||||
import { CaretRight } from '@manacore/shared-icons';
|
||||
|
||||
const allProjects = getContext<{ value: Project[] }>('projects');
|
||||
|
|
@ -357,10 +357,10 @@
|
|||
{/if}
|
||||
</div>
|
||||
|
||||
<ConfirmDialog
|
||||
<ConfirmationModal
|
||||
visible={deleteConfirmId !== null}
|
||||
title={$_('common.delete')}
|
||||
message={$_('project.deleteConfirm')}
|
||||
onConfirm={confirmDelete}
|
||||
onCancel={() => (deleteConfirmId = null)}
|
||||
onClose={() => (deleteConfirmId = null)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { AppSlider, type AppItem } from '@manacore/shared-ui';
|
||||
import { MANA_APPS, APP_STATUS_LABELS, APP_SLIDER_LABELS } from '@manacore/shared-branding';
|
||||
import { getActiveManaApps, APP_STATUS_LABELS, APP_SLIDER_LABELS } from '@manacore/shared-branding';
|
||||
|
||||
// Convert MANA_APPS to AppItem format (German)
|
||||
const apps: AppItem[] = MANA_APPS.map((app) => ({
|
||||
// Convert active apps to AppItem format (German)
|
||||
const apps: AppItem[] = getActiveManaApps().map((app) => ({
|
||||
name: app.name,
|
||||
description: app.description.de,
|
||||
longDescription: app.longDescription.de,
|
||||
|
|
|
|||
|
|
@ -1,51 +1,19 @@
|
|||
<script lang="ts">
|
||||
import { locale } from 'svelte-i18n';
|
||||
import { PillDropdown } from '@manacore/shared-ui';
|
||||
import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n';
|
||||
import { setLocale, supportedLocales } from '$lib/i18n';
|
||||
import { CaretDown } from '@manacore/shared-icons';
|
||||
|
||||
const languageLabels: Record<string, string> = {
|
||||
de: 'Deutsch',
|
||||
en: 'English',
|
||||
};
|
||||
let currentLocale = $derived($locale || 'de');
|
||||
|
||||
let isOpen = $state(false);
|
||||
|
||||
function handleSelect(lang: string) {
|
||||
setLocale(lang as 'de' | 'en');
|
||||
isOpen = false;
|
||||
function handleLocaleChange(newLocale: string) {
|
||||
setLocale(newLocale as any);
|
||||
}
|
||||
|
||||
let languageItems = $derived(
|
||||
getLanguageDropdownItems(supportedLocales, currentLocale, handleLocaleChange)
|
||||
);
|
||||
let currentLabel = $derived(getCurrentLanguageLabel(currentLocale));
|
||||
</script>
|
||||
|
||||
<div class="relative">
|
||||
<button
|
||||
onclick={() => (isOpen = !isOpen)}
|
||||
class="flex items-center gap-1 px-3 py-2 rounded-lg text-sm text-foreground-secondary hover:text-foreground hover:bg-surface transition-colors"
|
||||
>
|
||||
{languageLabels[$locale || 'de']}
|
||||
<CaretDown size={16} />
|
||||
</button>
|
||||
|
||||
{#if isOpen}
|
||||
<div
|
||||
class="absolute right-0 mt-1 py-1 bg-surface-elevated border border-border rounded-lg shadow-lg z-50"
|
||||
>
|
||||
{#each supportedLocales as lang}
|
||||
<button
|
||||
onclick={() => handleSelect(lang)}
|
||||
class="w-full px-4 py-2 text-left text-sm hover:bg-surface transition-colors"
|
||||
class:text-primary={$locale === lang}
|
||||
>
|
||||
{languageLabels[lang]}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if isOpen}
|
||||
<button
|
||||
class="fixed inset-0 z-40 bg-transparent"
|
||||
onclick={() => (isOpen = false)}
|
||||
aria-label="Close menu"
|
||||
></button>
|
||||
{/if}
|
||||
<PillDropdown items={languageItems} label={currentLabel} direction="down" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue