Replace all marketing placeholder content with real MYeasyCMS content
Some checks failed
Workflow / ʦ TypeScript (push) Failing after 6m12s
Workflow / ⚫️ Test (push) Has been skipped

- Logo: Replace generic Makerkit SVG with MYeasyCMS branded logo (grid icon + styled text)
- Blog: Replace 3 SaaS placeholder posts with 5 real articles (Vereinsverwaltung, SEPA, Website, DSGVO, Mitglieder-Tipps)
- Changelog: Replace 6 generic entries with real feature announcements (Verbandsverwaltung, Fischerei, Dateien, Kurse, Einladungen, i18n)
- Documentation: Rewrite all 20 docs from Makerkit references to MYeasyCMS content
- FAQ: Replace 6 generic SaaS questions with 10 real MYeasyCMS questions
- Navigation: Replace Changelog link with Contact in main nav
- Footer: Reorganize into Product/Company/Legal sections
- Translations: Update all EN marketing strings to match real Com.BISS content
This commit is contained in:
Zaid Marzguioui
2026-04-01 21:09:06 +02:00
parent bbb33aa63d
commit a5bbf42901
49 changed files with 1320 additions and 4735 deletions

View File

@@ -1,279 +1,37 @@
---
title: "Features Overview"
description: "Send emails and notifications to your users."
title: "E-Mail & Newsletter"
description: "Kommunizieren Sie mit Ihren Mitgliedern per E-Mail und Newsletter — direkt aus MYeasyCMS."
publishedAt: 2024-04-11
order: 0
order: 1
status: "published"
---
> **Note:** This is mock/placeholder content for demonstration purposes.
MYeasyCMS integriert E-Mail-Kommunikation und Newsletter-Versand direkt in die Vereinsverwaltung.
The application includes email functionality for transactional messages and user notifications.
## Newsletter erstellen
## Email Configuration
Erstellen Sie Newsletter mit dem integrierten Editor:
### Supabase Email (Default)
1. **Kampagne anlegen** — Betreff und Absender festlegen
2. **Inhalt gestalten** — Text, Bilder und Formatierungen hinzufügen
3. **Empfänger wählen** — Alle Mitglieder, einzelne Abteilungen oder manuelle Auswahl
4. **Vorschau prüfen** — So sieht der Newsletter im Posteingang aus
5. **Versenden** — Sofort oder zu einem geplanten Zeitpunkt
By default, emails are sent through Supabase:
- Authentication emails (sign-up, password reset, magic links)
- Email verification
- Email change confirmation
## Abonnentenverwaltung
### Custom SMTP
- **Anmeldung** — Neue Abonnenten über die Vereinswebsite mit Double-Opt-In
- **Abmeldung** — Automatischer Abmeldelink in jeder E-Mail
- **Statusverfolgung** — Aktive und inaktive Abonnenten im Überblick
For transactional emails, configure your own SMTP provider:
## Vorlagen
```bash
# .env
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=your-username
SMTP_PASSWORD=your-password
SMTP_FROM_EMAIL=noreply@yourdomain.com
SMTP_FROM_NAME=Your App Name
```
Erstellen Sie Vorlagen für wiederkehrende Newsletter — zum Beispiel den monatlichen Vereinsrundbrief oder Einladungen zu Veranstaltungen.
## Sending Emails
## Datenschutz
### Using the Email Service
Der Newsletter-Versand entspricht den DSGVO-Anforderungen:
```typescript
import { sendEmail } from '~/lib/email/send-email';
await sendEmail({
to: 'user@example.com',
subject: 'Welcome to Our App',
html: '<h1>Welcome!</h1><p>Thanks for signing up.</p>',
});
```
### Using Email Templates
Create reusable email templates:
```typescript
// lib/email/templates/welcome-email.tsx
import { EmailTemplate } from '~/lib/email/email-template';
interface WelcomeEmailProps {
name: string;
loginUrl: string;
}
export function WelcomeEmail({ name, loginUrl }: WelcomeEmailProps) {
return (
<EmailTemplate>
<h1>Welcome, {name}!</h1>
<p>We're excited to have you on board.</p>
<a href={loginUrl}>Get Started</a>
</EmailTemplate>
);
}
// Send the email
import { render } from '@react-email/render';
import { WelcomeEmail } from '~/lib/email/templates/welcome-email';
const html = render(
<WelcomeEmail name="John" loginUrl="https://app.com/login" />
);
await sendEmail({
to: 'john@example.com',
subject: 'Welcome to Our App',
html,
});
```
## Email Types
### Transactional Emails
Emails triggered by user actions:
- Welcome emails
- Order confirmations
- Password resets
- Account notifications
- Billing updates
### Marketing Emails
Promotional and engagement emails:
- Product updates
- Feature announcements
- Newsletters
- Onboarding sequences
## Email Providers
### Recommended Providers
**Resend** - Developer-friendly email API
```bash
npm install resend
```
```typescript
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
await resend.emails.send({
from: 'noreply@yourdomain.com',
to: 'user@example.com',
subject: 'Welcome',
html: emailHtml,
});
```
**SendGrid** - Comprehensive email platform
```bash
npm install @sendgrid/mail
```
```typescript
import sgMail from '@sendgrid/mail';
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
await sgMail.send({
to: 'user@example.com',
from: 'noreply@yourdomain.com',
subject: 'Welcome',
html: emailHtml,
});
```
**Postmark** - Fast transactional email
```bash
npm install postmark
```
## In-App Notifications
### Notification System
Send in-app notifications to users:
```typescript
import { createNotification } from '~/lib/notifications';
await createNotification({
userId: user.id,
title: 'New Message',
message: 'You have a new message from John',
type: 'info',
link: '/messages/123',
});
```
### Notification Types
```typescript
type NotificationType = 'info' | 'success' | 'warning' | 'error';
await createNotification({
userId: user.id,
title: 'Payment Successful',
message: 'Your subscription has been renewed',
type: 'success',
});
```
### Fetching Notifications
```typescript
import { getUserNotifications } from '~/lib/notifications';
const notifications = await getUserNotifications(userId, {
limit: 10,
unreadOnly: true,
});
```
### Marking as Read
```typescript
import { markNotificationAsRead } from '~/lib/notifications';
await markNotificationAsRead(notificationId);
```
## Real-time Notifications
Use Supabase Realtime for instant notifications:
```typescript
'use client';
import { useEffect } from 'react';
import { useSupabase } from '@kit/supabase/hooks/use-supabase';
export function NotificationListener() {
const supabase = useSupabase();
useEffect(() => {
const channel = supabase
.channel('notifications')
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'notifications',
filter: `user_id=eq.${userId}`,
},
(payload) => {
// Show toast notification
toast.info(payload.new.title);
}
)
.subscribe();
return () => {
supabase.removeChannel(channel);
};
}, [supabase]);
return null;
}
```
## Email Templates Best Practices
1. **Keep it simple** - Plain text and minimal HTML
2. **Mobile responsive** - Most emails are read on mobile
3. **Clear CTAs** - Make action buttons prominent
4. **Personalize** - Use user's name and relevant data
5. **Test rendering** - Check across email clients
6. **Include plain text** - Always provide text alternative
7. **Unsubscribe link** - Required for marketing emails
## Testing Emails
### Local Development
In development, emails are caught by InBucket:
```
http://localhost:54324
```
### Preview Emails
Use React Email to preview templates:
```bash
npm run email:dev
```
Visit `http://localhost:3001` to see email previews.
## Deliverability Tips
1. **Authenticate your domain** - Set up SPF, DKIM, DMARC
2. **Warm up your domain** - Start with low volumes
3. **Monitor bounce rates** - Keep below 5%
4. **Avoid spam triggers** - Don't use all caps, excessive punctuation
5. **Provide value** - Only send relevant, useful emails
6. **Easy unsubscribe** - Make it one-click simple
- Double-Opt-In für die Anmeldung
- Dokumentation der Einwilligung
- Einfache Abmeldemöglichkeit in jeder E-Mail

