feat: integrate API calls for forum, bug, and event pages; replace mock data with dynamic data fetching
This commit is contained in:
@@ -1,17 +1,29 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { useAuth } from '../../contexts/AuthContext';
|
||||
import { MOCK_THREADS, MOCK_BUGS } from '../../data/mockData';
|
||||
import { bugsApi, forumApi, usersApi } from '../../utils/api';
|
||||
import { formatDate } from '../../utils/format';
|
||||
import { Link } from 'react-router-dom';
|
||||
import type { BugReport, ForumThread } from '../../types';
|
||||
|
||||
type Tab = 'profile' | 'threads' | 'bugs' | 'password';
|
||||
|
||||
export default function AccountPage() {
|
||||
const { user, updateUsername } = useAuth();
|
||||
const [activeTab, setActiveTab] = useState<Tab>('profile');
|
||||
const [userThreads, setUserThreads] = useState<ForumThread[]>([]);
|
||||
const [userBugs, setUserBugs] = useState<BugReport[]>([]);
|
||||
|
||||
const userThreads = MOCK_THREADS.filter((t) => t.authorId === user?.id);
|
||||
const userBugs = MOCK_BUGS.filter((b) => b.submittedById === user?.id);
|
||||
useEffect(() => {
|
||||
if (!user) return;
|
||||
|
||||
forumApi.getThreads({ limit: 200 })
|
||||
.then((res) => setUserThreads(res.data.filter((t) => t.authorId === user.id)))
|
||||
.catch(() => setUserThreads([]));
|
||||
|
||||
bugsApi.getBugs({ limit: 200 })
|
||||
.then((res) => setUserBugs(res.data.filter((b) => b.submittedById === user.id)))
|
||||
.catch(() => setUserBugs([]));
|
||||
}, [user]);
|
||||
|
||||
const tabs: { id: Tab; label: string }[] = [
|
||||
{ id: 'profile', label: 'Profile' },
|
||||
@@ -121,19 +133,23 @@ export default function AccountPage() {
|
||||
|
||||
// ── Profile Tab ────────────────────────────────────────────────────────────────
|
||||
|
||||
function ProfileTab({ user, updateUsername }: { user: NonNullable<ReturnType<typeof useAuth>['user']>; updateUsername: (u: string) => void }) {
|
||||
function ProfileTab({ user, updateUsername }: { user: NonNullable<ReturnType<typeof useAuth>['user']>; updateUsername: (u: string) => Promise<{ success: boolean; error?: string }> }) {
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [username, setUsername] = useState(user.username);
|
||||
const [error, setError] = useState('');
|
||||
const [saved, setSaved] = useState(false);
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
const handleSave = useCallback(async () => {
|
||||
if (!username.trim()) { setError('Username cannot be empty.'); return; }
|
||||
if (username.length < 3) { setError('Must be at least 3 characters.'); return; }
|
||||
updateUsername(username.trim());
|
||||
setEditing(false);
|
||||
setSaved(true);
|
||||
setTimeout(() => setSaved(false), 3000);
|
||||
const result = await updateUsername(username.trim());
|
||||
if (result.success) {
|
||||
setEditing(false);
|
||||
setSaved(true);
|
||||
setTimeout(() => setSaved(false), 3000);
|
||||
} else {
|
||||
setError(result.error ?? 'Failed to update username.');
|
||||
}
|
||||
}, [username, updateUsername]);
|
||||
|
||||
return (
|
||||
@@ -210,10 +226,15 @@ function ChangePasswordForm() {
|
||||
if (Object.keys(next).length > 0) return;
|
||||
|
||||
setLoading(true);
|
||||
await new Promise((r) => setTimeout(r, 400));
|
||||
setLoading(false);
|
||||
setForm({ current: '', next: '', confirm: '' });
|
||||
setErrors({ success: 'Password changed successfully.' });
|
||||
try {
|
||||
await usersApi.changePassword(form.current, form.next);
|
||||
setForm({ current: '', next: '', confirm: '' });
|
||||
setErrors({ success: 'Password changed successfully.' });
|
||||
} catch (err) {
|
||||
setErrors({ current: err instanceof Error ? err.message : 'Failed to change password.' });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [form]);
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user