deploy #2

Merged
smueller merged 24 commits from deploy into main 2026-07-29 07:44:03 +00:00
5 changed files with 51 additions and 5 deletions
Showing only changes of commit b5bf214d7f - Show all commits

View File

@@ -94,18 +94,25 @@ export function TicketCategoriesManager({ guildId, initialCategories }: TicketCa
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: JSON.stringify({
name: category.name, name: category.name,
description: category.description, description: category.description?.trim() ? category.description : null,
emoji: category.emoji, emoji: category.emoji?.trim() ? category.emoji : null,
supportRoleIds: category.supportRoleIds supportRoleIds: category.supportRoleIds
}) })
}); });
const body = (await response.json().catch(() => null)) as const body = (await response.json().catch(() => null)) as
| (TicketCategoryDashboard & { error?: string; code?: string }) | (TicketCategoryDashboard & { error?: string; code?: string })
| null; | null;
if (!response.ok || !body) { if (!response.ok || !body || !body.id) {
toast.error(panelSyncErrorMessage(body?.code, body?.error, t)); toast.error(panelSyncErrorMessage(body?.code, body?.error, t));
return; return;
} }
setCategories((prev) =>
prev.map((entry) =>
entry.clientKey === category.clientKey
? { ...body, clientKey: entry.clientKey, saving: false }
: entry
)
);
toast.success(t('common.saveSuccess')); toast.success(t('common.saveSuccess'));
} catch { } catch {
toast.error(t('common.saveError')); toast.error(t('common.saveError'));

View File

@@ -105,7 +105,14 @@ async function maybeSyncTicketPanel(guildId: string): Promise<void> {
if (!config?.enabled || !config.panelChannelId) { if (!config?.enabled || !config.panelChannelId) {
return; return;
} }
await syncTicketPanel(guildId); // Category CRUD must succeed even if Discord panel sync fails — otherwise
// support roles / category fields appear "unsaved" in the UI while already
// persisted in Postgres.
try {
await syncTicketPanel(guildId);
} catch (error) {
console.warn('[tickets] panel sync after category change failed', { guildId, error });
}
} }
export async function updateTicketConfigDashboard( export async function updateTicketConfigDashboard(

View File

@@ -533,6 +533,21 @@
- [ ] Close aus Dashboard → Ticket verschwindet aus der Liste, Kanal/Thread wird geschlossen - [ ] Close aus Dashboard → Ticket verschwindet aus der Liste, Kanal/Thread wird geschlossen
- [ ] Refresh-Button aktualisiert die Liste - [ ] Refresh-Button aktualisiert die Liste
## Post-Phase Ticket Support-Rollen Speichern (Bugfix)
### Abgeschlossen (Code)
- Root cause: Kategorie-Save warf Fehler wenn Panel-Sync scheiterte → UI „Speichern fehlgeschlagen“, obwohl `supportRoleIds` schon in Postgres lagen
- `maybeSyncTicketPanel` fängt Sync-Fehler ab; Kategorie-CRUD bleibt erfolgreich
- Nach Speichern: Response inkl. `supportRoleIds` in die UI übernommen
- Zod: `supportRoleIds` jetzt `SnowflakeSchema[]`
### Manuell testen
- [ ] Kategorie: Support-Rolle wählen → Speichern → Erfolgstoast
- [ ] Seite neu laden → Rolle ist noch ausgewählt
- [ ] Neues Ticket → Support-Rolle wird gepingt / in Thread aufgenommen
## Post-Phase Ban/Kick-Reason Logging + Verification Fail Event (Status: implementiert) ## Post-Phase Ban/Kick-Reason Logging + Verification Fail Event (Status: implementiert)
### Abgeschlossen (Code) ### Abgeschlossen (Code)

View File

@@ -15,6 +15,7 @@ import {
SuggestionActionSchema, SuggestionActionSchema,
TagDashboardCreateSchema, TagDashboardCreateSchema,
TicketCategoryDashboardCreateSchema, TicketCategoryDashboardCreateSchema,
TicketCategoryDashboardUpdateSchema,
TicketConfigDashboardPatchSchema, TicketConfigDashboardPatchSchema,
WelcomeConfigDashboardPatchSchema WelcomeConfigDashboardPatchSchema
} from './dashboard.js'; } from './dashboard.js';
@@ -99,6 +100,22 @@ describe('dashboard schemas', () => {
expect(parsed.supportRoleIds).toHaveLength(1); expect(parsed.supportRoleIds).toHaveLength(1);
}); });
it('validates ticket category update with support roles', () => {
const parsed = TicketCategoryDashboardUpdateSchema.parse({
supportRoleIds: ['123456789012345678', '234567890123456789']
});
expect(parsed.supportRoleIds).toEqual(['123456789012345678', '234567890123456789']);
});
it('rejects invalid support role ids on ticket category create', () => {
expect(() =>
TicketCategoryDashboardCreateSchema.parse({
name: 'Support',
supportRoleIds: ['not-a-snowflake']
})
).toThrow();
});
it('validates giveaway creation input', () => { it('validates giveaway creation input', () => {
const parsed = GiveawayCreateDashboardSchema.parse({ const parsed = GiveawayCreateDashboardSchema.parse({
channelId: '123456789012345678', channelId: '123456789012345678',

View File

@@ -403,7 +403,7 @@ export const TicketCategoryDashboardSchema = z.object({
name: z.string().min(1).max(100), name: z.string().min(1).max(100),
description: z.string().max(500).nullable().optional(), description: z.string().max(500).nullable().optional(),
emoji: z.string().max(80).nullable().optional(), emoji: z.string().max(80).nullable().optional(),
supportRoleIds: z.array(z.string()) supportRoleIds: z.array(SnowflakeSchema)
}); });
export type TicketCategoryDashboard = z.infer<typeof TicketCategoryDashboardSchema>; export type TicketCategoryDashboard = z.infer<typeof TicketCategoryDashboardSchema>;