260 lines
7.6 KiB
TypeScript

'use client';
import { useState, useEffect } from 'react';
import { VoteOption } from '@/lib/survey';
import {
LoginForm,
TextEditor,
TextEditorManager,
StatsButton,
StatsDisplay,
VoteOptionsManager,
MemberAuthManager,
TokenGenerator,
ResetVotes
} from '@/components/admin';
// Define types based on the data structure
interface Stats {
total: number;
[key: string]: number; // Allow dynamic keys for vote options
}
interface Comment {
vote: VoteOption;
comment: string;
timestamp: string;
}
interface EditableText {
id: string;
content: string;
}
export default function AdminPage() {
const [showStats, setShowStats] = useState(false);
const [stats, setStats] = useState<Stats | null>(null);
const [comments, setComments] = useState<Comment[]>([]);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [showEditor, setShowEditor] = useState(false);
const [editableTexts, setEditableTexts] = useState<EditableText[]>([
{ id: 'welcome-text', content: '<p>Herzlich willkommen bei der Online-Abstimmungsplattform des SCHAFWASCHENER SEGELVEREIN CHIEMSEE E.V. RIMSTING.</p><p>Diese Plattform ermöglicht es Mitgliedern, sicher und bequem über Vereinsangelegenheiten abzustimmen.</p>' },
{ id: 'current-vote-text', content: '<p>Derzeit läuft eine Abstimmung zur Änderung der Vereinssatzung.</p><p>Bitte nutzen Sie den Ihnen zugesandten Link, um an der Abstimmung teilzunehmen.</p><p>Bei Fragen wenden Sie sich bitte an den Vorstand.</p>' },
{ id: 'vote-question', content: '<p>Stimmen Sie der vorgeschlagenen Änderung der Vereinssatzung zu?</p>' }
]);
const [voteOptions, setVoteOptions] = useState<{ id: string; label: string }[]>([
{ id: 'yes', label: 'Ja, ich stimme zu' },
{ id: 'no', label: 'Nein, ich stimme nicht zu' },
{ id: 'abstain', label: 'Ich enthalte mich' }
]);
const [selectedTextId, setSelectedTextId] = useState<string | null>(null);
const [editorContent, setEditorContent] = useState('');
const [memberAuthEnabled, setMemberAuthEnabled] = useState(false);
// Check if already authenticated and load settings on component mount
useEffect(() => {
const checkAuthAndSettings = async () => {
try {
// Check authentication
const response = await fetch('/api/generate-token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({}), // Empty body to check if cookie auth works
});
if (response.ok) {
setIsAuthenticated(true);
// Get current settings
const settingsResponse = await fetch('/api/settings');
if (settingsResponse.ok) {
const data = await settingsResponse.json();
setMemberAuthEnabled(data.settings.memberAuthEnabled);
}
}
} catch {
// Silently fail, user will need to enter password
}
};
checkAuthAndSettings();
}, []);
const handleEditText = (textId: string) => {
const textToEdit = editableTexts.find(text => text.id === textId);
if (textToEdit) {
setSelectedTextId(textId);
setEditorContent(textToEdit.content);
setShowEditor(true);
}
};
// Load editable texts and vote options on component mount
useEffect(() => {
const loadEditableTexts = async () => {
try {
const response = await fetch('/api/editable-text');
if (response.ok) {
const data = await response.json();
if (data.texts && Array.isArray(data.texts)) {
setEditableTexts(data.texts);
// Find and set vote options
const voteOptionsEntry = data.texts.find((text: { id: string }) => text.id === 'vote-options');
if (voteOptionsEntry && Array.isArray(voteOptionsEntry.content)) {
setVoteOptions(voteOptionsEntry.content);
}
}
}
} catch (err) {
console.error('Error loading editable texts:', err);
}
};
if (isAuthenticated) {
loadEditableTexts();
}
}, [isAuthenticated]);
const handleSaveText = async (content: string) => {
if (selectedTextId) {
try {
// Update local state
setEditableTexts(prev =>
prev.map(text =>
text.id === selectedTextId
? { ...text, content }
: text
)
);
// Save to server
const response = await fetch('/api/editable-text', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
id: selectedTextId,
content
}),
});
if (!response.ok) {
throw new Error('Failed to save text');
}
setShowEditor(false);
setSelectedTextId(null);
} catch (err) {
console.error('Error saving text:', err);
// Show error message to user if needed
}
}
};
const handleStatsLoaded = (loadedStats: Stats, loadedComments: Comment[]) => {
setStats(loadedStats);
setComments(loadedComments);
setShowStats(true);
};
const handleVoteOptionsChange = (newOptions: { id: string; label: string }[]) => {
setVoteOptions(newOptions);
};
const handleMemberAuthChange = (enabled: boolean) => {
setMemberAuthEnabled(enabled);
};
const handleResetVotes = () => {
// If stats are currently shown, refresh them
if (showStats) {
// Refresh stats
const fetchStats = async () => {
try {
const response = await fetch('/api/stats', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({}),
});
const data = await response.json();
if (response.ok) {
setStats(data.stats);
setComments(data.comments || []);
}
} catch (err) {
console.error('Error refreshing stats:', err);
}
};
fetchStats();
}
};
// Login form if not authenticated
if (!isAuthenticated) {
return <LoginForm onLoginSuccess={() => setIsAuthenticated(true)} />;
}
// WYSIWYG editor view
if (showEditor && selectedTextId) {
const textToEdit = editableTexts.find(text => text.id === selectedTextId);
if (textToEdit) {
return (
<TextEditor
textId={selectedTextId}
initialContent={editorContent}
onSave={handleSaveText}
onCancel={() => setShowEditor(false)}
/>
);
}
}
return (
<div className="container mx-auto px-4 py-8">
<div className="mb-8">
<h1 className="text-2xl font-bold text-[#0057a6] mb-4">ADMIN-BEREICH</h1>
{!showStats ? (
<div className="ssvc-main-content">
<StatsButton onStatsLoaded={handleStatsLoaded} />
<TextEditorManager
editableTexts={editableTexts}
onEditText={handleEditText}
/>
<VoteOptionsManager
voteOptions={voteOptions}
onVoteOptionsChange={handleVoteOptionsChange}
/>
<MemberAuthManager
memberAuthEnabled={memberAuthEnabled}
onMemberAuthChange={handleMemberAuthChange}
/>
<TokenGenerator />
<ResetVotes onReset={handleResetVotes} />
</div>
) : (
<StatsDisplay
stats={stats!}
comments={comments}
voteOptions={voteOptions}
onBack={() => setShowStats(false)}
/>
)}
</div>
</div>
);
}