feat: vollständiges Dashboard-Redesign mit HeroUI - monolithische App.tsx aufgelöst, 16 Seiten, Context-API, collapsible Sidebar, neues Dashboard-Layout
Some checks failed
Deploy Discord Bot / deploy (push) Has been cancelled
Some checks failed
Deploy Discord Bot / deploy (push) Has been cancelled
This commit is contained in:
511
frontend/src/context/AppContext.tsx
Normal file
511
frontend/src/context/AppContext.tsx
Normal file
@@ -0,0 +1,511 @@
|
||||
import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react';
|
||||
import { apiFetch } from '../utils/api';
|
||||
import type {
|
||||
AppConfig, User, Guild, NavKey, TicketRecord, StatusService,
|
||||
EventItem, ReactionRoleSet, ModuleItem, LogEntry, SettingsState,
|
||||
SupportLoginConfig, SupportLoginStatus, RegisterForm, RegisterFormField,
|
||||
RegisterApplication, MusicSession
|
||||
} from '../types';
|
||||
|
||||
const appConfig: AppConfig = (window as any).__PAPO__ || {};
|
||||
|
||||
type AppState = {
|
||||
user: User | null;
|
||||
guilds: Guild[];
|
||||
currentGuildId: string;
|
||||
section: NavKey;
|
||||
guildInfo: any;
|
||||
overview: any;
|
||||
activity: any;
|
||||
logs: LogEntry[];
|
||||
tickets: TicketRecord[];
|
||||
pipeline: Record<string, TicketRecord[]>;
|
||||
sla: any;
|
||||
automations: any[];
|
||||
kbArticles: any[];
|
||||
settings: SettingsState;
|
||||
modules: ModuleItem[];
|
||||
birthday: any;
|
||||
reactionRoles: ReactionRoleSet[];
|
||||
statuspage: any;
|
||||
serverStats: any;
|
||||
events: EventItem[];
|
||||
admin: any;
|
||||
statusMessage: string;
|
||||
loading: boolean;
|
||||
supportLogin: { config: SupportLoginConfig; status: SupportLoginStatus; supportRoleId?: string } | null;
|
||||
registerForms: RegisterForm[];
|
||||
registerApps: RegisterApplication[];
|
||||
musicStatus: { activeGuilds: number; sessions: MusicSession[] };
|
||||
};
|
||||
|
||||
type AppContextType = AppState & {
|
||||
setCurrentGuildId: (id: string) => void;
|
||||
setSection: (key: NavKey) => void;
|
||||
setSettings: (s: SettingsState | ((prev: SettingsState) => SettingsState)) => void;
|
||||
setBirthday: (s: any | ((prev: any) => any)) => void;
|
||||
setSupportLogin: (s: any | ((prev: any) => any)) => void;
|
||||
setStatusDraft: (s: any | ((prev: any) => any)) => void;
|
||||
setStatsDraft: (s: any | ((prev: any) => any)) => void;
|
||||
setStatusMessage: (msg: string) => void;
|
||||
loadGuildData: (guildId: string) => Promise<void>;
|
||||
saveSettingsPayload: (payload: Record<string, any>, okMessage: string) => Promise<void>;
|
||||
saveBirthday: () => Promise<void>;
|
||||
saveStatuspage: () => Promise<void>;
|
||||
saveServerStats: () => Promise<void>;
|
||||
toggleModule: (key: string, enabled: boolean) => Promise<void>;
|
||||
handleLogout: () => void;
|
||||
loadTicketData: (guildId: string) => Promise<void>;
|
||||
loadTicketMessages: (ticketId: string) => Promise<void>;
|
||||
updateTicketStatus: (ticketId: string, status: string) => Promise<void>;
|
||||
closeTicket: (ticketId: string) => Promise<void>;
|
||||
saveAutomation: () => Promise<void>;
|
||||
saveKbArticle: () => Promise<void>;
|
||||
updateKbArticle: (id: string) => Promise<void>;
|
||||
deleteKbArticle: (id: string) => Promise<void>;
|
||||
updateAutomation: (id: string) => Promise<void>;
|
||||
deleteAutomation: (id: string) => Promise<void>;
|
||||
saveSupportLogin: () => Promise<void>;
|
||||
saveForm: () => Promise<void>;
|
||||
deleteForm: (id: string) => Promise<void>;
|
||||
sendFormPanel: (formId: string) => Promise<void>;
|
||||
addStatusService: () => Promise<void>;
|
||||
deleteStatusService: (id: string) => Promise<void>;
|
||||
addStatsItem: () => Promise<void>;
|
||||
deleteStatsItem: (index: number) => Promise<void>;
|
||||
saveEvent: () => Promise<void>;
|
||||
deleteEvent: (id: string) => Promise<void>;
|
||||
saveReactionRole: () => Promise<void>;
|
||||
ticketTab: string;
|
||||
setTicketTab: (tab: string) => void;
|
||||
automationDraft: any;
|
||||
setAutomationDraft: (s: any | ((prev: any) => any)) => void;
|
||||
kbDraft: any;
|
||||
setKbDraft: (s: any | ((prev: any) => any)) => void;
|
||||
eventDraft: any;
|
||||
setEventDraft: (s: any | ((prev: any) => any)) => void;
|
||||
statusDraft: any;
|
||||
statsDraft: any;
|
||||
reactionDraft: any;
|
||||
setReactionDraft: (s: any | ((prev: any) => any)) => void;
|
||||
formDraft: any;
|
||||
setFormDraft: (s: any | ((prev: any) => any)) => void;
|
||||
editingFormId: string | null;
|
||||
setEditingFormId: (id: string | null) => void;
|
||||
registerTab: string;
|
||||
setRegisterTab: (tab: string) => void;
|
||||
statusServiceDraft: any;
|
||||
setStatusServiceDraft: (s: any | ((prev: any) => any)) => void;
|
||||
statsItemDraft: any;
|
||||
setStatsItemDraft: (s: any | ((prev: any) => any)) => void;
|
||||
ticketDetail: TicketRecord | null;
|
||||
setTicketDetail: (t: TicketRecord | null) => void;
|
||||
ticketMessages: any[];
|
||||
kbEditDraft: any;
|
||||
setKbEditDraft: (s: any | ((prev: any) => any)) => void;
|
||||
automationEditDraft: any;
|
||||
setAutomationEditDraft: (s: any | ((prev: any) => any)) => void;
|
||||
};
|
||||
|
||||
const AppContext = createContext<AppContextType | null>(null);
|
||||
|
||||
export function AppProvider({ children }: { children: ReactNode }) {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
const [guilds, setGuilds] = useState<Guild[]>([]);
|
||||
const [currentGuildId, setCurrentGuildId] = useState(appConfig.initialGuildId || '');
|
||||
const [section, setSectionState] = useState<NavKey>('overview');
|
||||
const [guildInfo, setGuildInfo] = useState<any>(null);
|
||||
const [overview, setOverview] = useState<any>(null);
|
||||
const [activity, setActivity] = useState<any>(null);
|
||||
const [logs, setLogs] = useState<LogEntry[]>([]);
|
||||
const [tickets, setTickets] = useState<TicketRecord[]>([]);
|
||||
const [pipeline, setPipeline] = useState<Record<string, TicketRecord[]>>({});
|
||||
const [sla, setSla] = useState<any>({ supporters: [], days: [] });
|
||||
const [automations, setAutomations] = useState<any[]>([]);
|
||||
const [kbArticles, setKbArticles] = useState<any[]>([]);
|
||||
const [settings, setSettings] = useState<SettingsState>({});
|
||||
const [modules, setModules] = useState<ModuleItem[]>([]);
|
||||
const [birthday, setBirthday] = useState<any>({ config: {}, birthdays: [] });
|
||||
const [reactionRoles, setReactionRoles] = useState<ReactionRoleSet[]>([]);
|
||||
const [statuspage, setStatuspage] = useState<any>({ services: [] });
|
||||
const [serverStats, setServerStats] = useState<any>({ items: [] });
|
||||
const [events, setEvents] = useState<EventItem[]>([]);
|
||||
const [admin, setAdmin] = useState<any>({ overview: null, activity: null, logs: [] });
|
||||
const [statusMessage, setStatusMessage] = useState('');
|
||||
const [ticketTab, setTicketTab] = useState('overview');
|
||||
const [automationDraft, setAutomationDraft] = useState({ name: '', conditionValue: '', actionValue: '' });
|
||||
const [kbDraft, setKbDraft] = useState({ title: '', keywords: '', content: '' });
|
||||
const [eventDraft, setEventDraft] = useState({ title: '', description: '', channelId: '', startsAt: '' });
|
||||
const [statusDraft, setStatusDraft] = useState<any>(null);
|
||||
const [statsDraft, setStatsDraft] = useState<any>(null);
|
||||
const [reactionDraft, setReactionDraft] = useState({ title: '', channelId: '', entries: '' });
|
||||
const [supportLogin, setSupportLogin] = useState<{ config: SupportLoginConfig; status: SupportLoginStatus; supportRoleId?: string } | null>(null);
|
||||
const [registerForms, setRegisterForms] = useState<RegisterForm[]>([]);
|
||||
const [registerApps, setRegisterApps] = useState<RegisterApplication[]>([]);
|
||||
const [formDraft, setFormDraft] = useState({ name: '', description: '', reviewChannelId: '', notifyRoleIds: '', fields: '' });
|
||||
const [editingFormId, setEditingFormId] = useState<string | null>(null);
|
||||
const [registerTab, setRegisterTab] = useState('forms');
|
||||
const [musicStatus, setMusicStatus] = useState<{ activeGuilds: number; sessions: MusicSession[] }>({ activeGuilds: 0, sessions: [] });
|
||||
const [kbEditDraft, setKbEditDraft] = useState<{ id: string; title: string; keywords: string; content: string } | null>(null);
|
||||
const [automationEditDraft, setAutomationEditDraft] = useState<{ id: string; name: string; conditionValue: string; actionValue: string } | null>(null);
|
||||
const [statusServiceDraft, setStatusServiceDraft] = useState<{ id?: string; name: string; url: string; status: string }>({ name: '', url: '', status: 'unknown' });
|
||||
const [statsItemDraft, setStatsItemDraft] = useState<{ id?: string; label: string; type: string }>({ label: '', type: 'members' });
|
||||
const [ticketDetail, setTicketDetail] = useState<TicketRecord | null>(null);
|
||||
const [ticketMessages, setTicketMessages] = useState<any[]>([]);
|
||||
|
||||
const setSection = useCallback((key: NavKey) => {
|
||||
setSectionState(key);
|
||||
window.location.hash = key;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const hash = window.location.hash.replace('#', '') as NavKey;
|
||||
const validKeys: NavKey[] = ['overview', 'tickets', 'supportlogin', 'automod', 'welcome', 'dynamicvoice', 'birthday', 'reactionroles', 'statuspage', 'serverstats', 'register', 'music', 'settings', 'modules', 'events', 'admin'];
|
||||
if (validKeys.includes(hash)) setSectionState(hash);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentGuildId) loadGuildData(currentGuildId);
|
||||
}, [currentGuildId]);
|
||||
|
||||
useEffect(() => { bootstrap(); }, []);
|
||||
|
||||
async function bootstrap() {
|
||||
try {
|
||||
const me = await apiFetch<{ user: User }>('/me');
|
||||
const guildRes = await apiFetch<{ guilds: Guild[] }>('/guilds');
|
||||
setUser(me.user);
|
||||
setGuilds(guildRes.guilds || []);
|
||||
if (!currentGuildId && guildRes.guilds?.length) setCurrentGuildId(guildRes.guilds[0].id);
|
||||
} finally { setLoading(false); }
|
||||
}
|
||||
|
||||
async function loadTicketData(guildId: string) {
|
||||
try {
|
||||
const [ticketRes, pipelineRes, slaRes, automationRes, kbRes] = await Promise.all([
|
||||
apiFetch<any>(`/tickets?guildId=${encodeURIComponent(guildId)}`),
|
||||
apiFetch<any>(`/tickets/pipeline?guildId=${encodeURIComponent(guildId)}`),
|
||||
apiFetch<any>(`/tickets/sla?guildId=${encodeURIComponent(guildId)}&range=30`),
|
||||
apiFetch<any>(`/automations?guildId=${encodeURIComponent(guildId)}`),
|
||||
apiFetch<any>(`/kb?guildId=${encodeURIComponent(guildId)}`)
|
||||
]);
|
||||
setTickets(ticketRes.tickets || []);
|
||||
setPipeline(pipelineRes.pipeline || {});
|
||||
setSla(slaRes || { supporters: [], days: [] });
|
||||
setAutomations(automationRes.rules || []);
|
||||
setKbArticles(kbRes.articles || []);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
async function loadGuildData(guildId: string) {
|
||||
setStatusMessage('Lade Daten...');
|
||||
try {
|
||||
const [guildInfoRes, overviewRes, activityRes, logsRes, settingsRes, modulesRes,
|
||||
birthdayRes, reactionRes, statusRes, statsRes, eventsRes, supportLoginRes,
|
||||
registerFormsRes, registerAppsRes] = await Promise.all([
|
||||
apiFetch<any>(`/guild/info?guildId=${encodeURIComponent(guildId)}`),
|
||||
apiFetch<any>(`/overview?guildId=${encodeURIComponent(guildId)}`),
|
||||
apiFetch<any>(`/guild/activity?guildId=${encodeURIComponent(guildId)}`),
|
||||
apiFetch<any>(`/guild/logs?guildId=${encodeURIComponent(guildId)}`),
|
||||
apiFetch<any>(`/settings?guildId=${encodeURIComponent(guildId)}`),
|
||||
apiFetch<any>(`/modules?guildId=${encodeURIComponent(guildId)}`),
|
||||
apiFetch<any>(`/birthday?guildId=${encodeURIComponent(guildId)}`),
|
||||
apiFetch<any>(`/reactionroles?guildId=${encodeURIComponent(guildId)}`),
|
||||
apiFetch<any>(`/statuspage?guildId=${encodeURIComponent(guildId)}`),
|
||||
apiFetch<any>(`/server-stats?guildId=${encodeURIComponent(guildId)}`),
|
||||
apiFetch<any>(`/events?guildId=${encodeURIComponent(guildId)}`),
|
||||
apiFetch<any>(`/tickets/support-login?guildId=${encodeURIComponent(guildId)}`),
|
||||
apiFetch<any>(`/register/forms?guildId=${encodeURIComponent(guildId)}`),
|
||||
apiFetch<any>(`/register/apps?guildId=${encodeURIComponent(guildId)}`)
|
||||
]);
|
||||
setGuildInfo(guildInfoRes.guild || null);
|
||||
setOverview(overviewRes);
|
||||
setMusicStatus(overviewRes.music || { activeGuilds: 0, sessions: [] });
|
||||
setActivity(activityRes.activity || {});
|
||||
setLogs(logsRes.logs || []);
|
||||
setSettings(settingsRes.settings || {});
|
||||
setModules(modulesRes.modules || []);
|
||||
setBirthday(birthdayRes);
|
||||
setReactionRoles(reactionRes.sets || []);
|
||||
setStatuspage(statusRes.config || { services: [] });
|
||||
setServerStats(statsRes.config || { items: [] });
|
||||
setStatsDraft(statsRes.config || { items: [] });
|
||||
setStatusDraft(statusRes.config || { services: [] });
|
||||
setEvents(eventsRes.events || []);
|
||||
setSupportLogin(supportLoginRes);
|
||||
setRegisterForms(registerFormsRes.forms || []);
|
||||
setRegisterApps(registerAppsRes.applications || []);
|
||||
setReactionDraft({ title: '', channelId: '', entries: '' });
|
||||
await Promise.all([loadTicketData(guildId), loadAdminData()]);
|
||||
setStatusMessage('');
|
||||
} catch { setStatusMessage('Daten konnten nicht geladen werden'); }
|
||||
}
|
||||
|
||||
async function loadAdminData() {
|
||||
if (!user?.isAdmin) return;
|
||||
try {
|
||||
const [overviewRes, , logsRes] = await Promise.all([
|
||||
apiFetch<any>('/admin/overview'),
|
||||
apiFetch<any>('/admin/activity'),
|
||||
apiFetch<any>('/admin/logs')
|
||||
]);
|
||||
setAdmin({ overview: overviewRes, activity: null, logs: logsRes.logs || [] });
|
||||
} catch {}
|
||||
}
|
||||
|
||||
async function saveSettingsPayload(payload: Record<string, any>, okMessage: string) {
|
||||
if (!currentGuildId) return;
|
||||
await apiFetch('/settings', { method: 'POST', body: JSON.stringify({ guildId: currentGuildId, ...payload }) });
|
||||
setStatusMessage(okMessage);
|
||||
await loadGuildData(currentGuildId);
|
||||
}
|
||||
|
||||
async function saveBirthday() {
|
||||
await apiFetch('/birthday', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
guildId: currentGuildId,
|
||||
enabled: birthday.config?.enabled ?? true,
|
||||
channelId: birthday.config?.channelId || '',
|
||||
sendHour: birthday.config?.sendHour || 9,
|
||||
messageTemplate: birthday.config?.messageTemplate || ''
|
||||
})
|
||||
});
|
||||
setStatusMessage('Birthday gespeichert');
|
||||
await loadGuildData(currentGuildId);
|
||||
}
|
||||
|
||||
async function saveStatuspage() {
|
||||
await apiFetch('/statuspage', { method: 'POST', body: JSON.stringify({ guildId: currentGuildId, config: statusDraft }) });
|
||||
setStatusMessage('Statuspage gespeichert');
|
||||
await loadGuildData(currentGuildId);
|
||||
}
|
||||
|
||||
async function saveServerStats() {
|
||||
await apiFetch('/server-stats', { method: 'POST', body: JSON.stringify({ guildId: currentGuildId, config: statsDraft }) });
|
||||
setStatusMessage('Server Stats gespeichert');
|
||||
await loadGuildData(currentGuildId);
|
||||
}
|
||||
|
||||
async function saveEvent() {
|
||||
if (!eventDraft.title) return;
|
||||
await apiFetch('/events', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
guildId: currentGuildId, title: eventDraft.title,
|
||||
description: eventDraft.description, channelId: eventDraft.channelId || undefined,
|
||||
startsAt: eventDraft.startsAt || undefined
|
||||
})
|
||||
});
|
||||
setEventDraft({ title: '', description: '', channelId: '', startsAt: '' });
|
||||
await loadGuildData(currentGuildId);
|
||||
setStatusMessage('Event gespeichert');
|
||||
}
|
||||
|
||||
async function deleteEvent(id: string) {
|
||||
await apiFetch(`/events/${id}`, { method: 'DELETE', body: JSON.stringify({ guildId: currentGuildId }) });
|
||||
await loadGuildData(currentGuildId);
|
||||
}
|
||||
|
||||
async function saveReactionRole() {
|
||||
const entries = reactionDraft.entries.split('\n').map((l) => l.trim()).filter(Boolean)
|
||||
.map((line) => { const p = line.split('|').map((s) => s.trim()); return { emoji: p[0], roleId: p[1], label: p[2], description: p[3] }; })
|
||||
.filter((e) => e.emoji && e.roleId);
|
||||
await apiFetch('/reactionroles', { method: 'POST', body: JSON.stringify({ guildId: currentGuildId, channelId: reactionDraft.channelId, title: reactionDraft.title, entries }) });
|
||||
await loadGuildData(currentGuildId);
|
||||
setStatusMessage('Reaction Role gespeichert');
|
||||
}
|
||||
|
||||
async function toggleModule(key: string, enabled: boolean) {
|
||||
await saveSettingsPayload({ [key]: enabled }, `${key} aktualisiert`);
|
||||
}
|
||||
|
||||
async function saveAutomation() {
|
||||
await apiFetch('/automations', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
guildId: currentGuildId, name: automationDraft.name || 'Automation',
|
||||
condition: { category: automationDraft.conditionValue },
|
||||
action: { type: 'reminder', message: automationDraft.actionValue || 'Reminder' }, active: true
|
||||
})
|
||||
});
|
||||
setAutomationDraft({ name: '', conditionValue: '', actionValue: '' });
|
||||
await loadTicketData(currentGuildId);
|
||||
}
|
||||
|
||||
async function saveKbArticle() {
|
||||
await apiFetch('/kb', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ guildId: currentGuildId, title: kbDraft.title || 'Artikel', keywords: kbDraft.keywords, content: kbDraft.content })
|
||||
});
|
||||
setKbDraft({ title: '', keywords: '', content: '' });
|
||||
await loadTicketData(currentGuildId);
|
||||
}
|
||||
|
||||
async function updateKbArticle(id: string) {
|
||||
if (!kbEditDraft) return;
|
||||
await apiFetch(`/kb/${id}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ guildId: currentGuildId, title: kbEditDraft.title, keywords: kbEditDraft.keywords, content: kbEditDraft.content })
|
||||
});
|
||||
setKbEditDraft(null);
|
||||
setStatusMessage('KB-Artikel aktualisiert');
|
||||
await loadTicketData(currentGuildId);
|
||||
}
|
||||
|
||||
async function deleteKbArticle(id: string) {
|
||||
await apiFetch(`/kb/${id}`, { method: 'DELETE', body: JSON.stringify({ guildId: currentGuildId }) });
|
||||
setStatusMessage('KB-Artikel gelöscht');
|
||||
await loadTicketData(currentGuildId);
|
||||
}
|
||||
|
||||
async function updateAutomation(id: string) {
|
||||
if (!automationEditDraft) return;
|
||||
await apiFetch(`/automations/${id}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ guildId: currentGuildId, name: automationEditDraft.name, condition: { category: automationEditDraft.conditionValue }, action: { type: 'reminder', message: automationEditDraft.actionValue }, active: true })
|
||||
});
|
||||
setAutomationEditDraft(null);
|
||||
setStatusMessage('Automation aktualisiert');
|
||||
await loadTicketData(currentGuildId);
|
||||
}
|
||||
|
||||
async function deleteAutomation(id: string) {
|
||||
await apiFetch(`/automations/${id}`, { method: 'DELETE', body: JSON.stringify({ guildId: currentGuildId }) });
|
||||
setStatusMessage('Automation gelöscht');
|
||||
await loadTicketData(currentGuildId);
|
||||
}
|
||||
|
||||
async function saveSupportLogin() {
|
||||
if (!supportLogin) return;
|
||||
await apiFetch('/tickets/support-login', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ guildId: currentGuildId, ...supportLogin.config })
|
||||
});
|
||||
setStatusMessage('Support Login gespeichert');
|
||||
await loadGuildData(currentGuildId);
|
||||
}
|
||||
|
||||
async function saveForm() {
|
||||
const fields = formDraft.fields.split('\n').filter(Boolean).map((line) => {
|
||||
const parts = line.split('|').map((s) => s.trim());
|
||||
return { label: parts[0] || 'Feld', type: (parts[1] || 'text') as any, required: parts[2] === 'required', options: parts[3] ? parts[3].split(',').map((s) => s.trim()) : undefined };
|
||||
});
|
||||
const body: any = { guildId: currentGuildId, name: formDraft.name, description: formDraft.description, reviewChannelId: formDraft.reviewChannelId || undefined, notifyRoleIds: formDraft.notifyRoleIds.split(',').map((s) => s.trim()).filter(Boolean), fields, isActive: true };
|
||||
if (editingFormId) {
|
||||
await apiFetch(`/register/forms/${editingFormId}`, { method: 'PUT', body: JSON.stringify(body) });
|
||||
setStatusMessage('Formular aktualisiert');
|
||||
} else {
|
||||
await apiFetch('/register/forms', { method: 'POST', body: JSON.stringify(body) });
|
||||
setStatusMessage('Formular erstellt');
|
||||
}
|
||||
setFormDraft({ name: '', description: '', reviewChannelId: '', notifyRoleIds: '', fields: '' });
|
||||
setEditingFormId(null);
|
||||
await loadGuildData(currentGuildId);
|
||||
}
|
||||
|
||||
async function deleteForm(id: string) {
|
||||
await apiFetch(`/register/forms/${id}`, { method: 'DELETE', body: JSON.stringify({ guildId: currentGuildId }) });
|
||||
setStatusMessage('Formular gelöscht');
|
||||
await loadGuildData(currentGuildId);
|
||||
}
|
||||
|
||||
async function sendFormPanel(formId: string) {
|
||||
if (!supportLogin?.config?.panelChannelId) { setStatusMessage('Bitte zuerst Support Login konfigurieren'); return; }
|
||||
await apiFetch(`/register/forms/${formId}/panel`, { method: 'POST', body: JSON.stringify({ guildId: currentGuildId, channelId: supportLogin.config.panelChannelId }) });
|
||||
setStatusMessage('Panel gesendet');
|
||||
}
|
||||
|
||||
async function addStatusService() {
|
||||
if (!statusServiceDraft.name) return;
|
||||
await apiFetch('/statuspage/service', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ guildId: currentGuildId, name: statusServiceDraft.name, url: statusServiceDraft.url, status: statusServiceDraft.status })
|
||||
});
|
||||
setStatusServiceDraft({ name: '', url: '', status: 'unknown' });
|
||||
setStatusMessage('Service hinzugefügt');
|
||||
await loadGuildData(currentGuildId);
|
||||
}
|
||||
|
||||
async function deleteStatusService(id: string) {
|
||||
await apiFetch(`/statuspage/service/${id}`, { method: 'DELETE', body: JSON.stringify({ guildId: currentGuildId }) });
|
||||
setStatusMessage('Service entfernt');
|
||||
await loadGuildData(currentGuildId);
|
||||
}
|
||||
|
||||
async function addStatsItem() {
|
||||
if (!statsItemDraft.label) return;
|
||||
const draft = statsDraft || { enabled: true, categoryName: '', refreshMinutes: 10, items: [] };
|
||||
const items = [...(draft.items || []), { key: statsItemDraft.label.toLowerCase().replace(/\s+/g, '_'), label: statsItemDraft.label, type: statsItemDraft.type }];
|
||||
const updated = { ...draft, items };
|
||||
setStatsDraft(updated);
|
||||
await apiFetch('/server-stats', { method: 'POST', body: JSON.stringify({ guildId: currentGuildId, config: updated }) });
|
||||
setStatsItemDraft({ label: '', type: 'members' });
|
||||
setStatusMessage('Stat-Item hinzugefügt');
|
||||
await loadGuildData(currentGuildId);
|
||||
}
|
||||
|
||||
async function deleteStatsItem(index: number) {
|
||||
const draft = statsDraft || { enabled: true, categoryName: '', refreshMinutes: 10, items: [] };
|
||||
const items = (draft.items || []).filter((_: any, i: number) => i !== index);
|
||||
const updated = { ...draft, items };
|
||||
setStatsDraft(updated);
|
||||
await apiFetch('/server-stats', { method: 'POST', body: JSON.stringify({ guildId: currentGuildId, config: updated }) });
|
||||
setStatusMessage('Stat-Item entfernt');
|
||||
await loadGuildData(currentGuildId);
|
||||
}
|
||||
|
||||
async function loadTicketMessages(ticketId: string) {
|
||||
const res = await apiFetch<any>(`/tickets/${ticketId}/messages`);
|
||||
setTicketMessages(res.messages || []);
|
||||
}
|
||||
|
||||
async function updateTicketStatus(ticketId: string, status: string) {
|
||||
await apiFetch(`/tickets/${ticketId}/status`, { method: 'POST', body: JSON.stringify({ status }) });
|
||||
setStatusMessage('Status aktualisiert');
|
||||
await loadGuildData(currentGuildId);
|
||||
}
|
||||
|
||||
async function closeTicket(ticketId: string) {
|
||||
await apiFetch(`/tickets/${ticketId}/close`, { method: 'POST', body: JSON.stringify({ guildId: currentGuildId }) });
|
||||
setStatusMessage('Ticket geschlossen');
|
||||
await loadGuildData(currentGuildId);
|
||||
}
|
||||
|
||||
const handleLogout = useCallback(() => {
|
||||
window.location.href = `${appConfig.baseAuth || '/auth'}/logout`;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AppContext.Provider value={{
|
||||
user, guilds, currentGuildId, section, guildInfo, overview, activity,
|
||||
logs, tickets, pipeline, sla, automations, kbArticles, settings, modules,
|
||||
birthday, reactionRoles, statuspage, serverStats, events, admin, statusMessage,
|
||||
loading, supportLogin, registerForms, registerApps, musicStatus, ticketTab,
|
||||
automationDraft, kbDraft, eventDraft, statusDraft, statsDraft, reactionDraft,
|
||||
formDraft, editingFormId, registerTab, statusServiceDraft, statsItemDraft,
|
||||
ticketDetail, ticketMessages, kbEditDraft, automationEditDraft,
|
||||
setCurrentGuildId, setSection, setSettings, setBirthday, setSupportLogin,
|
||||
setStatusDraft, setStatsDraft, setStatusMessage, loadGuildData,
|
||||
saveSettingsPayload, saveBirthday, saveStatuspage, saveServerStats,
|
||||
toggleModule, handleLogout, loadTicketData, loadTicketMessages,
|
||||
updateTicketStatus, closeTicket, saveAutomation, saveKbArticle,
|
||||
updateKbArticle, deleteKbArticle, updateAutomation, deleteAutomation,
|
||||
saveSupportLogin, saveForm, deleteForm, sendFormPanel, addStatusService,
|
||||
deleteStatusService, addStatsItem, deleteStatsItem, saveEvent, deleteEvent,
|
||||
saveReactionRole, setTicketTab, setAutomationDraft, setKbDraft, setEventDraft,
|
||||
setReactionDraft, setFormDraft, setEditingFormId, setRegisterTab,
|
||||
setStatusServiceDraft, setStatsItemDraft, setTicketDetail, setKbEditDraft,
|
||||
setAutomationEditDraft,
|
||||
}}>
|
||||
{children}
|
||||
</AppContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useApp() {
|
||||
const ctx = useContext(AppContext);
|
||||
if (!ctx) throw new Error('useApp must be used within AppProvider');
|
||||
return ctx;
|
||||
}
|
||||
Reference in New Issue
Block a user