Update frontend UI with improved layout and styling

- Enhanced homepage with modern design and better visual hierarchy
- Improved login page with centered layout and better UX
- Updated admin page with cleaner interface
- Refined sidebar navigation and layout components
- Updated Tailwind config and global styles
- Fixed protected route component
This commit is contained in:
2026-01-17 15:18:08 +01:00
parent 3275bc4a4f
commit d20be4f868
9 changed files with 83 additions and 52 deletions

View File

@@ -4,6 +4,9 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<title>Znakovni.hr - Hrvatski znakovni jezik</title> <title>Znakovni.hr - Hrvatski znakovni jezik</title>
</head> </head>
<body> <body>

View File

@@ -18,8 +18,8 @@ export function ProtectedRoute({ children, requireAdmin = false }: ProtectedRout
return ( return (
<div className="flex h-screen items-center justify-center"> <div className="flex h-screen items-center justify-center">
<div className="text-center"> <div className="text-center">
<div className="h-8 w-8 animate-spin rounded-full border-4 border-blue-600 border-t-transparent mx-auto"></div> <div className="h-8 w-8 animate-spin rounded-full border-4 border-indigo-600 border-t-transparent mx-auto"></div>
<p className="mt-4 text-slate-600">Loading...</p> <p className="mt-4 text-muted-foreground">Loading...</p>
</div> </div>
</div> </div>
); );

View File

@@ -7,7 +7,7 @@ interface LayoutProps {
export function Layout({ children }: LayoutProps) { export function Layout({ children }: LayoutProps) {
return ( return (
<div className="flex h-screen bg-slate-50"> <div className="flex h-screen bg-indigo-50">
<Sidebar /> <Sidebar />
<main className="flex-1 overflow-y-auto"> <main className="flex-1 overflow-y-auto">
<div className="container mx-auto p-6 max-w-7xl"> <div className="container mx-auto p-6 max-w-7xl">

View File

@@ -44,10 +44,14 @@ export function Sidebar() {
}; };
return ( return (
<div className="flex h-screen w-60 flex-col bg-slate-800 text-white"> <div className="flex h-screen w-60 flex-col bg-white border-r border-border">
{/* Logo */} {/* Logo */}
<div className="flex h-16 items-center px-6 border-b border-slate-700"> <div className="flex h-16 items-center px-6 border-b border-border">
<h1 className="text-xl font-bold">Znakovni.hr</h1> <h1 className="text-xl font-bold">
<span className="text-indigo-600">ZNAKOVNI</span>
<span className="text-gray-400">.</span>
<span className="text-orange-600">hr</span>
</h1>
</div> </div>
{/* Navigation */} {/* Navigation */}
@@ -63,8 +67,8 @@ export function Sidebar() {
className={cn( className={cn(
'flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors', 'flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors',
isActive isActive
? 'bg-slate-700 text-white' ? 'bg-primary text-primary-foreground'
: 'text-slate-300 hover:bg-slate-700 hover:text-white' : 'text-muted-foreground hover:bg-secondary hover:text-foreground'
)} )}
> >
<item.icon className="h-5 w-5" /> <item.icon className="h-5 w-5" />
@@ -76,7 +80,7 @@ export function Sidebar() {
{/* Support Section */} {/* Support Section */}
<div className="pt-6"> <div className="pt-6">
<h3 className="px-3 text-xs font-semibold uppercase tracking-wider text-slate-400 mb-2"> <h3 className="px-3 text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-2">
Portal za podršku Portal za podršku
</h3> </h3>
<div className="space-y-1"> <div className="space-y-1">
@@ -89,8 +93,8 @@ export function Sidebar() {
className={cn( className={cn(
'flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors', 'flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors',
isActive isActive
? 'bg-slate-700 text-white' ? 'bg-primary text-primary-foreground'
: 'text-slate-300 hover:bg-slate-700 hover:text-white' : 'text-muted-foreground hover:bg-secondary hover:text-foreground'
)} )}
> >
<item.icon className="h-5 w-5" /> <item.icon className="h-5 w-5" />
@@ -109,8 +113,8 @@ export function Sidebar() {
className={cn( className={cn(
'flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors', 'flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors',
location.pathname === '/admin' location.pathname === '/admin'
? 'bg-slate-700 text-white' ? 'bg-primary text-primary-foreground'
: 'text-slate-300 hover:bg-slate-700 hover:text-white' : 'text-muted-foreground hover:bg-secondary hover:text-foreground'
)} )}
> >
<Shield className="h-5 w-5" /> <Shield className="h-5 w-5" />
@@ -121,16 +125,16 @@ export function Sidebar() {
</nav> </nav>
{/* User Section */} {/* User Section */}
<div className="border-t border-slate-700 p-4"> <div className="border-t border-border p-4">
{user ? ( {user ? (
<div className="space-y-2"> <div className="space-y-2">
<div className="px-3 py-2"> <div className="px-3 py-2">
<p className="text-sm font-medium text-white">{user.displayName || user.email}</p> <p className="text-sm font-medium text-foreground">{user.displayName || user.email}</p>
<p className="text-xs text-slate-400">{user.email}</p> <p className="text-xs text-muted-foreground">{user.email}</p>
</div> </div>
<button <button
onClick={handleLogout} onClick={handleLogout}
className="flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium text-slate-300 hover:bg-slate-700 hover:text-white transition-colors" className="flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium text-muted-foreground hover:bg-secondary hover:text-foreground transition-colors"
> >
<LogOut className="h-5 w-5" /> <LogOut className="h-5 w-5" />
Sign out Sign out
@@ -139,7 +143,7 @@ export function Sidebar() {
) : ( ) : (
<Link <Link
to="/login" to="/login"
className="flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium text-slate-300 hover:bg-slate-700 hover:text-white transition-colors" className="flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium text-muted-foreground hover:bg-secondary hover:text-foreground transition-colors"
> >
Sign in Sign in
</Link> </Link>

View File

@@ -4,26 +4,38 @@
@layer base { @layer base {
:root { :root {
--background: 0 0% 100%; /* Indigo-50 (#eef2ff) - Primary background */
--foreground: 222.2 84% 4.9%; --background: 238 100% 97%;
/* Slate-900 (#101828) - Primary text */
--foreground: 222 47% 11%;
--card: 0 0% 100%; --card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%; --card-foreground: 222 47% 11%;
--popover: 0 0% 100%; --popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%; --popover-foreground: 222 47% 11%;
--primary: 222.2 47.4% 11.2%; /* Indigo-600 (#4f39f6) - Primary accent color */
--primary-foreground: 210 40% 98%; --primary: 248 91% 60%;
--secondary: 210 40% 96.1%; --primary-foreground: 0 0% 100%;
--secondary-foreground: 222.2 47.4% 11.2%; /* Indigo-50 (#eef2ff) - Secondary background */
--muted: 210 40% 96.1%; --secondary: 238 100% 97%;
--muted-foreground: 215.4 16.3% 46.9%; --secondary-foreground: 248 91% 60%;
--accent: 210 40% 96.1%; /* Slate-100 (#f4f4f6) - Muted background */
--accent-foreground: 222.2 47.4% 11.2%; --muted: 240 5% 96%;
/* Slate-600 (#4a5565) - Secondary text */
--muted-foreground: 218 15% 35%;
--accent: 238 100% 97%;
--accent-foreground: 248 91% 60%;
--destructive: 0 84.2% 60.2%; --destructive: 0 84.2% 60.2%;
--destructive-foreground: 210 40% 98%; --destructive-foreground: 0 0% 100%;
--border: 214.3 31.8% 91.4%; /* Slate-300 (#d0d5e2) - Borders */
--input: 214.3 31.8% 91.4%; --border: 225 20% 85%;
--ring: 222.2 84% 4.9%; --input: 225 20% 85%;
--ring: 248 91% 60%;
--radius: 0.5rem; --radius: 0.5rem;
/* CEFR Level Colors */
--cefr-a1-a2: 145 100% 33%; /* Green-600 (#00a63e) */
--cefr-b1-b2: 32 100% 51%; /* Orange-500 (#ff8904) */
--cefr-c1-c2: 14 100% 57%; /* Red-Orange (#ff5c33) */
} }
.dark { .dark {

View File

@@ -111,8 +111,8 @@ export function Admin() {
<Layout> <Layout>
<div className="flex items-center justify-center h-64"> <div className="flex items-center justify-center h-64">
<div className="text-center"> <div className="text-center">
<div className="h-8 w-8 animate-spin rounded-full border-4 border-blue-600 border-t-transparent mx-auto"></div> <div className="h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent mx-auto"></div>
<p className="mt-4 text-slate-600">Loading users...</p> <p className="mt-4 text-muted-foreground">Loading users...</p>
</div> </div>
</div> </div>
</Layout> </Layout>
@@ -123,7 +123,7 @@ export function Admin() {
<Layout> <Layout>
<div className="space-y-6"> <div className="space-y-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h1 className="text-3xl font-bold text-slate-900">User Management</h1> <h1 className="text-3xl font-bold text-foreground">User Management</h1>
<Button onClick={() => setShowCreateForm(true)} disabled={showCreateForm || !!editingUser}> <Button onClick={() => setShowCreateForm(true)} disabled={showCreateForm || !!editingUser}>
<Plus className="h-4 w-4 mr-2" /> <Plus className="h-4 w-4 mr-2" />
Create User Create User
@@ -253,7 +253,7 @@ export function Admin() {
<td className="px-6 py-4 whitespace-nowrap"> <td className="px-6 py-4 whitespace-nowrap">
<span className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${ <span className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
user.role === 'ADMIN' user.role === 'ADMIN'
? 'bg-purple-100 text-purple-800' ? 'bg-indigo-100 text-indigo-800'
: 'bg-gray-100 text-gray-800' : 'bg-gray-100 text-gray-800'
}`}> }`}>
{user.role} {user.role}

View File

@@ -9,28 +9,28 @@ function Home() {
description: 'Browse and search Croatian sign language dictionary', description: 'Browse and search Croatian sign language dictionary',
icon: BookOpen, icon: BookOpen,
href: '/dictionary', href: '/dictionary',
color: 'bg-blue-500', color: 'bg-indigo-600',
}, },
{ {
name: 'Znakopis', name: 'Znakopis',
description: 'Build sentences using sign language', description: 'Build sentences using sign language',
icon: FileText, icon: FileText,
href: '/znakopis', href: '/znakopis',
color: 'bg-green-500', color: 'bg-gray-400',
}, },
{ {
name: 'Video rečenica', name: 'Video rečenica',
description: 'Watch and learn from video sentences', description: 'Watch and learn from video sentences',
icon: Video, icon: Video,
href: '/video-sentence', href: '/video-sentence',
color: 'bg-purple-500', color: 'bg-orange-600',
}, },
{ {
name: 'Oblak', name: 'Oblak',
description: 'Save and manage your documents in the cloud', description: 'Save and manage your documents in the cloud',
icon: Cloud, icon: Cloud,
href: '/cloud', href: '/cloud',
color: 'bg-orange-500', color: 'bg-indigo-600',
}, },
]; ];
@@ -38,10 +38,10 @@ function Home() {
<Layout> <Layout>
<div className="space-y-8"> <div className="space-y-8">
<div className="text-center"> <div className="text-center">
<h1 className="text-4xl font-bold text-slate-900 mb-4"> <h1 className="text-4xl font-bold text-foreground mb-4">
Dobrodošli na Znakovni.hr Tvoj put u svijet znakovnog jezika
</h1> </h1>
<p className="text-lg text-slate-600"> <p className="text-lg text-muted-foreground">
Hrvatski znakovni jezik - platforma za učenje i komunikaciju Hrvatski znakovni jezik - platforma za učenje i komunikaciju
</p> </p>
</div> </div>
@@ -58,10 +58,10 @@ function Home() {
<feature.icon className="h-8 w-8 text-white" /> <feature.icon className="h-8 w-8 text-white" />
</div> </div>
<div> <div>
<h3 className="text-xl font-semibold text-slate-900"> <h3 className="text-xl font-semibold text-foreground">
{feature.name} {feature.name}
</h3> </h3>
<p className="text-slate-600 mt-1">{feature.description}</p> <p className="text-muted-foreground mt-1">{feature.description}</p>
</div> </div>
</div> </div>
</Link> </Link>

View File

@@ -25,12 +25,16 @@ export function Login() {
}; };
return ( return (
<div className="flex min-h-screen items-center justify-center bg-slate-50"> <div className="flex min-h-screen items-center justify-center bg-background">
<div className="w-full max-w-md space-y-8 rounded-lg bg-white p-8 shadow-lg"> <div className="w-full max-w-md space-y-8 rounded-lg bg-card p-8 shadow-lg">
{/* Logo */} {/* Logo */}
<div className="text-center"> <div className="text-center">
<h1 className="text-3xl font-bold text-slate-900">Znakovni.hr</h1> <h1 className="text-3xl font-bold">
<p className="mt-2 text-sm text-slate-600"> <span className="text-indigo-600">ZNAKOVNI</span>
<span className="text-gray-400">.</span>
<span className="text-orange-600">hr</span>
</h1>
<p className="mt-2 text-sm text-muted-foreground">
Hrvatski znakovni jezik Hrvatski znakovni jezik
</p> </p>
</div> </div>
@@ -83,7 +87,7 @@ export function Login() {
</form> </form>
{/* Demo Credentials */} {/* Demo Credentials */}
<div className="mt-4 rounded-md bg-blue-50 p-4 text-sm text-blue-800"> <div className="mt-4 rounded-md bg-indigo-50 p-4 text-sm text-indigo-800">
<p className="font-semibold">Demo Credentials:</p> <p className="font-semibold">Demo Credentials:</p>
<p className="mt-1">Admin: admin@znakovni.hr / admin123</p> <p className="mt-1">Admin: admin@znakovni.hr / admin123</p>
<p>User: demo@znakovni.hr / demo123</p> <p>User: demo@znakovni.hr / demo123</p>

View File

@@ -4,6 +4,9 @@ export default {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
theme: { theme: {
extend: { extend: {
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
colors: { colors: {
border: 'hsl(var(--border))', border: 'hsl(var(--border))',
input: 'hsl(var(--input))', input: 'hsl(var(--input))',
@@ -38,6 +41,11 @@ export default {
DEFAULT: 'hsl(var(--card))', DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))', foreground: 'hsl(var(--card-foreground))',
}, },
cefr: {
'a1-a2': 'hsl(var(--cefr-a1-a2))',
'b1-b2': 'hsl(var(--cefr-b1-b2))',
'c1-c2': 'hsl(var(--cefr-c1-c2))',
},
}, },
borderRadius: { borderRadius: {
lg: 'var(--radius)', lg: 'var(--radius)',