View File

@@ -1,16 +1,73 @@
---
title: "Features"
description: "Learn about the built-in features available in MakerKit."
title: "Funktionen"
description: "Überblick über die Kernfunktionen von MYeasyCMS — Mitgliederverwaltung, Finanzen, Kurse, Veranstaltungen und mehr."
publishedAt: 2024-04-11
order: 3
order: 0
status: "published"
---
MakerKit comes with a rich set of features to help you build your SaaS quickly.
MYeasyCMS bietet eine umfassende Palette von Funktionen für die tägliche Vereinsarbeit.
This section covers:
- Team collaboration
- File uploads
- Email functionality
- User management
- And more built-in features
## Mitgliederverwaltung
- **Stammdaten** — Name, Adresse, Kontaktdaten, Geburtsdatum, Fotos
- **Abteilungen** — Mitglieder verschiedenen Abteilungen zuordnen
- **SEPA-Mandate** — Lastschriftmandate direkt im Mitgliedsstamm verwalten
- **Mitgliedsausweise** — Ausweise aus Vorlagen generieren (mit Foto und Barcode)
- **Ein-/Austritte** — Historisierte Dokumentation aller Statusänderungen
- **Import/Export** — Mitgliederlisten aus Excel/CSV importieren und exportieren
- **Statistiken** — Mitgliederentwicklung, Altersstruktur, Abteilungsverteilung
## Finanzen & SEPA
- **Beitragskategorien** — Verschiedene Beitragssätze definieren und zuordnen
- **SEPA-Sammellastschrift** — XML-Dateien (pain.008) für den Bankeinzug erzeugen
- **Rechnungen** — Erstellen, versenden und nachverfolgen
- **Zahlungsverfolgung** — Offene Posten, Mahnwesen, Rückläufer
- **Geschäftsjahre** — Jahresabschluss und Übertrag
## Kursverwaltung
- **Kurse anlegen** — Titel, Beschreibung, Termine, Dozent, Kosten
- **Teilnehmerverwaltung** — Anmeldungen, Wartelisten, Anwesenheit
- **Kalender** — Übersichtliche Terminplanung mit Ferien- und Feiertagsberücksichtigung
- **Kategorien und Orte** — Kurstypen und Veranstaltungsorte verwalten
- **Online-Anmeldung** — Teilnehmer können sich über die Vereinswebsite anmelden
## Veranstaltungen
- **Eventplanung** — Veranstaltungen mit Beschreibung, Ort, Datum und Kapazität
- **Registrierungen** — Online-Anmeldung und Teilnehmerlisten
- **Ferienpässe** — Verwaltung von Ferienprogrammen (entwickelt mit der Stadt Regensburg)
## Newsletter
- **Kampagnen erstellen** — E-Mails mit Vorlagen gestalten
- **Empfänger verwalten** — Versand an Abteilungen, Gruppen oder individuelle Listen
- **An-/Abmeldung** — Automatische Verwaltung mit Double-Opt-In
## Dokumente
- **Vorlagen** — Briefvorlagen, Etiketten, Ausweise, Zertifikate
- **Seriendokumente** — Massengenerierung für alle Mitglieder oder Gruppen
- **Export** — PDF- und Excel-Export
## Site-Builder
- **Drag-and-Drop** — Seiten visuell zusammenbauen
- **Vereinsdaten einbinden** — Kurse, Veranstaltungen und Kontakte direkt anzeigen
- **Responsive** — Automatisch für alle Bildschirmgrößen optimiert
## Fischereiverwaltung
- **Gewässer** — Gewässerverzeichnis mit Details und Karten
- **Besatzmaßnahmen** — Dokumentation von Fischbesatz
- **Fangbücher** — Digitale Fangbuchführung
- **Erlaubnisscheine** — Ausgabe und Verwaltung von Fischereierlaubnisscheinen
- **Statistiken** — Fang- und Besatzauswertungen
## Sitzungsprotokolle
- **Protokolle** — Sitzungsprotokolle mit Tagesordnungspunkten erstellen
- **Aufgaben** — Aufgaben aus Protokollen ableiten und nachverfolgen
- **Beschlüsse** — Beschlüsse dokumentieren und archivieren

