fix: fix mobile features, like missing setting pages and items sizes

This commit is contained in:
2026-04-21 08:45:10 +02:00
parent 6274b4a0b8
commit b2a7c1a2e3
5 changed files with 52 additions and 6 deletions

View File

@ -20,7 +20,7 @@ func GenerateToken(userID, email, role, secret string) (string, error) {
Email: email, Email: email,
Role: role, Role: role,
RegisteredClaims: jwt.RegisteredClaims{ RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), ExpiresAt: jwt.NewNumericDate(time.Now().Add(7 * 24 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()), IssuedAt: jwt.NewNumericDate(time.Now()),
}, },
} }

View File

@ -14,6 +14,12 @@ async function request<T>(path: string, options: RequestInit = {}): Promise<T> {
...options.headers, ...options.headers,
}, },
}) })
if (res.status === 401) {
localStorage.removeItem('token')
localStorage.removeItem('user')
window.location.href = '/login'
throw new Error('Session expirée')
}
if (!res.ok) { if (!res.ok) {
const err = await res.json().catch(() => ({ error: res.statusText })) const err = await res.json().catch(() => ({ error: res.statusText }))
throw new Error(err.error || res.statusText) throw new Error(err.error || res.statusText)

View File

@ -37,3 +37,8 @@
::-webkit-scrollbar { width: 6px; height: 6px; } ::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { @apply bg-muted; } ::-webkit-scrollbar-track { @apply bg-muted; }
::-webkit-scrollbar-thumb { @apply bg-border rounded-full; } ::-webkit-scrollbar-thumb { @apply bg-border rounded-full; }
@layer utilities {
.scrollbar-none { scrollbar-width: none; }
.scrollbar-none::-webkit-scrollbar { display: none; }
}

View File

@ -1,12 +1,47 @@
import { Outlet, Navigate } from 'react-router-dom' import { NavLink, Outlet, Navigate } from 'react-router-dom'
import { Cpu, Key, Database, ClipboardList, Users, CalendarDays, Settings } from 'lucide-react'
import { useAuth } from '@/lib/auth' import { useAuth } from '@/lib/auth'
import { cn } from '@/lib/cn'
const adminItems = [
{ to: '/admin/ai', icon: Cpu, label: 'Fournisseurs IA' },
{ to: '/admin/credentials', icon: Key, label: 'Identifiants' },
{ to: '/admin/sources', icon: Database, label: 'Sources' },
{ to: '/admin/jobs', icon: ClipboardList,label: 'Jobs' },
{ to: '/admin/users', icon: Users, label: 'Utilisateurs' },
{ to: '/admin/schedule', icon: CalendarDays, label: 'Planning' },
{ to: '/admin/settings', icon: Settings, label: 'Paramètres' },
]
export function AdminLayout() { export function AdminLayout() {
const { isAdmin } = useAuth() const { isAdmin } = useAuth()
if (!isAdmin) return <Navigate to="/" replace /> if (!isAdmin) return <Navigate to="/" replace />
return ( return (
<div>
{/* Mobile sub-nav — scrollable horizontal tabs */}
<nav className="md:hidden flex overflow-x-auto border-b bg-card scrollbar-none sticky top-0 z-10">
{adminItems.map(({ to, icon: Icon, label }) => (
<NavLink
key={to}
to={to}
className={({ isActive }) =>
cn(
'flex shrink-0 flex-col items-center gap-1 px-4 py-3 text-xs font-medium transition-colors border-b-2',
isActive
? 'border-primary text-primary'
: 'border-transparent text-muted-foreground'
)
}
>
<Icon className="h-4 w-4" />
{label}
</NavLink>
))}
</nav>
<div className="p-6"> <div className="p-6">
<Outlet /> <Outlet />
</div> </div>
</div>
) )
} }

View File

@ -34,12 +34,12 @@ export function Sources() {
<div className="space-y-3"> <div className="space-y-3">
{sources.map(s => ( {sources.map(s => (
<Card key={s.id}> <Card key={s.id}>
<CardContent className="flex items-center justify-between py-4"> <CardContent className="flex flex-col gap-3 py-4 sm:flex-row sm:items-center sm:justify-between">
<div> <div>
<span className="font-semibold">{s.name}</span> <span className="font-semibold">{s.name}</span>
<span className="ml-2 text-xs text-muted-foreground capitalize">({s.type})</span> <span className="ml-2 text-xs text-muted-foreground capitalize">({s.type})</span>
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-2">
<Badge variant={s.enabled ? 'default' : 'outline'}> <Badge variant={s.enabled ? 'default' : 'outline'}>
{s.enabled ? 'Activée' : 'Désactivée'} {s.enabled ? 'Activée' : 'Désactivée'}
</Badge> </Badge>