Panel admin, planning, tags, suivi du temps, thèmes clair/sombre
- Admin: création/suppression de comptes + rôles via Edge Function sécurisée - Config Supabase intégrée au build (.env), écran Setup supprimé - Page Planning (vue mois/semaine) - Tags "jobs" sur les tâches + filtres (membre / tag) - Suivi du temps par tâche, total par jalon - Toasts + confirmations in-app (fini alert/confirm natifs) - Thème Notion clair + mode sombre (accent monochrome, bascule persistée) - .env suivi par git (public uniquement) pour synchro multi-machines Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+10
-63
@@ -2,37 +2,19 @@ import { useState } from 'react'
|
||||
import { motion } from 'framer-motion'
|
||||
import { Gamepad2, Loader2 } from 'lucide-react'
|
||||
import { supa } from '../lib/supabase'
|
||||
import { Select } from '../components/ui'
|
||||
import { MemberRole, ROLE_LABELS } from '../lib/types'
|
||||
|
||||
export default function Login(): JSX.Element {
|
||||
const [mode, setMode] = useState<'signin' | 'signup'>('signin')
|
||||
const [email, setEmail] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
const [fullName, setFullName] = useState('')
|
||||
const [role, setRole] = useState<MemberRole>('programmer')
|
||||
const [busy, setBusy] = useState(false)
|
||||
const [error, setError] = useState('')
|
||||
const [info, setInfo] = useState('')
|
||||
|
||||
const submit = async (): Promise<void> => {
|
||||
setError('')
|
||||
setInfo('')
|
||||
setBusy(true)
|
||||
try {
|
||||
if (mode === 'signup') {
|
||||
const { error } = await supa().auth.signUp({
|
||||
email,
|
||||
password,
|
||||
options: { data: { full_name: fullName || email.split('@')[0], role } }
|
||||
})
|
||||
if (error) throw error
|
||||
setInfo('Compte créé ! Tu peux maintenant te connecter.')
|
||||
setMode('signin')
|
||||
} else {
|
||||
const { error } = await supa().auth.signInWithPassword({ email, password })
|
||||
if (error) throw error
|
||||
}
|
||||
const { error } = await supa().auth.signInWithPassword({ email, password })
|
||||
if (error) throw error
|
||||
} catch (e) {
|
||||
setError(translate((e as Error).message))
|
||||
} finally {
|
||||
@@ -52,36 +34,10 @@ export default function Login(): JSX.Element {
|
||||
<div className="rounded-xl bg-accent/20 p-3 text-accent">
|
||||
<Gamepad2 size={28} />
|
||||
</div>
|
||||
<h1 className="text-xl font-semibold text-white">GameDev Tracker</h1>
|
||||
<p className="text-sm text-gray-400">
|
||||
{mode === 'signin' ? 'Connecte-toi à ton équipe' : 'Crée ton compte membre'}
|
||||
</p>
|
||||
<h1 className="text-xl font-semibold text-ink">GameDev Tracker</h1>
|
||||
<p className="text-sm text-muted">Connecte-toi à ton équipe</p>
|
||||
</div>
|
||||
|
||||
{mode === 'signup' && (
|
||||
<>
|
||||
<label className="label">Nom complet</label>
|
||||
<input
|
||||
className="input mb-3"
|
||||
placeholder="Alex Martin"
|
||||
value={fullName}
|
||||
onChange={(e) => setFullName(e.target.value)}
|
||||
/>
|
||||
<label className="label">Rôle dans l'équipe</label>
|
||||
<Select
|
||||
className="mb-3"
|
||||
value={role}
|
||||
onChange={(e) => setRole(e.target.value as MemberRole)}
|
||||
>
|
||||
{Object.entries(ROLE_LABELS).map(([v, l]) => (
|
||||
<option key={v} value={v}>
|
||||
{l}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</>
|
||||
)}
|
||||
|
||||
<label className="label">Email</label>
|
||||
<input
|
||||
className="input mb-3"
|
||||
@@ -100,24 +56,16 @@ export default function Login(): JSX.Element {
|
||||
onKeyDown={(e) => e.key === 'Enter' && submit()}
|
||||
/>
|
||||
|
||||
{error && <p className="mb-3 text-sm text-rose-400">{error}</p>}
|
||||
{info && <p className="mb-3 text-sm text-accent2">{info}</p>}
|
||||
{error && <p className="mb-3 text-sm text-rose-600">{error}</p>}
|
||||
|
||||
<button className="btn-primary w-full" onClick={submit} disabled={busy}>
|
||||
{busy && <Loader2 className="animate-spin" size={16} />}
|
||||
{mode === 'signin' ? 'Se connecter' : "Créer le compte"}
|
||||
Se connecter
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="mt-4 w-full text-center text-sm text-gray-400 hover:text-white"
|
||||
onClick={() => {
|
||||
setMode(mode === 'signin' ? 'signup' : 'signin')
|
||||
setError('')
|
||||
setInfo('')
|
||||
}}
|
||||
>
|
||||
{mode === 'signin' ? "Pas encore de compte ? S'inscrire" : 'Déjà un compte ? Se connecter'}
|
||||
</button>
|
||||
<p className="mt-4 text-center text-xs text-subtle">
|
||||
Pas de compte ? Demande à ton administrateur de t'en créer un.
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
)
|
||||
@@ -125,7 +73,6 @@ export default function Login(): JSX.Element {
|
||||
|
||||
function translate(msg: string): string {
|
||||
if (msg.includes('Invalid login')) return 'Email ou mot de passe incorrect.'
|
||||
if (msg.includes('already registered')) return 'Cet email est déjà utilisé.'
|
||||
if (msg.includes('at least 6')) return 'Le mot de passe doit faire au moins 6 caractères.'
|
||||
if (msg.includes('Email not confirmed')) return "L'email n'a pas encore été confirmé."
|
||||
return msg
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user