View File

@@ -1,398 +1,37 @@
---
title: "File Uploads"
description: "Handle file uploads with Supabase Storage."
title: "Dateien und Dokumente"
description: "Verwalten und generieren Sie Dokumente — Mitgliedsausweise, Rechnungen, Briefe und mehr."
publishedAt: 2024-04-11
order: 2
order: 3
status: "published"
---
> **Note:** This is mock/placeholder content for demonstration purposes.
MYeasyCMS bietet integrierte Dokumentenverwaltung und -generierung.
Enable users to upload and manage files using Supabase Storage.
## Dokumentenvorlagen
## Setup
Erstellen Sie Vorlagen für häufig benötigte Dokumente:
### Create Storage Bucket
- **Mitgliedsausweise** — Mit Foto, Mitgliedsnummer und Barcode
- **Rechnungen** — Für Beiträge, Kursgebühren und sonstige Leistungen
- **Briefe** — Serienbriefe an Mitglieder oder Gruppen
- **Etiketten** — Adressetiketten für den Postversand
- **Zertifikate** — Teilnahmebestätigungen für Kurse und Lehrgänge
- **Berichte** — Mitgliederlisten und Statistiken
```sql
-- Create a public bucket for avatars
INSERT INTO storage.buckets (id, name, public)
VALUES ('avatars', 'avatars', true);
## Seriendokumente
-- Create a private bucket for documents
INSERT INTO storage.buckets (id, name, public)
VALUES ('documents', 'documents', false);
```
Generieren Sie Dokumente für mehrere Mitglieder gleichzeitig. Platzhalter in der Vorlage werden automatisch mit den Mitgliederdaten gefüllt.
### Set Storage Policies
## Dateiverwaltung
```sql
-- Allow users to upload their own avatars
CREATE POLICY "Users can upload their own avatar"
ON storage.objects FOR INSERT
WITH CHECK (
bucket_id = 'avatars' AND
auth.uid()::text = (storage.foldername(name))[1]
);
Laden Sie Dateien direkt in MYeasyCMS hoch und organisieren Sie Ihre Vereinsdokumente:
-- Allow users to view their own avatars
CREATE POLICY "Users can view their own avatar"
ON storage.objects FOR SELECT
USING (
bucket_id = 'avatars' AND
auth.uid()::text = (storage.foldername(name))[1]
);
- Satzung und Geschäftsordnung
- Protokolle und Beschlüsse
- Formulare und Vordrucke
- Fotos und Medien
-- Allow users to delete their own avatars
CREATE POLICY "Users can delete their own avatar"
ON storage.objects FOR DELETE
USING (
bucket_id = 'avatars' AND
auth.uid()::text = (storage.foldername(name))[1]
);
```
## Export
## Upload Component
### Basic File Upload
```tsx
'use client';
import { useState } from 'react';
import { uploadFileAction } from '../_lib/actions';
export function FileUpload() {
const [uploading, setUploading] = useState(false);
const [file, setFile] = useState<File | null>(null);
const handleUpload = async () => {
if (!file) return;
setUploading(true);
const formData = new FormData();
formData.append('file', file);
const result = await uploadFileAction(formData);
if (result.success) {
toast.success('File uploaded successfully');
}
setUploading(false);
};
return (
<div>
<input
type="file"
onChange={(e) => setFile(e.files?.[0] || null)}
accept="image/*"
/>
<button
onClick={handleUpload}
disabled={!file || uploading}
>
{uploading ? 'Uploading...' : 'Upload'}
</button>
</div>
);
}
```
### Server Action
```typescript
'use server';
import { enhanceAction } from '@kit/next/actions';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
export const uploadFileAction = enhanceAction(
async (formData: FormData, user) => {
const file = formData.get('file') as File;
if (!file) {
throw new Error('No file provided');
}
const client = getSupabaseServerClient();
const fileExt = file.name.split('.').pop();
const fileName = `${user.id}/${Date.now()}.${fileExt}`;
const { data, error } = await client.storage
.from('avatars')
.upload(fileName, file, {
cacheControl: '3600',
upsert: false,
});
if (error) throw error;
// Get public URL
const { data: { publicUrl } } = client.storage
.from('avatars')
.getPublicUrl(fileName);
return {
success: true,
url: publicUrl,
path: data.path,
};
},
{ auth: true }
);
```
## Drag and Drop Upload
```tsx
'use client';
import { useCallback } from 'react';
import { useDropzone } from 'react-dropzone';
export function DragDropUpload() {
const onDrop = useCallback(async (acceptedFiles: File[]) => {
for (const file of acceptedFiles) {
const formData = new FormData();
formData.append('file', file);
await uploadFileAction(formData);
}
}, []);
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
accept: {
'image/*': ['.png', '.jpg', '.jpeg', '.gif'],
},
maxSize: 5 * 1024 * 1024, // 5MB
});
return (
<div
{...getRootProps()}
className={cn(
'border-2 border-dashed rounded-lg p-8 text-center cursor-pointer',
isDragActive && 'border-primary bg-primary/10'
)}
>
<input {...getInputProps()} />
{isDragActive ? (
<p>Drop files here...</p>
) : (
<p>Drag and drop files here, or click to select</p>
)}
</div>
);
}
```
## File Validation
### Client-Side Validation
```typescript
function validateFile(file: File) {
const maxSize = 5 * 1024 * 1024; // 5MB
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
if (file.size > maxSize) {
throw new Error('File size must be less than 5MB');
}
if (!allowedTypes.includes(file.type)) {
throw new Error('File type must be JPEG, PNG, or GIF');
}
return true;
}
```
### Server-Side Validation
```typescript
export const uploadFileAction = enhanceAction(
async (formData: FormData, user) => {
const file = formData.get('file') as File;
// Validate file size
if (file.size > 5 * 1024 * 1024) {
throw new Error('File too large');
}
// Validate file type
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
if (!allowedTypes.includes(file.type)) {
throw new Error('Invalid file type');
}
// Validate dimensions for images
if (file.type.startsWith('image/')) {
const dimensions = await getImageDimensions(file);
if (dimensions.width > 4000 || dimensions.height > 4000) {
throw new Error('Image dimensions too large');
}
}
// Continue with upload...
},
{ auth: true }
);
```
## Image Optimization
### Resize on Upload
```typescript
import sharp from 'sharp';
export const uploadAvatarAction = enhanceAction(
async (formData: FormData, user) => {
const file = formData.get('file') as File;
const buffer = Buffer.from(await file.arrayBuffer());
// Resize image
const resized = await sharp(buffer)
.resize(200, 200, {
fit: 'cover',
position: 'center',
})
.jpeg({ quality: 90 })
.toBuffer();
const client = getSupabaseServerClient();
const fileName = `${user.id}/avatar.jpg`;
const { error } = await client.storage
.from('avatars')
.upload(fileName, resized, {
contentType: 'image/jpeg',
upsert: true,
});
if (error) throw error;
return { success: true };
},
{ auth: true }
);
```
## Progress Tracking
```tsx
'use client';
import { useState } from 'react';
export function UploadWithProgress() {
const [progress, setProgress] = useState(0);
const handleUpload = async (file: File) => {
const client = getSupabaseBrowserClient();
const { error } = await client.storage
.from('documents')
.upload(`uploads/${file.name}`, file, {
onUploadProgress: (progressEvent) => {
const percent = (progressEvent.loaded / progressEvent.total) * 100;
setProgress(Math.round(percent));
},
});
if (error) throw error;
};
return (
<div>
<input type="file" onChange={(e) => handleUpload(e.target.files![0])} />
{progress > 0 && (
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className="bg-primary h-2 rounded-full transition-all"
style={{ width: `${progress}%` }}
/>
</div>
)}
</div>
);
}
```
## Downloading Files
### Get Public URL
```typescript
const { data } = client.storage
.from('avatars')
.getPublicUrl('user-id/avatar.jpg');
console.log(data.publicUrl);
```
### Download Private File
```typescript
const { data, error } = await client.storage
.from('documents')
.download('private-file.pdf');
if (data) {
const url = URL.createObjectURL(data);
const a = document.createElement('a');
a.href = url;
a.download = 'file.pdf';
a.click();
}
```
### Generate Signed URL
```typescript
const { data, error } = await client.storage
.from('documents')
.createSignedUrl('private-file.pdf', 3600); // 1 hour
console.log(data.signedUrl);
```
## Deleting Files
```typescript
export const deleteFileAction = enhanceAction(
async (data, user) => {
const client = getSupabaseServerClient();
const { error } = await client.storage
.from('avatars')
.remove([data.path]);
if (error) throw error;
return { success: true };
},
{
schema: z.object({
path: z.string(),
}),
auth: true,
}
);
```
## Best Practices
1. **Validate on both sides** - Client and server
2. **Limit file sizes** - Prevent abuse
3. **Sanitize filenames** - Remove special characters
4. **Use unique names** - Prevent collisions
5. **Optimize images** - Resize before upload
6. **Set storage policies** - Control access
7. **Monitor usage** - Track storage costs
8. **Clean up unused files** - Regular maintenance
9. **Use CDN** - For public files
10. **Implement virus scanning** - For user uploads
Alle generierten Dokumente können als PDF heruntergeladen werden. Listen und Auswertungen sind zusätzlich als Excel-Export verfügbar.

View File

@@ -1,276 +1,32 @@
---
title: "Team Collaboration"
description: "Manage team members, roles, and permissions in your application."
title: "Teamarbeit"
description: "Arbeiten Sie im Team — mit Rollen, Berechtigungen und gemeinsamer Datenpflege."
publishedAt: 2024-04-11
order: 1
order: 2
status: "published"
---
> **Note:** This is mock/placeholder content for demonstration purposes.
MYeasyCMS ist für die Zusammenarbeit im Vereinsvorstand konzipiert. Mehrere Benutzer können gleichzeitig arbeiten.
Enable teams to collaborate effectively with built-in team management features.
## Mehrere Benutzer
## Team Accounts
Fügen Sie beliebig viele Benutzer zu Ihrem Vereins-Account hinzu. Es gibt keine Begrenzung der Benutzeranzahl — alle Tarife enthalten unbegrenzte Zugänge.
The application supports multi-tenant team accounts where multiple users can collaborate.
## Rollenbasierte Zugriffskontrolle
### Creating a Team
Jeder Benutzer sieht nur die Daten, die für seine Aufgabe relevant sind:
Users can create new team accounts:
| Rolle | Zugriff |
|-------|---------|
| Administrator | Alle Module und Einstellungen |
| Kassenwart | Finanzen, Beiträge, Mitgliederdaten |
| Kursleiter | Kursverwaltung, Teilnehmer |
| Schriftführer | Protokolle, Dokumente |
```typescript
import { createTeamAccount } from '~/lib/teams/create-team';
## Einladungen
const team = await createTeamAccount({
name: 'Acme Corp',
slug: 'acme-corp',
ownerId: currentUser.id,
});
```
Neue Benutzer werden per E-Mail eingeladen. Die Einladung enthält einen Registrierungslink, über den der neue Benutzer sein Konto erstellt.
### Team Workspace
## Aktivitätsprotokoll
Each team has its own workspace with isolated data:
- Projects and resources
- Team-specific settings
- Billing and subscription
- Activity logs
## Inviting Members
### Send Invitations
Invite new members to your team:
```typescript
import { inviteTeamMember } from '~/lib/teams/invitations';
await inviteTeamMember({
teamId: team.id,
email: 'member@example.com',
role: 'member',
});
```
### Invitation Flow
1. Owner sends invitation via email
2. Recipient receives email with invitation link
3. Recipient accepts invitation
4. Member gains access to team workspace
### Managing Invitations
```tsx
import { PendingInvitations } from '~/components/teams/pending-invitations';
<PendingInvitations teamId={team.id} />
```
## Roles and Permissions
### Default Roles
**Owner**
- Full access to team and settings
- Manage billing and subscriptions
- Invite and remove members
- Delete team
**Admin**
- Manage team members
- Manage team resources
- Cannot access billing
- Cannot delete team
**Member**
- View team resources
- Create and edit own content
- Limited team settings access
### Custom Roles
Define custom roles with specific permissions:
```typescript
const customRole = {
name: 'Editor',
permissions: [
'read:projects',
'write:projects',
'read:members',
],
};
```
### Checking Permissions
```typescript
import { checkPermission } from '~/lib/teams/permissions';
const canEdit = await checkPermission(userId, teamId, 'write:projects');
if (!canEdit) {
throw new Error('Insufficient permissions');
}
```
## Member Management
### Listing Members
```typescript
import { getTeamMembers } from '~/lib/teams/members';
const members = await getTeamMembers(teamId);
```
### Updating Member Role
```typescript
import { updateMemberRole } from '~/lib/teams/members';
await updateMemberRole({
memberId: member.id,
role: 'admin',
});
```
### Removing Members
```typescript
import { removeMember } from '~/lib/teams/members';
await removeMember(memberId);
```
## Team Settings
### Updating Team Info
```tsx
'use client';
import { useForm } from 'react-hook-form';
import { updateTeamAction } from '../_lib/server/actions';
export function TeamSettingsForm({ team }) {
const { register, handleSubmit } = useForm({
defaultValues: {
name: team.name,
description: team.description,
},
});
const onSubmit = async (data) => {
await updateTeamAction({ teamId: team.id, ...data });
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('name')} placeholder="Team name" />
<textarea {...register('description')} placeholder="Description" />
<button type="submit">Save Changes</button>
</form>
);
}
```
### Team Avatar
```typescript
import { uploadTeamAvatar } from '~/lib/teams/avatar';
const avatarUrl = await uploadTeamAvatar({
teamId: team.id,
file: avatarFile,
});
```
## Activity Log
Track team activity for transparency:
```typescript
import { logActivity } from '~/lib/teams/activity';
await logActivity({
teamId: team.id,
userId: user.id,
action: 'member_invited',
metadata: {
invitedEmail: 'new@example.com',
},
});
```
### Viewing Activity
```typescript
import { getTeamActivity } from '~/lib/teams/activity';
const activities = await getTeamActivity(teamId, {
limit: 50,
offset: 0,
});
```
## Team Switching
Allow users to switch between their teams:
```tsx
'use client';
import { useTeamAccountWorkspace } from '@kit/team-accounts/hooks/use-team-account-workspace';
export function TeamSwitcher() {
const { accounts, account } = useTeamAccountWorkspace();
return (
<select
value={account.id}
onChange={(e) => switchTeam(e.target.value)}
>
{accounts.map((team) => (
<option key={team.id} value={team.id}>
{team.name}
</option>
))}
</select>
);
}
```
## Notifications
### Member Joined
```typescript
await createNotification({
teamId: team.id,
title: 'New Member',
message: `${user.name} joined the team`,
type: 'info',
});
```
### Role Changed
```typescript
await createNotification({
userId: member.userId,
title: 'Role Updated',
message: `Your role was changed to ${newRole}`,
type: 'info',
});
```
## Best Practices
1. **Clear role hierarchy** - Define roles that make sense for your use case
2. **Principle of least privilege** - Give minimum required permissions
3. **Audit trail** - Log important team actions
4. **Easy onboarding** - Simple invitation process
5. **Self-service** - Let members manage their own settings
6. **Transparent billing** - Show usage and costs clearly
Unter **Verwaltung → Audit** sehen Administratoren, wer wann welche Änderungen vorgenommen hat. Das Protokoll hilft bei der Nachverfolgung und Qualitätssicherung.