mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 19:41:09 +02:00
chore: global ToastContainer, migrate inline toasts, delete SETUP.md
Add ToastContainer.svelte to (app) layout — renders toasts from the central toast.svelte store (stacked, auto-dismiss, color-coded by type). Previously the store existed but had no renderer. Migrate inline toast implementations to the central store: - SyncSection: showToast() → toast.success/error(), strip DOM + CSS - Credits ListView: same migration, remove showToast + inline toast - Profile ListView: same migration, remove showToast + inline toast Delete apps/mana/apps/web/SETUP.md — completely outdated (references Supabase, teams, organizations — all removed long ago). Real docs live in CLAUDE.md and docs/LOCAL_DEVELOPMENT.md. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0ddaab53e4
commit
8def989ed9
6 changed files with 107 additions and 480 deletions
|
|
@ -1,364 +0,0 @@
|
|||
# Mana Web - Setup Guide
|
||||
|
||||
## 🎉 Project Created Successfully!
|
||||
|
||||
A brand new SvelteKit application has been created in the `mana-web` folder, separate from the existing React Native `mana_app`.
|
||||
|
||||
## ✅ What's Been Implemented
|
||||
|
||||
### Core Infrastructure
|
||||
|
||||
- ✅ SvelteKit 2.x with Svelte 5 (Runes)
|
||||
- ✅ TypeScript configuration
|
||||
- ✅ Tailwind CSS styling
|
||||
- ✅ Supabase authentication integration
|
||||
- ✅ Server-side hooks for auth management
|
||||
- ✅ Environment configuration
|
||||
|
||||
### Authentication System
|
||||
|
||||
- ✅ Login page (`/login`)
|
||||
- ✅ Registration page (`/register`)
|
||||
- ✅ Protected routes with automatic redirects
|
||||
- ✅ Server-side session management
|
||||
- ✅ SSR-safe Supabase client setup
|
||||
|
||||
### Dashboard
|
||||
|
||||
- ✅ Main dashboard with stats display
|
||||
- ✅ Available mana/credits tracking
|
||||
- ✅ Organization and team counts
|
||||
- ✅ Quick action links
|
||||
- ✅ Responsive design
|
||||
|
||||
### UI Components
|
||||
|
||||
- ✅ Button component (primary, secondary, danger, ghost variants)
|
||||
- ✅ Card component
|
||||
- ✅ Input component with validation
|
||||
- ✅ Responsive navigation with mobile menu
|
||||
|
||||
## 📁 Project Structure
|
||||
|
||||
```
|
||||
mana-web/
|
||||
├── src/
|
||||
│ ├── routes/
|
||||
│ │ ├── (auth)/ # Public auth routes
|
||||
│ │ │ ├── login/ # Login page
|
||||
│ │ │ └── register/ # Registration page
|
||||
│ │ ├── (app)/ # Protected app routes
|
||||
│ │ │ └── dashboard/ # Main dashboard
|
||||
│ │ ├── +layout.svelte # Root layout
|
||||
│ │ ├── +layout.ts # Client-side layout load
|
||||
│ │ ├── +layout.server.ts # Server-side layout load
|
||||
│ │ └── +page.svelte # Home page (redirects)
|
||||
│ ├── lib/
|
||||
│ │ ├── components/
|
||||
│ │ │ └── ui/ # Reusable UI components
|
||||
│ │ ├── server/ # Server-only utilities
|
||||
│ │ ├── stores/ # Svelte stores
|
||||
│ │ ├── types/ # TypeScript types
|
||||
│ │ └── utils/ # Utility functions
|
||||
│ ├── hooks.server.ts # Server hooks (auth middleware)
|
||||
│ ├── app.css # Global Tailwind styles
|
||||
│ ├── app.d.ts # TypeScript declarations
|
||||
│ └── app.html # HTML template
|
||||
├── static/ # Static assets
|
||||
├── tests/ # Test files (future)
|
||||
├── .env # Environment variables
|
||||
├── .env.example # Environment template
|
||||
├── svelte.config.js # SvelteKit configuration
|
||||
├── vite.config.ts # Vite configuration
|
||||
├── tsconfig.json # TypeScript configuration
|
||||
└── package.json # Dependencies
|
||||
```
|
||||
|
||||
## 🚀 Getting Started
|
||||
|
||||
### 1. Configure Environment Variables
|
||||
|
||||
Update `.env` with your actual Supabase credentials:
|
||||
|
||||
```bash
|
||||
# Get these from your Supabase project settings
|
||||
PUBLIC_SUPABASE_URL=https://your-project-id.supabase.co
|
||||
PUBLIC_SUPABASE_ANON_KEY=your-anon-key-here
|
||||
```
|
||||
|
||||
### 2. Install Dependencies (if not already done)
|
||||
|
||||
```bash
|
||||
cd mana-web
|
||||
pnpm install
|
||||
```
|
||||
|
||||
### 3. Start Development Server
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Visit `http://localhost:5173`
|
||||
|
||||
### 4. Database Setup
|
||||
|
||||
The app expects the same Supabase database schema as the React Native app. Ensure you have these tables:
|
||||
|
||||
- `profiles` - User profile data
|
||||
- `organizations` - Organization entities
|
||||
- `teams` - Team entities
|
||||
- `team_members` - Team membership
|
||||
- `user_roles` - Role assignments
|
||||
- `roles` - Role definitions
|
||||
- `credit_transactions` - Credit history
|
||||
|
||||
## 📝 Next Steps
|
||||
|
||||
### Immediate (Must Do)
|
||||
|
||||
1. **Update .env file** with real Supabase credentials
|
||||
2. **Test login/registration** flow
|
||||
3. **Verify database schema** matches expected structure
|
||||
|
||||
### Short-Term Features to Add
|
||||
|
||||
1. **Organizations Management**
|
||||
- List organizations page
|
||||
- Organization detail page
|
||||
- Create organization form
|
||||
|
||||
2. **Teams Management**
|
||||
- List teams page
|
||||
- Team detail page with members
|
||||
- Create team form
|
||||
- Add/remove team members
|
||||
|
||||
3. **Credit/Mana Transfer**
|
||||
- Send mana page
|
||||
- Credit transaction history
|
||||
- Balance tracking
|
||||
|
||||
4. **Settings Page**
|
||||
- Profile management
|
||||
- Theme toggle (light/dark)
|
||||
- Account settings
|
||||
|
||||
### Medium-Term Enhancements
|
||||
|
||||
1. **Real-time Updates**
|
||||
- Supabase realtime subscriptions
|
||||
- Live credit balance updates
|
||||
- Team activity notifications
|
||||
|
||||
2. **Enhanced UI/UX**
|
||||
- Loading skeletons
|
||||
- Error boundaries
|
||||
- Toast notifications
|
||||
- Optimistic UI updates
|
||||
|
||||
3. **Testing**
|
||||
- Vitest unit tests
|
||||
- Playwright E2E tests
|
||||
- Component testing
|
||||
|
||||
4. **Performance**
|
||||
- Image optimization
|
||||
- Code splitting
|
||||
- Caching strategies
|
||||
- Performance monitoring
|
||||
|
||||
## 🔧 Available Scripts
|
||||
|
||||
```bash
|
||||
# Development
|
||||
pnpm dev # Start dev server
|
||||
|
||||
# Building
|
||||
pnpm build # Build for production
|
||||
pnpm preview # Preview production build
|
||||
|
||||
# Type Checking
|
||||
pnpm check # Run TypeScript checks
|
||||
pnpm check:watch # Watch mode
|
||||
|
||||
# Code Quality
|
||||
pnpm format # Format code with Prettier
|
||||
pnpm lint # Lint code
|
||||
|
||||
# Testing
|
||||
pnpm test # Run unit tests
|
||||
pnpm test:ui # Run tests with UI
|
||||
pnpm test:e2e # Run E2E tests
|
||||
```
|
||||
|
||||
## 🎨 Design System
|
||||
|
||||
### Colors
|
||||
|
||||
- Primary: `#0055FF` (blue-600)
|
||||
- Background Light: `#FFFFFF`
|
||||
- Background Dark: `#121212`
|
||||
- Text Light: `#1F2937`
|
||||
- Text Dark: `#F9FAFB`
|
||||
|
||||
### Components
|
||||
|
||||
All components support:
|
||||
|
||||
- Dark mode (automatic via system preference)
|
||||
- TypeScript props
|
||||
- Tailwind CSS classes
|
||||
- Accessibility features
|
||||
|
||||
### Responsive Breakpoints
|
||||
|
||||
- `sm`: 640px
|
||||
- `md`: 768px
|
||||
- `lg`: 1024px
|
||||
- `xl`: 1280px
|
||||
- `2xl`: 1536px
|
||||
|
||||
## 🔐 Authentication Flow
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ User visits site │
|
||||
│ ↓ │
|
||||
│ hooks.server.ts checks session │
|
||||
│ ↓ │
|
||||
│ Has session? → Go to /dashboard │
|
||||
│ ↓ │
|
||||
│ No session? → Go to /login │
|
||||
│ ↓ │
|
||||
│ User logs in │
|
||||
│ ↓ │
|
||||
│ Supabase creates session │
|
||||
│ ↓ │
|
||||
│ Redirect to /dashboard │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 📚 Key Technologies
|
||||
|
||||
| Technology | Version | Purpose |
|
||||
| ------------ | ------- | ------------- |
|
||||
| SvelteKit | 2.48.4 | Web framework |
|
||||
| Svelte | 5.43.3 | UI components |
|
||||
| TypeScript | 5.9.3 | Type safety |
|
||||
| Tailwind CSS | 3.4.18 | Styling |
|
||||
| Supabase | 2.79.0 | Backend/Auth |
|
||||
| Vite | 6.4.1 | Build tool |
|
||||
| Playwright | 1.56.1 | E2E testing |
|
||||
| Vitest | 3.2.4 | Unit testing |
|
||||
|
||||
## 🆚 Comparison: React Native vs SvelteKit
|
||||
|
||||
| Feature | React Native (mana_app) | SvelteKit (mana-web) |
|
||||
| ---------- | ----------------------- | -------------------- |
|
||||
| Platform | Mobile (iOS/Android) | Web (Browser) |
|
||||
| Routing | Expo Router | File-based routing |
|
||||
| State | useState/Context | Svelte Runes/$state |
|
||||
| Styling | NativeWind | Tailwind CSS |
|
||||
| Auth | Supabase | Supabase (same) |
|
||||
| Database | Supabase | Supabase (same) |
|
||||
| Build | Expo | Vite |
|
||||
| Deployment | App Stores | Vercel/Netlify |
|
||||
|
||||
## 🚨 Common Issues & Solutions
|
||||
|
||||
### Issue: Type errors on first run
|
||||
|
||||
**Solution**: Run `pnpm run check` to sync types
|
||||
|
||||
### Issue: Port already in use
|
||||
|
||||
**Solution**: Change port in `vite.config.ts` or kill process on port 5173
|
||||
|
||||
### Issue: Supabase auth not working
|
||||
|
||||
**Solution**:
|
||||
|
||||
1. Check `.env` variables are correct
|
||||
2. Verify Supabase project is active
|
||||
3. Check browser console for errors
|
||||
|
||||
### Issue: Dark mode not switching
|
||||
|
||||
**Solution**: Check system dark mode preference, or implement manual toggle
|
||||
|
||||
## 📖 Documentation & Resources
|
||||
|
||||
### SvelteKit
|
||||
|
||||
- [SvelteKit Docs](https://svelte.dev/docs/kit)
|
||||
- [Svelte 5 Tutorial](https://svelte.dev/tutorial/svelte/welcome)
|
||||
|
||||
### Supabase
|
||||
|
||||
- [Supabase Docs](https://supabase.com/docs)
|
||||
- [Supabase Auth](https://supabase.com/docs/guides/auth)
|
||||
|
||||
### Tailwind CSS
|
||||
|
||||
- [Tailwind Docs](https://tailwindcss.com/docs)
|
||||
- [Tailwind UI](https://tailwindui.com)
|
||||
|
||||
## 💡 Development Tips
|
||||
|
||||
1. **Use Runes ($state, $derived, $effect)** - Modern Svelte 5 reactivity
|
||||
2. **Server-side data loading** - Use `+page.server.ts` for secure data fetching
|
||||
3. **Progressive enhancement** - Forms work without JavaScript
|
||||
4. **Type safety** - Leverage generated types from SvelteKit
|
||||
5. **Component composition** - Use snippets for flexible component APIs
|
||||
|
||||
## ✨ What Makes This Different
|
||||
|
||||
Unlike a migration, this is a **clean, modern implementation** that:
|
||||
|
||||
- ✅ Follows 2025 SvelteKit best practices
|
||||
- ✅ Uses latest Svelte 5 features (Runes)
|
||||
- ✅ Implements SSR-safe authentication
|
||||
- ✅ Provides better developer experience
|
||||
- ✅ Enables easy deployment to web platforms
|
||||
- ✅ Maintains compatibility with same backend
|
||||
|
||||
## 🎯 Success Criteria
|
||||
|
||||
The project is ready for development when:
|
||||
|
||||
- [x] Project structure created
|
||||
- [x] Dependencies installed
|
||||
- [x] TypeScript configured
|
||||
- [x] Tailwind CSS working
|
||||
- [x] Authentication pages created
|
||||
- [x] Dashboard implemented
|
||||
- [ ] Environment variables configured with real credentials
|
||||
- [ ] Database schema verified
|
||||
- [ ] First successful login completed
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
When adding new features:
|
||||
|
||||
1. Create components in `src/lib/components/`
|
||||
2. Add pages in appropriate route groups
|
||||
3. Use server-side data loading for security
|
||||
4. Follow existing code patterns
|
||||
5. Test in both light and dark modes
|
||||
6. Add TypeScript types
|
||||
|
||||
## 📞 Support
|
||||
|
||||
For issues or questions:
|
||||
|
||||
- Check SvelteKit docs
|
||||
- Review Supabase documentation
|
||||
- Check browser console for errors
|
||||
- Verify environment variables
|
||||
|
||||
---
|
||||
|
||||
**Status**: ✅ Core application structure complete and ready for feature development!
|
||||
|
||||
**Next**: Configure Supabase credentials and start building organization/team management features.
|
||||
85
apps/mana/apps/web/src/lib/components/ToastContainer.svelte
Normal file
85
apps/mana/apps/web/src/lib/components/ToastContainer.svelte
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
<script lang="ts">
|
||||
import { toast } from '$lib/stores/toast.svelte';
|
||||
</script>
|
||||
|
||||
{#if toast.toasts.length > 0}
|
||||
<div class="toast-stack">
|
||||
{#each toast.toasts as t (t.id)}
|
||||
<div
|
||||
class="toast-item"
|
||||
class:success={t.type === 'success'}
|
||||
class:error={t.type === 'error'}
|
||||
class:warning={t.type === 'warning'}
|
||||
>
|
||||
<span>{t.message}</span>
|
||||
<button class="dismiss" onclick={() => toast.dismiss(t.id)}>✕</button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.toast-stack {
|
||||
position: fixed;
|
||||
bottom: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
max-width: 24rem;
|
||||
}
|
||||
|
||||
.toast-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
font-size: 0.8125rem;
|
||||
color: white;
|
||||
background: hsl(var(--color-foreground));
|
||||
box-shadow: 0 4px 12px hsl(0 0% 0% / 0.15);
|
||||
animation: slide-in 0.2s ease-out;
|
||||
}
|
||||
|
||||
.toast-item.success {
|
||||
background: hsl(142 71% 45%);
|
||||
}
|
||||
|
||||
.toast-item.error {
|
||||
background: hsl(var(--color-error));
|
||||
}
|
||||
|
||||
.toast-item.warning {
|
||||
background: hsl(45 93% 47%);
|
||||
color: hsl(0 0% 10%);
|
||||
}
|
||||
|
||||
.dismiss {
|
||||
background: none;
|
||||
border: none;
|
||||
color: inherit;
|
||||
opacity: 0.7;
|
||||
cursor: pointer;
|
||||
font-size: 0.75rem;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.dismiss:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@keyframes slide-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
import { syncBilling } from '$lib/stores/sync-billing.svelte';
|
||||
import { creditsService, type CreditBalance } from '$lib/api/credits';
|
||||
import type { BillingInterval } from '$lib/api/sync';
|
||||
import { toast } from '$lib/stores/toast.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
const SYNC_PRICES: Record<BillingInterval, number> = {
|
||||
|
|
@ -22,10 +23,6 @@
|
|||
let error = $state<string | null>(null);
|
||||
let selectedInterval = $state<BillingInterval>('monthly');
|
||||
|
||||
// Toast
|
||||
let toastMessage = $state<string | null>(null);
|
||||
let toastType = $state<'success' | 'error'>('success');
|
||||
|
||||
onMount(async () => {
|
||||
await Promise.all([syncBilling.load(), loadBalance()]);
|
||||
selectedInterval = syncBilling.interval;
|
||||
|
|
@ -45,10 +42,10 @@
|
|||
try {
|
||||
await syncBilling.activate(selectedInterval);
|
||||
await loadBalance();
|
||||
showToast('Cloud Sync aktiviert!', 'success');
|
||||
toast.success('Cloud Sync aktiviert!');
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : 'Aktivierung fehlgeschlagen';
|
||||
showToast(error, 'error');
|
||||
toast.error(error);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
|
|
@ -63,7 +60,7 @@
|
|||
showToast('Cloud Sync deaktiviert', 'success');
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : 'Deaktivierung fehlgeschlagen';
|
||||
showToast(error, 'error');
|
||||
toast.error(error);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
|
|
@ -78,7 +75,7 @@
|
|||
showToast(`Intervall auf ${INTERVAL_LABELS[selectedInterval]} geändert`, 'success');
|
||||
} catch (e) {
|
||||
error = e instanceof Error ? e.message : 'Änderung fehlgeschlagen';
|
||||
showToast(error, 'error');
|
||||
toast.error(error);
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
|
|
@ -95,14 +92,6 @@
|
|||
year: 'numeric',
|
||||
});
|
||||
}
|
||||
|
||||
function showToast(message: string, type: 'success' | 'error') {
|
||||
toastMessage = message;
|
||||
toastType = type;
|
||||
setTimeout(() => {
|
||||
toastMessage = null;
|
||||
}, 4000);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
|
|
@ -267,14 +256,3 @@
|
|||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Toast Notification -->
|
||||
{#if toastMessage}
|
||||
<div
|
||||
class="fixed bottom-4 right-4 z-50 px-4 py-3 rounded-lg shadow-lg {toastType === 'success'
|
||||
? 'bg-green-600 text-white'
|
||||
: 'bg-red-600 text-white'}"
|
||||
>
|
||||
{toastMessage}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
} from '@mana/credits';
|
||||
import { ManaEvents } from '@mana/shared-utils/analytics';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { toast } from '$lib/stores/toast.svelte';
|
||||
|
||||
// ── Credits state ──────────────────────────────────────
|
||||
let balance = $state<CreditBalance | null>(null);
|
||||
|
|
@ -58,8 +59,6 @@
|
|||
);
|
||||
let costFilter = $state<'all' | 'ai' | 'premium'>('all');
|
||||
let processingPackageId = $state<string | null>(null);
|
||||
let toastMessage = $state<string | null>(null);
|
||||
let toastType = $state<'success' | 'error'>('success');
|
||||
|
||||
// ── Derived pricing data ───────────────────────────────
|
||||
const allOperations = $derived(
|
||||
|
|
@ -139,10 +138,10 @@
|
|||
const canceled = params.get('canceled');
|
||||
|
||||
if (success === 'true') {
|
||||
showToast('Zahlung erfolgreich!', 'success');
|
||||
toast.success('\1');
|
||||
history.replaceState({}, '', '/');
|
||||
} else if (canceled === 'true') {
|
||||
showToast('Kauf wurde abgebrochen', 'error');
|
||||
toast.error('Kauf wurde abgebrochen');
|
||||
history.replaceState({}, '', '/');
|
||||
}
|
||||
|
||||
|
|
@ -251,10 +250,7 @@
|
|||
const result = await creditsService.initiatePurchase(pkg.id);
|
||||
window.location.href = result.checkoutUrl;
|
||||
} catch (e) {
|
||||
showToast(
|
||||
e instanceof Error ? e.message : 'Fehler beim Erstellen der Checkout-Session',
|
||||
'error'
|
||||
);
|
||||
toast.error(e instanceof Error ? e.message : 'Fehler beim Erstellen der Checkout-Session');
|
||||
} finally {
|
||||
processingPackageId = null;
|
||||
}
|
||||
|
|
@ -285,7 +281,7 @@
|
|||
const { url } = await subscriptionsService.createCheckout(plan.id, billingInterval);
|
||||
window.location.href = url;
|
||||
} catch (e) {
|
||||
showToast(e instanceof Error ? e.message : 'Fehler beim Checkout', 'error');
|
||||
toast.error(e instanceof Error ? e.message : 'Fehler beim Checkout');
|
||||
} finally {
|
||||
processingPlanId = null;
|
||||
}
|
||||
|
|
@ -297,7 +293,7 @@
|
|||
const { url } = await subscriptionsService.openPortal();
|
||||
window.location.href = url;
|
||||
} catch (e) {
|
||||
showToast(e instanceof Error ? e.message : 'Fehler beim Billing-Portal', 'error');
|
||||
toast.error(e instanceof Error ? e.message : 'Fehler beim Billing-Portal');
|
||||
} finally {
|
||||
openingPortal = false;
|
||||
}
|
||||
|
|
@ -308,10 +304,10 @@
|
|||
cancelingSub = true;
|
||||
try {
|
||||
await subscriptionsService.cancelSubscription();
|
||||
showToast('Abonnement wird zum Ende der Laufzeit gekündigt', 'success');
|
||||
toast.success('\1');
|
||||
await loadData();
|
||||
} catch (e) {
|
||||
showToast(e instanceof Error ? e.message : 'Fehler beim Kündigen', 'error');
|
||||
toast.error(e instanceof Error ? e.message : 'Fehler beim Kündigen');
|
||||
} finally {
|
||||
cancelingSub = false;
|
||||
}
|
||||
|
|
@ -321,20 +317,14 @@
|
|||
reactivatingSub = true;
|
||||
try {
|
||||
await subscriptionsService.reactivateSubscription();
|
||||
showToast('Abonnement wurde reaktiviert', 'success');
|
||||
toast.success('\1');
|
||||
await loadData();
|
||||
} catch (e) {
|
||||
showToast(e instanceof Error ? e.message : 'Fehler beim Reaktivieren', 'error');
|
||||
toast.error(e instanceof Error ? e.message : 'Fehler beim Reaktivieren');
|
||||
} finally {
|
||||
reactivatingSub = false;
|
||||
}
|
||||
}
|
||||
|
||||
function showToast(message: string, type: 'success' | 'error') {
|
||||
toastMessage = message;
|
||||
toastType = type;
|
||||
setTimeout(() => (toastMessage = null), 4000);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="credits-page">
|
||||
|
|
@ -714,10 +704,6 @@
|
|||
{/if}
|
||||
</div>
|
||||
|
||||
{#if toastMessage}
|
||||
<div class="toast" class:toast-error={toastType === 'error'}>{toastMessage}</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.credits-page {
|
||||
padding: 0.75rem;
|
||||
|
|
@ -1518,33 +1504,4 @@
|
|||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.toast {
|
||||
position: fixed;
|
||||
bottom: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 50;
|
||||
padding: 0.75rem 1rem;
|
||||
background: hsl(142 71% 45%);
|
||||
color: white;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 4px 12px hsl(0 0% 0% / 0.15);
|
||||
font-size: 0.8125rem;
|
||||
animation: fadeIn 0.2s ease-out;
|
||||
}
|
||||
|
||||
.toast-error {
|
||||
background: hsl(var(--color-error));
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
import { onMount } from 'svelte';
|
||||
import { profileService, type UserProfile as ApiUserProfile } from '$lib/api/profile';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { toast } from '$lib/stores/toast.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import {
|
||||
EditProfileModal,
|
||||
|
|
@ -24,7 +25,6 @@
|
|||
let showEditModal = $state(false);
|
||||
let showPasswordModal = $state(false);
|
||||
let showDeleteModal = $state(false);
|
||||
let toastMessage = $state<string | null>(null);
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
|
|
@ -45,23 +45,18 @@
|
|||
|
||||
function handleProfileUpdate(user: ApiUserProfile) {
|
||||
apiProfile = user;
|
||||
showToast('Profil erfolgreich aktualisiert');
|
||||
toast.success('Profil erfolgreich aktualisiert');
|
||||
}
|
||||
|
||||
function handlePasswordChange() {
|
||||
showToast('Passwort erfolgreich geändert');
|
||||
toast.success('Passwort erfolgreich geändert');
|
||||
}
|
||||
|
||||
async function handleAccountDeleted() {
|
||||
showToast('Konto wird gelöscht...');
|
||||
toast.info('Konto wird gelöscht...');
|
||||
await authStore.signOut();
|
||||
goto('/login');
|
||||
}
|
||||
|
||||
function showToast(message: string) {
|
||||
toastMessage = message;
|
||||
setTimeout(() => (toastMessage = null), 3000);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="profile-page">
|
||||
|
|
@ -155,10 +150,6 @@
|
|||
onSuccess={handleAccountDeleted}
|
||||
/>
|
||||
|
||||
{#if toastMessage}
|
||||
<div class="toast">{toastMessage}</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.profile-page {
|
||||
display: flex;
|
||||
|
|
@ -289,27 +280,4 @@
|
|||
.account-btn.danger:hover {
|
||||
background: hsl(var(--color-destructive, 0 84% 60%) / 0.08);
|
||||
}
|
||||
.toast {
|
||||
position: fixed;
|
||||
bottom: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 50;
|
||||
padding: 0.75rem 1rem;
|
||||
background: hsl(142 71% 45%);
|
||||
color: white;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 4px 12px hsl(0 0% 0% / 0.15);
|
||||
font-size: 0.875rem;
|
||||
animation: fade-in 0.2s ease-out;
|
||||
}
|
||||
@keyframes fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import type { Component, Snippet } from 'svelte';
|
||||
import ToastContainer from '$lib/components/ToastContainer.svelte';
|
||||
import { onDestroy, setContext, tick } from 'svelte';
|
||||
import { createReminderScheduler } from '@mana/shared-stores';
|
||||
import { todoReminderSource } from '$lib/modules/todo/reminder-source';
|
||||
|
|
@ -1063,6 +1064,8 @@
|
|||
{/if}
|
||||
</AuthGate>
|
||||
|
||||
<ToastContainer />
|
||||
|
||||
<style>
|
||||
.bottom-stack {
|
||||
position: fixed;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue