mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-17 15:29:41 +02:00
feat(feedback): add feedback pages to all archived web apps
- Add feedback service and page to maerchenzauber - Add feedback service and page to memoro - Add feedback service and page to news - Add feedback service and page to nutriphi - Add feedback service and page to uload - Add feedback service and page to wisekeep - Fix zitare feedback to use env variable and add nav link All apps now use shared FeedbackPage component from @manacore/shared-feedback-ui 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
51edd52241
commit
1f2d21e005
19 changed files with 241 additions and 431 deletions
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Feedback Service Instance for Maerchenzauber Web App
|
||||
*/
|
||||
|
||||
import { createFeedbackService } from '@manacore/shared-feedback-service';
|
||||
import { authStore } from '$lib/stores/authStore.svelte';
|
||||
import { PUBLIC_MANA_CORE_AUTH_URL } from '$env/static/public';
|
||||
|
||||
const MANA_AUTH_URL = PUBLIC_MANA_CORE_AUTH_URL || 'http://localhost:3001';
|
||||
|
||||
export const feedbackService = createFeedbackService({
|
||||
apiUrl: MANA_AUTH_URL,
|
||||
appId: 'maerchenzauber',
|
||||
getAuthToken: async () => authStore.getAccessToken(),
|
||||
});
|
||||
|
|
@ -3,9 +3,25 @@
|
|||
import { page } from '$app/stores';
|
||||
import { authStore } from '$lib/stores/authStore.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import Sidebar from '$lib/components/layout/Sidebar.svelte';
|
||||
import Header from '$lib/components/layout/Header.svelte';
|
||||
import ToastContainer from '$lib/components/ui/ToastContainer.svelte';
|
||||
import { PillNavigation } from '@manacore/shared-ui';
|
||||
import type { PillNavItem } from '@manacore/shared-ui';
|
||||
import { getPillAppItems } from '@manacore/shared-branding';
|
||||
|
||||
// App switcher items
|
||||
const appItems = getPillAppItems('maerchenzauber');
|
||||
|
||||
// User email for dropdown
|
||||
let userEmail = $derived(authStore.user?.email);
|
||||
|
||||
// Navigation items for Märchenzauber
|
||||
const navItems: PillNavItem[] = [
|
||||
{ href: '/dashboard', label: 'Dashboard', icon: 'home' },
|
||||
{ href: '/stories', label: 'Geschichten', icon: 'document' },
|
||||
{ href: '/characters', label: 'Charaktere', icon: 'users' },
|
||||
{ href: '/discover', label: 'Entdecken', icon: 'compass' },
|
||||
{ href: '/settings', label: 'Einstellungen', icon: 'settings' },
|
||||
];
|
||||
|
||||
let { children } = $props();
|
||||
let loading = $state(true);
|
||||
|
|
|
|||
|
|
@ -1,425 +1,11 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { dataService } from '$lib/api';
|
||||
import { toastStore } from '$lib/stores/toast.svelte';
|
||||
|
||||
// Types
|
||||
interface FeedbackItem {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
votes: number;
|
||||
hasVoted: boolean;
|
||||
status: 'open' | 'planned' | 'in_progress' | 'completed';
|
||||
category: 'feature' | 'bug' | 'improvement';
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
// State
|
||||
let feedbackItems = $state<FeedbackItem[]>([]);
|
||||
let loading = $state(true);
|
||||
let submitting = $state(false);
|
||||
let showForm = $state(false);
|
||||
let activeFilter = $state<'all' | 'feature' | 'bug' | 'improvement'>('all');
|
||||
let sortBy = $state<'votes' | 'newest'>('votes');
|
||||
|
||||
// Form state
|
||||
let newTitle = $state('');
|
||||
let newDescription = $state('');
|
||||
let newCategory = $state<'feature' | 'bug' | 'improvement'>('feature');
|
||||
|
||||
// Fetch feedback (mock data - API not implemented yet)
|
||||
async function fetchFeedback() {
|
||||
loading = true;
|
||||
try {
|
||||
// TODO: Replace with actual API call when available
|
||||
// const data = await dataService.getFeedback();
|
||||
// feedbackItems = data || [];
|
||||
feedbackItems = [
|
||||
{
|
||||
id: '1',
|
||||
title: 'Mehrseitige Geschichten',
|
||||
description: 'Längere Geschichten mit mehr als 10 Seiten erstellen können',
|
||||
votes: 42,
|
||||
hasVoted: false,
|
||||
status: 'planned',
|
||||
category: 'feature',
|
||||
createdAt: '2024-01-15',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: 'Audio-Vorlesefunktion',
|
||||
description: 'Geschichten von einer KI-Stimme vorlesen lassen',
|
||||
votes: 38,
|
||||
hasVoted: true,
|
||||
status: 'in_progress',
|
||||
category: 'feature',
|
||||
createdAt: '2024-01-10',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: 'Charakter-Vorlagen',
|
||||
description: 'Vorgefertigte Charakter-Templates zum Anpassen',
|
||||
votes: 25,
|
||||
hasVoted: false,
|
||||
status: 'open',
|
||||
category: 'feature',
|
||||
createdAt: '2024-01-20',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
title: 'PDF Export',
|
||||
description: 'Geschichten als PDF herunterladen und drucken',
|
||||
votes: 31,
|
||||
hasVoted: false,
|
||||
status: 'completed',
|
||||
category: 'feature',
|
||||
createdAt: '2024-01-05',
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
title: 'Bildqualität verbessern',
|
||||
description: 'Höhere Auflösung für generierte Illustrationen',
|
||||
votes: 19,
|
||||
hasVoted: false,
|
||||
status: 'open',
|
||||
category: 'improvement',
|
||||
createdAt: '2024-01-18',
|
||||
},
|
||||
];
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove unused import warning
|
||||
void dataService;
|
||||
|
||||
onMount(() => {
|
||||
fetchFeedback();
|
||||
});
|
||||
|
||||
// Vote for feedback (local state only - API not implemented yet)
|
||||
function handleVote(feedbackId: string) {
|
||||
const item = feedbackItems.find((f) => f.id === feedbackId);
|
||||
if (!item) return;
|
||||
|
||||
if (item.hasVoted) {
|
||||
item.votes--;
|
||||
item.hasVoted = false;
|
||||
} else {
|
||||
item.votes++;
|
||||
item.hasVoted = true;
|
||||
}
|
||||
feedbackItems = [...feedbackItems];
|
||||
}
|
||||
|
||||
// Submit new feedback (local state only - API not implemented yet)
|
||||
function handleSubmit() {
|
||||
if (!newTitle.trim() || !newDescription.trim()) {
|
||||
toastStore.warning('Bitte fülle alle Felder aus');
|
||||
return;
|
||||
}
|
||||
|
||||
submitting = true;
|
||||
|
||||
// Add locally (API not implemented yet)
|
||||
const newItem: FeedbackItem = {
|
||||
id: Date.now().toString(),
|
||||
title: newTitle,
|
||||
description: newDescription,
|
||||
votes: 1,
|
||||
hasVoted: true,
|
||||
status: 'open',
|
||||
category: newCategory,
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
feedbackItems = [newItem, ...feedbackItems];
|
||||
toastStore.success('Feedback eingereicht!');
|
||||
showForm = false;
|
||||
newTitle = '';
|
||||
newDescription = '';
|
||||
newCategory = 'feature';
|
||||
submitting = false;
|
||||
}
|
||||
|
||||
// Filter and sort
|
||||
let filteredItems = $derived(
|
||||
feedbackItems
|
||||
.filter((item) => activeFilter === 'all' || item.category === activeFilter)
|
||||
.sort((a, b) => {
|
||||
if (sortBy === 'votes') return b.votes - a.votes;
|
||||
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
|
||||
})
|
||||
);
|
||||
|
||||
// Status colors
|
||||
const statusColors: Record<string, { bg: string; text: string }> = {
|
||||
open: { bg: 'bg-gray-100 dark:bg-gray-700', text: 'text-gray-600 dark:text-gray-300' },
|
||||
planned: { bg: 'bg-blue-100 dark:bg-blue-900/30', text: 'text-blue-600 dark:text-blue-400' },
|
||||
in_progress: {
|
||||
bg: 'bg-amber-100 dark:bg-amber-900/30',
|
||||
text: 'text-amber-600 dark:text-amber-400',
|
||||
},
|
||||
completed: {
|
||||
bg: 'bg-green-100 dark:bg-green-900/30',
|
||||
text: 'text-green-600 dark:text-green-400',
|
||||
},
|
||||
};
|
||||
|
||||
const statusLabels: Record<string, string> = {
|
||||
open: 'Offen',
|
||||
planned: 'Geplant',
|
||||
in_progress: 'In Arbeit',
|
||||
completed: 'Fertig',
|
||||
};
|
||||
|
||||
const categoryLabels: Record<string, string> = {
|
||||
feature: 'Feature',
|
||||
bug: 'Bug',
|
||||
improvement: 'Verbesserung',
|
||||
};
|
||||
import { FeedbackPage } from '@manacore/shared-feedback-ui';
|
||||
import { feedbackService } from '$lib/api/feedback';
|
||||
import { authStore } from '$lib/stores/authStore.svelte';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Feedback | Märchenzauber</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- Header -->
|
||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold text-gray-800 dark:text-gray-200">Feedback & Ideen</h1>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
Stimme für Features ab und teile deine Ideen
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onclick={() => (showForm = true)}
|
||||
class="flex items-center justify-center gap-2 rounded-xl bg-gradient-to-r from-pink-500 to-purple-600 px-5 py-2.5 text-sm font-medium text-white shadow-lg transition-transform hover:scale-105"
|
||||
>
|
||||
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
Idee einreichen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Filters -->
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<!-- Category filter -->
|
||||
<div class="flex gap-1 rounded-xl bg-gray-100 p-1 dark:bg-gray-700">
|
||||
{#each ['all', 'feature', 'bug', 'improvement'] as filter}
|
||||
<button
|
||||
onclick={() => (activeFilter = filter as typeof activeFilter)}
|
||||
class="rounded-lg px-3 py-1.5 text-sm font-medium transition-colors {activeFilter ===
|
||||
filter
|
||||
? 'bg-white text-gray-800 shadow dark:bg-gray-600 dark:text-white'
|
||||
: 'text-gray-600 hover:text-gray-800 dark:text-gray-300 dark:hover:text-white'}"
|
||||
>
|
||||
{filter === 'all' ? 'Alle' : categoryLabels[filter]}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<!-- Sort -->
|
||||
<select
|
||||
bind:value={sortBy}
|
||||
class="rounded-xl border border-gray-200 bg-white px-3 py-2 text-sm text-gray-700 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200"
|
||||
>
|
||||
<option value="votes">Meiste Stimmen</option>
|
||||
<option value="newest">Neueste</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Feedback List -->
|
||||
{#if loading}
|
||||
<div class="space-y-4">
|
||||
{#each Array(5) as _}
|
||||
<div class="h-24 animate-pulse rounded-2xl bg-gray-200 dark:bg-gray-700"></div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else if filteredItems.length === 0}
|
||||
<div class="rounded-2xl bg-gray-50 p-8 text-center dark:bg-gray-800/50">
|
||||
<svg
|
||||
class="mx-auto h-16 w-16 text-gray-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1.5"
|
||||
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>
|
||||
<h3 class="mt-4 font-medium text-gray-700 dark:text-gray-300">Noch kein Feedback</h3>
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
Sei der Erste, der eine Idee teilt!
|
||||
</p>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="space-y-3">
|
||||
{#each filteredItems as item (item.id)}
|
||||
<div class="flex gap-4 rounded-2xl bg-white p-4 shadow-md dark:bg-gray-800">
|
||||
<!-- Vote button -->
|
||||
<button
|
||||
onclick={() => handleVote(item.id)}
|
||||
class="flex flex-col items-center gap-1 rounded-xl px-3 py-2 transition-colors {item.hasVoted
|
||||
? 'bg-pink-100 text-pink-600 dark:bg-pink-900/30 dark:text-pink-400'
|
||||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-400 dark:hover:bg-gray-600'}"
|
||||
>
|
||||
<svg
|
||||
class="h-5 w-5"
|
||||
fill={item.hasVoted ? 'currentColor' : 'none'}
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M5 15l7-7 7 7"
|
||||
/>
|
||||
</svg>
|
||||
<span class="text-sm font-semibold">{item.votes}</span>
|
||||
</button>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<h3 class="font-semibold text-gray-800 dark:text-gray-200">
|
||||
{item.title}
|
||||
</h3>
|
||||
<span
|
||||
class="rounded-full px-2 py-0.5 text-xs font-medium {statusColors[item.status]
|
||||
.bg} {statusColors[item.status].text}"
|
||||
>
|
||||
{statusLabels[item.status]}
|
||||
</span>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400 line-clamp-2">
|
||||
{item.description}
|
||||
</p>
|
||||
<div class="mt-2 flex items-center gap-3 text-xs text-gray-500 dark:text-gray-500">
|
||||
<span class="rounded-full bg-gray-100 px-2 py-0.5 dark:bg-gray-700">
|
||||
{categoryLabels[item.category]}
|
||||
</span>
|
||||
<span>
|
||||
{new Date(item.createdAt).toLocaleDateString('de-DE')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Submit Form Modal -->
|
||||
{#if showForm}
|
||||
<div
|
||||
class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4"
|
||||
onclick={() => (showForm = false)}
|
||||
onkeydown={(e) => e.key === 'Escape' && (showForm = false)}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="w-full max-w-lg rounded-2xl bg-white p-6 shadow-2xl dark:bg-gray-800"
|
||||
onclick={(e) => e.stopPropagation()}
|
||||
onkeydown={(e) => e.stopPropagation()}
|
||||
role="dialog"
|
||||
>
|
||||
<div class="mb-6 flex items-center justify-between">
|
||||
<h2 class="text-xl font-bold text-gray-800 dark:text-gray-200">Neue Idee einreichen</h2>
|
||||
<button
|
||||
onclick={() => (showForm = false)}
|
||||
class="rounded-lg p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-600 dark:hover:bg-gray-700"
|
||||
>
|
||||
<svg class="h-5 w-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>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form
|
||||
onsubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handleSubmit();
|
||||
}}
|
||||
class="space-y-4"
|
||||
>
|
||||
<!-- Category -->
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Kategorie
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
{#each ['feature', 'bug', 'improvement'] as cat}
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => (newCategory = cat as typeof newCategory)}
|
||||
class="flex-1 rounded-xl px-3 py-2 text-sm font-medium transition-colors {newCategory ===
|
||||
cat
|
||||
? 'bg-pink-500 text-white'
|
||||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300'}"
|
||||
>
|
||||
{categoryLabels[cat]}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Title -->
|
||||
<div>
|
||||
<label
|
||||
for="title"
|
||||
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Titel
|
||||
</label>
|
||||
<input
|
||||
id="title"
|
||||
type="text"
|
||||
bind:value={newTitle}
|
||||
placeholder="Kurze Beschreibung deiner Idee"
|
||||
class="w-full rounded-xl border border-gray-200 bg-gray-50 px-4 py-3 text-gray-800 placeholder-gray-400 focus:border-pink-500 focus:outline-none focus:ring-2 focus:ring-pink-500/20 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
<div>
|
||||
<label
|
||||
for="description"
|
||||
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
Beschreibung
|
||||
</label>
|
||||
<textarea
|
||||
id="description"
|
||||
bind:value={newDescription}
|
||||
rows="4"
|
||||
placeholder="Erkläre deine Idee im Detail..."
|
||||
class="w-full resize-none rounded-xl border border-gray-200 bg-gray-50 px-4 py-3 text-gray-800 placeholder-gray-400 focus:border-pink-500 focus:outline-none focus:ring-2 focus:ring-pink-500/20 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-500"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Submit -->
|
||||
<button
|
||||
type="submit"
|
||||
disabled={submitting}
|
||||
class="w-full rounded-xl bg-gradient-to-r from-pink-500 to-purple-600 py-3 text-sm font-medium text-white shadow-lg transition-transform hover:scale-[1.02] disabled:opacity-50 disabled:hover:scale-100"
|
||||
>
|
||||
{submitting ? 'Wird eingereicht...' : 'Einreichen'}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<FeedbackPage
|
||||
{feedbackService}
|
||||
appName="Märchenzauber"
|
||||
currentUserId={authStore.user?.id}
|
||||
/>
|
||||
|
|
|
|||
15
apps-archived/memoro/apps/web/src/lib/api/feedback.ts
Normal file
15
apps-archived/memoro/apps/web/src/lib/api/feedback.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Feedback Service Instance for Memoro Web App
|
||||
*/
|
||||
|
||||
import { createFeedbackService } from '@manacore/shared-feedback-service';
|
||||
import { authStore } from '$lib/stores/auth';
|
||||
import { PUBLIC_MANA_CORE_AUTH_URL } from '$env/static/public';
|
||||
|
||||
const MANA_AUTH_URL = PUBLIC_MANA_CORE_AUTH_URL || 'http://localhost:3001';
|
||||
|
||||
export const feedbackService = createFeedbackService({
|
||||
apiUrl: MANA_AUTH_URL,
|
||||
appId: 'memoro',
|
||||
getAuthToken: async () => authStore.getAccessToken(),
|
||||
});
|
||||
|
|
@ -11,6 +11,13 @@
|
|||
import { onMount } from 'svelte';
|
||||
import { PillNavigation } from '@manacore/shared-ui';
|
||||
import type { PillNavItem, PillDropdownItem } from '@manacore/shared-ui';
|
||||
import { getPillAppItems } from '@manacore/shared-branding';
|
||||
|
||||
// App switcher items
|
||||
const appItems = getPillAppItems('memoro');
|
||||
|
||||
// User email for dropdown
|
||||
let userEmail = $derived(auth.user?.email);
|
||||
|
||||
// Navigation shortcuts (Ctrl+1-9)
|
||||
const navRoutes = [
|
||||
|
|
@ -179,6 +186,13 @@
|
|||
{languageItems}
|
||||
{currentLanguageLabel}
|
||||
primaryColor="#F7D44C"
|
||||
showAppSwitcher={true}
|
||||
{appItems}
|
||||
{userEmail}
|
||||
settingsHref="/settings"
|
||||
manaHref="/subscription"
|
||||
profileHref="/profile"
|
||||
allAppsHref="/apps"
|
||||
>
|
||||
{#snippet logo()}
|
||||
<svg
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
<script lang="ts">
|
||||
import { AppsPage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Alle Apps - Memoro</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="apps-page-wrapper">
|
||||
<AppsPage currentAppId="memoro" locale="de" title="Alle Apps" />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.apps-page-wrapper {
|
||||
min-height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<script lang="ts">
|
||||
import { FeedbackPage } from '@manacore/shared-feedback-ui';
|
||||
import { feedbackService } from '$lib/api/feedback';
|
||||
import { authStore } from '$lib/stores/auth';
|
||||
</script>
|
||||
|
||||
<FeedbackPage
|
||||
{feedbackService}
|
||||
appName="Memoro"
|
||||
currentUserId={authStore.user?.id}
|
||||
/>
|
||||
15
apps-archived/news/apps/web/src/lib/api/feedback.ts
Normal file
15
apps-archived/news/apps/web/src/lib/api/feedback.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Feedback Service Instance for News Web App
|
||||
*/
|
||||
|
||||
import { createFeedbackService } from '@manacore/shared-feedback-service';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { PUBLIC_MANA_CORE_AUTH_URL } from '$env/static/public';
|
||||
|
||||
const MANA_AUTH_URL = PUBLIC_MANA_CORE_AUTH_URL || 'http://localhost:3001';
|
||||
|
||||
export const feedbackService = createFeedbackService({
|
||||
apiUrl: MANA_AUTH_URL,
|
||||
appId: 'news',
|
||||
getAuthToken: async () => authStore.getAccessToken(),
|
||||
});
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<script lang="ts">
|
||||
import { FeedbackPage } from '@manacore/shared-feedback-ui';
|
||||
import { feedbackService } from '$lib/api/feedback';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
</script>
|
||||
|
||||
<FeedbackPage
|
||||
{feedbackService}
|
||||
appName="ManaNews"
|
||||
currentUserId={authStore.user?.id}
|
||||
/>
|
||||
15
apps-archived/nutriphi/apps/web/src/lib/api/feedback.ts
Normal file
15
apps-archived/nutriphi/apps/web/src/lib/api/feedback.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Feedback Service Instance for Nutriphi Web App
|
||||
*/
|
||||
|
||||
import { createFeedbackService } from '@manacore/shared-feedback-service';
|
||||
import { authStore } from '$lib/stores/auth';
|
||||
import { PUBLIC_MANA_CORE_AUTH_URL } from '$env/static/public';
|
||||
|
||||
const MANA_AUTH_URL = PUBLIC_MANA_CORE_AUTH_URL || 'http://localhost:3001';
|
||||
|
||||
export const feedbackService = createFeedbackService({
|
||||
apiUrl: MANA_AUTH_URL,
|
||||
appId: 'nutriphi',
|
||||
getAuthToken: async () => authStore.getAccessToken(),
|
||||
});
|
||||
|
|
@ -10,6 +10,13 @@
|
|||
import { onMount } from 'svelte';
|
||||
import { PillNavigation } from '@manacore/shared-ui';
|
||||
import type { PillNavItem } from '@manacore/shared-ui';
|
||||
import { getPillAppItems } from '@manacore/shared-branding';
|
||||
|
||||
// App switcher items
|
||||
const appItems = getPillAppItems('nutriphi');
|
||||
|
||||
// User email for dropdown
|
||||
let userEmail = $derived(auth.user?.email);
|
||||
|
||||
// Navigation shortcuts (Ctrl+1-7)
|
||||
const navRoutes = [
|
||||
|
|
@ -134,6 +141,13 @@
|
|||
onCollapsedChange={handleCollapsedChange}
|
||||
showThemeToggle={true}
|
||||
primaryColor="#22c55e"
|
||||
showAppSwitcher={true}
|
||||
{appItems}
|
||||
{userEmail}
|
||||
settingsHref="/settings"
|
||||
manaHref="/subscription"
|
||||
profileHref="/profile"
|
||||
allAppsHref="/apps"
|
||||
>
|
||||
{#snippet logo()}
|
||||
<span class="text-xl">🥗</span>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
<script lang="ts">
|
||||
import { AppsPage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Alle Apps - Nutriphi</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="apps-page-wrapper">
|
||||
<AppsPage currentAppId="nutriphi" locale="de" title="Alle Apps" />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.apps-page-wrapper {
|
||||
min-height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<script lang="ts">
|
||||
import { FeedbackPage } from '@manacore/shared-feedback-ui';
|
||||
import { feedbackService } from '$lib/api/feedback';
|
||||
import { authStore } from '$lib/stores/auth';
|
||||
</script>
|
||||
|
||||
<FeedbackPage
|
||||
{feedbackService}
|
||||
appName="Nutriphi"
|
||||
currentUserId={authStore.user?.id}
|
||||
/>
|
||||
15
apps-archived/uload/apps/web/src/lib/api/feedback.ts
Normal file
15
apps-archived/uload/apps/web/src/lib/api/feedback.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Feedback Service Instance for uLoad Web App
|
||||
*/
|
||||
|
||||
import { createFeedbackService } from '@manacore/shared-feedback-service';
|
||||
import { pb } from '$lib/pocketbase';
|
||||
import { PUBLIC_MANA_CORE_AUTH_URL } from '$env/static/public';
|
||||
|
||||
const MANA_AUTH_URL = PUBLIC_MANA_CORE_AUTH_URL || 'http://localhost:3001';
|
||||
|
||||
export const feedbackService = createFeedbackService({
|
||||
apiUrl: MANA_AUTH_URL,
|
||||
appId: 'uload',
|
||||
getAuthToken: async () => pb.authStore.token || '',
|
||||
});
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<script lang="ts">
|
||||
import { FeedbackPage } from '@manacore/shared-feedback-ui';
|
||||
import { feedbackService } from '$lib/api/feedback';
|
||||
import { pb } from '$lib/pocketbase';
|
||||
</script>
|
||||
|
||||
<FeedbackPage
|
||||
{feedbackService}
|
||||
appName="uLoad"
|
||||
currentUserId={pb.authStore.record?.id}
|
||||
/>
|
||||
15
apps-archived/wisekeep/apps/web/src/lib/api/feedback.ts
Normal file
15
apps-archived/wisekeep/apps/web/src/lib/api/feedback.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Feedback Service Instance for Wisekeep Web App
|
||||
*/
|
||||
|
||||
import { createFeedbackService } from '@manacore/shared-feedback-service';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { PUBLIC_MANA_CORE_AUTH_URL } from '$env/static/public';
|
||||
|
||||
const MANA_AUTH_URL = PUBLIC_MANA_CORE_AUTH_URL || 'http://localhost:3001';
|
||||
|
||||
export const feedbackService = createFeedbackService({
|
||||
apiUrl: MANA_AUTH_URL,
|
||||
appId: 'wisekeep',
|
||||
getAuthToken: async () => authStore.getAccessToken(),
|
||||
});
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<script lang="ts">
|
||||
import { FeedbackPage } from '@manacore/shared-feedback-ui';
|
||||
import { feedbackService } from '$lib/api/feedback';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
</script>
|
||||
|
||||
<FeedbackPage
|
||||
{feedbackService}
|
||||
appName="Wisekeep"
|
||||
currentUserId={authStore.user?.id}
|
||||
/>
|
||||
|
|
@ -4,8 +4,9 @@
|
|||
|
||||
import { createFeedbackService } from '@manacore/shared-feedback-service';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { PUBLIC_MANA_CORE_AUTH_URL } from '$env/static/public';
|
||||
|
||||
const MANA_AUTH_URL = 'http://localhost:3001'; // TODO: Use PUBLIC_MANA_CORE_AUTH_URL from env
|
||||
const MANA_AUTH_URL = PUBLIC_MANA_CORE_AUTH_URL || 'http://localhost:3001';
|
||||
|
||||
export const feedbackService = createFeedbackService({
|
||||
apiUrl: MANA_AUTH_URL,
|
||||
|
|
|
|||
|
|
@ -63,19 +63,17 @@
|
|||
);
|
||||
let currentLanguageLabel = $derived(getCurrentLanguageLabel(currentLocale));
|
||||
|
||||
// User email for user dropdown
|
||||
let userEmail = $derived(authStore.user?.email);
|
||||
// User email for user dropdown (fallback to 'Menü' when not logged in)
|
||||
let userEmail = $derived(authStore.user?.email || 'Menü');
|
||||
|
||||
// Navigation items for Zitare
|
||||
const navItems: PillNavItem[] = [
|
||||
{ href: '/', label: 'Start', icon: 'home' },
|
||||
{ href: '/', label: 'Zitate', icon: 'chat' },
|
||||
{ href: '/search', label: 'Suche', icon: 'search' },
|
||||
{ href: '/discover', label: 'Entdecken', icon: 'compass' },
|
||||
{ href: '/authors', label: 'Autoren', icon: 'users' },
|
||||
{ href: '/favorites', label: 'Favoriten', icon: 'heart' },
|
||||
{ href: '/lists', label: 'Listen', icon: 'list' },
|
||||
{ href: '/profile', label: 'Profil', icon: 'user' },
|
||||
{ href: '/mana', label: 'Mana', icon: 'sparkles' },
|
||||
{ href: '/feedback', label: 'Feedback', icon: 'chat' },
|
||||
];
|
||||
|
||||
// Navigation shortcuts (Ctrl+1-5)
|
||||
|
|
@ -200,6 +198,7 @@
|
|||
{currentLanguageLabel}
|
||||
showLogout={authStore.isAuthenticated}
|
||||
onLogout={handleLogout}
|
||||
loginHref="/login"
|
||||
primaryColor="#f59e0b"
|
||||
showAppSwitcher={true}
|
||||
{appItems}
|
||||
|
|
@ -207,6 +206,7 @@
|
|||
settingsHref="/settings"
|
||||
manaHref="/mana"
|
||||
profileHref="/profile"
|
||||
allAppsHref="/apps"
|
||||
/>
|
||||
|
||||
<!-- Main Content with dynamic padding based on nav mode -->
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue