- Integrated PlaceholderHelp component into multiple forms including WelcomeForm, TagsManager, and StatsForm to provide contextual hints for available placeholders. - Updated DiscordEmbedBuilder to support placeholder presets, improving user experience when creating embeds. - Enhanced localization files to include new placeholder descriptions, ensuring clarity in both English and German. - Refactored related components to maintain consistency in placeholder handling across the application.
100 lines
4.4 KiB
TypeScript
100 lines
4.4 KiB
TypeScript
'use client';
|
|
|
|
import type { BirthdayConfigDashboard } from '@nexumi/shared';
|
|
import { useId } from 'react';
|
|
import { FieldAnchor } from '@/components/layout/field-anchor';
|
|
import { useTranslations } from '@/components/locale-provider';
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { DiscordChannelSelect } from '@/components/ui/discord-channel-select';
|
|
import { DiscordRoleSelect } from '@/components/ui/discord-role-select';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Label } from '@/components/ui/label';
|
|
import { PlaceholderHelp } from '@/components/ui/placeholder-help';
|
|
import { Switch } from '@/components/ui/switch';
|
|
import { Textarea } from '@/components/ui/textarea';
|
|
import type { SettingsSaveResult } from '@/components/settings/settings-form';
|
|
import { SettingsForm } from '@/components/settings/settings-form';
|
|
|
|
async function saveBirthdays(guildId: string, value: BirthdayConfigDashboard): Promise<SettingsSaveResult> {
|
|
try {
|
|
const response = await fetch(`/api/guilds/${guildId}/birthdays`, {
|
|
method: 'PATCH',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(value)
|
|
});
|
|
if (!response.ok) {
|
|
const body = (await response.json().catch(() => null)) as { error?: string } | null;
|
|
return { ok: false, error: body?.error ?? `Request failed with status ${response.status}` };
|
|
}
|
|
return { ok: true };
|
|
} catch {
|
|
return { ok: false, error: 'Network error' };
|
|
}
|
|
}
|
|
|
|
interface BirthdaysFormProps {
|
|
guildId: string;
|
|
initialValue: BirthdayConfigDashboard;
|
|
}
|
|
|
|
export function BirthdaysForm({ guildId, initialValue }: BirthdaysFormProps) {
|
|
const t = useTranslations();
|
|
const timezoneId = useId();
|
|
|
|
return (
|
|
<SettingsForm<BirthdayConfigDashboard> initialValue={initialValue} onSave={(value) => saveBirthdays(guildId, value)}>
|
|
{({ value, setValue }) => (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>{t('modulePages.birthdays.title')}</CardTitle>
|
|
<CardDescription>{t('modulePages.birthdays.description')}</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<FieldAnchor id="field-birthdays-enabled">
|
|
<div className="flex items-center justify-between rounded-md border border-border p-4">
|
|
<Label>{t('modulePages.birthdays.enabledLabel')}</Label>
|
|
<Switch checked={value.enabled} onCheckedChange={(enabled) => setValue((prev) => ({ ...prev, enabled }))} />
|
|
</div>
|
|
</FieldAnchor>
|
|
<div className="grid gap-4 sm:grid-cols-3">
|
|
<FieldAnchor id="field-birthdays-channel">
|
|
<div className="space-y-2">
|
|
<Label>{t('modulePages.birthdays.channelId')}</Label>
|
|
<DiscordChannelSelect
|
|
guildId={guildId}
|
|
value={value.channelId}
|
|
onChange={(channelId) => setValue((prev) => ({ ...prev, channelId }))}
|
|
/>
|
|
</div>
|
|
</FieldAnchor>
|
|
<FieldAnchor id="field-birthdays-role">
|
|
<div className="space-y-2">
|
|
<Label>{t('modulePages.birthdays.roleId')}</Label>
|
|
<DiscordRoleSelect
|
|
guildId={guildId}
|
|
value={value.roleId}
|
|
onChange={(roleId) => setValue((prev) => ({ ...prev, roleId }))}
|
|
/>
|
|
</div>
|
|
</FieldAnchor>
|
|
<FieldAnchor id="field-birthdays-timezone">
|
|
<div className="space-y-2">
|
|
<Label htmlFor={timezoneId}>{t('modulePages.birthdays.timezone')}</Label>
|
|
<Input id={timezoneId} value={value.timezone} onChange={(event) => setValue((prev) => ({ ...prev, timezone: event.target.value }))} placeholder="Europe/Berlin" />
|
|
</div>
|
|
</FieldAnchor>
|
|
</div>
|
|
<FieldAnchor id="field-birthdays-message">
|
|
<div className="space-y-2">
|
|
<Label>{t('modulePages.birthdays.message')}</Label>
|
|
<Textarea value={value.message ?? ''} onChange={(event) => setValue((prev) => ({ ...prev, message: event.target.value || null }))} placeholder={t('modulePages.birthdays.messagePlaceholder')} />
|
|
<PlaceholderHelp preset="birthdays" />
|
|
</div>
|
|
</FieldAnchor>
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
</SettingsForm>
|
|
);
|
|
}
|