diff --git a/.gitignore b/.gitignore index ea90594..a4af855 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .DS_Store build/ node_modules/ +server/dist/ diff --git a/client/src/components/Manage.tsx b/client/src/components/Manage.tsx index e378d09..072fdd5 100644 --- a/client/src/components/Manage.tsx +++ b/client/src/components/Manage.tsx @@ -1,16 +1,17 @@ -import React, { useState, useEffect, useCallback } from 'react'; +import React, { useState, useEffect, useCallback, useRef } from 'react'; // ── Types ──────────────────────────────────────────────────────────────────── -interface User { id: string; username: string; name: string; role: string; } +interface User { id: string; name: string; email: string; role: string; } interface BoardMember { _id: string; name: string; position: string; email: string; image: string; bio: string; order: number; } interface Event { _id: string; date: string; title: string; description: string; startTime: string; endTime: string; pointOfContact: string; email: string; address: string; } interface GalleryItem { _id: string; imageUrl: string; alt: string; order: number; } interface Sponsor { _id: string; name: string; logoPath: string; description: string; websiteUrl?: string; facebookUrl?: string; instagramUrl?: string; order: number; } -interface Minute { _id: string; date: string; meetingType: string; location: string; fileUrl: string; } +interface Minute { _id: string; date: string; meetingType: string; location: string; fileUrl: string; order: number; } interface Member { _id: string; firstName: string; lastName: string; email: string; phone?: string; address?: string; membershipType: string; status: string; joinDate: string; notes?: string; } type Tab = 'board' | 'events' | 'gallery' | 'sponsors' | 'minutes' | 'members'; +type AuthScreen = 'login' | 'forgot' | 'reset' | 'changePassword'; // ── Auth helpers ───────────────────────────────────────────────────────────── @@ -24,52 +25,14 @@ async function api(path: string, opts?: RequestInit): Promise { return res.json(); } -// ── Login screen ───────────────────────────────────────────────────────────── - -const Login: React.FC<{ onLogin: (token: string, user: User) => void }> = ({ onLogin }) => { - const [username, setUsername] = useState(''); - const [password, setPassword] = useState(''); - const [error, setError] = useState(''); - const [loading, setLoading] = useState(false); - - const submit = async (e: React.FormEvent) => { - e.preventDefault(); - setLoading(true); setError(''); - try { - const data = await fetch('/api/auth/login', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ username, password }), - }).then(r => { if (!r.ok) throw new Error('Invalid credentials'); return r.json(); }); - onLogin(data.token, data.user); - } catch (err: unknown) { - setError(err instanceof Error ? err.message : 'Login failed'); - } finally { - setLoading(false); - } - }; - - return ( -
-
-

MCDi Management

-
- setUsername(e.target.value)} required /> - setPassword(e.target.value)} required /> - {error &&

{error}

} - -
-
-
- ); -}; - // ── Shared UI ───────────────────────────────────────────────────────────────── -const Btn: React.FC<{ onClick?: () => void; type?: 'button'|'submit'; color?: string; disabled?: boolean; children: React.ReactNode }> = ({ onClick, type = 'button', color = 'bg-[#8C1D40] hover:bg-[#6B1631]', disabled, children }) => ( - +const Btn: React.FC<{ onClick?: () => void; type?: 'button'|'submit'; color?: string; disabled?: boolean; small?: boolean; children: React.ReactNode }> = + ({ onClick, type = 'button', color = 'bg-[#8C1D40] hover:bg-[#6B1631]', disabled, small, children }) => ( + ); const Field: React.FC<{ label: string; children: React.ReactNode }> = ({ label, children }) => ( @@ -87,6 +50,236 @@ const Textarea: React.FC> = (p