Fix ticket category saving and synchronization error handling
- Updated the ticket category saving logic to handle empty description and emoji fields, ensuring they are set to null if trimmed. - Enhanced error handling in the `maybeSyncTicketPanel` function to prevent UI errors when synchronization fails, allowing category CRUD operations to succeed. - Updated the response handling to include support role IDs in the UI after saving, improving data consistency. - Added validation for support role IDs in the dashboard schemas to ensure correct data types are used.
This commit is contained in:
@@ -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'));
|
||||||
|
|||||||
@@ -105,7 +105,14 @@ async function maybeSyncTicketPanel(guildId: string): Promise<void> {
|
|||||||
if (!config?.enabled || !config.panelChannelId) {
|
if (!config?.enabled || !config.panelChannelId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 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);
|
await syncTicketPanel(guildId);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[tickets] panel sync after category change failed', { guildId, error });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateTicketConfigDashboard(
|
export async function updateTicketConfigDashboard(
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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>;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user