Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 4 additions & 20 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

.main-content {
flex: 1;
padding: 20px;
padding: 24px 20px;
max-width: 1200px;
margin: 0 auto;
width: 100%;
Expand Down
33 changes: 30 additions & 3 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
import React, { useState } from 'react';
import './App.css';
import { AuthProvider, useAuth } from './contexts/AuthContext';
import Header from './components/Header';
import Navigation from './components/Navigation';
import Login from './pages/Login';
import Home from './pages/Home';
import Kids from './pages/Kids';
import Advanced from './pages/Advanced';
import SkillTest from './pages/SkillTest';
import FindTutor from './pages/FindTutor';
import Resources from './pages/Resources';
import Admin from './pages/Admin';

function App() {
function AppContent() {
const { user, loading } = useAuth();
const [currentPage, setCurrentPage] = useState('home');

if (loading) {
return (
<div className="loading-screen">
<div className="loading-spinner" />
<p>Učitavanje...</p>
</div>
);
}

if (!user) {
return <Login />;
}

const renderPage = () => {
switch(currentPage) {
switch (currentPage) {
case 'home':
return <Home setCurrentPage={setCurrentPage} />;
case 'kids':
Expand All @@ -26,14 +43,16 @@ function App() {
return <FindTutor />;
case 'resources':
return <Resources />;
case 'admin':
return user.role === 'admin' ? <Admin /> : <Home setCurrentPage={setCurrentPage} />;
default:
return <Home setCurrentPage={setCurrentPage} />;
}
};

return (
<div className="App">
<Header onFindTutor={() => setCurrentPage('find-tutor')} />
<Header onFindTutor={() => setCurrentPage('find-tutor')} currentPage={currentPage} setCurrentPage={setCurrentPage} />
<Navigation currentPage={currentPage} setCurrentPage={setCurrentPage} />
<main className="main-content">
{renderPage()}
Expand All @@ -42,4 +61,12 @@ function App() {
);
}

function App() {
return (
<AuthProvider>
<AppContent />
</AuthProvider>
);
}

export default App;
71 changes: 60 additions & 11 deletions src/components/Header.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,65 @@
import React from 'react';
import React, { useState } from 'react';
import { useAuth } from '../contexts/AuthContext';

function Header({ onFindTutor, currentPage, setCurrentPage }) {
const { user, logout } = useAuth();
const [menuOpen, setMenuOpen] = useState(false);

function Header({ onFindTutor }) {
return (
<header style={{ background: '#2c3e50', color: 'white', padding: '15px 20px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<h1 style={{ fontSize: '24px' }}>🇩🇪 Blebetalo</h1>
<p style={{ fontSize: '14px', opacity: 0.8 }}>Platforma za učenje nemačkog jezika na srpskom</p>
<button
onClick={onFindTutor}
style={{ background: '#e74c3c', color: 'white', border: 'none', padding: '8px 16px', borderRadius: '4px', cursor: 'pointer', fontSize: '14px' }}
>
Pronađi Tutora
</button>
<header className="app-header">
<div className="header-brand">
<span className="header-flag">🇩🇪</span>
<div className="header-brand-text">
<span className="header-title">Blebetalo</span>
<span className="header-subtitle">Učenje nemačkog na srpskom</span>
</div>
</div>

<div className="header-actions">
<button
className="btn btn-outline-light btn-sm header-tutor-btn"
onClick={onFindTutor}
>
👨‍🏫 Tutori
</button>

<div className="user-menu-wrapper">
<button
className="user-avatar-btn"
onClick={() => setMenuOpen(!menuOpen)}
aria-expanded={menuOpen}
>
<span className="user-avatar">
{user?.provider === 'google' ? '🌐' : user?.name?.[0]?.toUpperCase() || '?'}
</span>
<span className="user-name">{user?.name}</span>
<span className="chevron">{menuOpen ? '▲' : '▼'}</span>
</button>

{menuOpen && (
<div className="user-dropdown">
<div className="user-dropdown-header">
<strong>{user?.name}</strong>
<small>{user?.role === 'admin' ? '👑 Administrator' : '👤 Korisnik'}</small>
</div>
{user?.role === 'admin' && (
<button
className="user-dropdown-item"
onClick={() => { setCurrentPage('admin'); setMenuOpen(false); }}
>
⚙️ Admin Panel
</button>
)}
<button
className="user-dropdown-item user-dropdown-logout"
onClick={() => { logout(); setMenuOpen(false); }}
>
🚪 Odjavi se
</button>
</div>
)}
</div>
</div>
</header>
);
}
Expand Down
35 changes: 17 additions & 18 deletions src/components/Navigation.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
import React from 'react';
import { useAuth } from '../contexts/AuthContext';

const navItems = [
{ id: 'home', label: '🏠 Početna' },
{ id: 'kids', label: '👦 Za Decu' },
{ id: 'advanced', label: '🎓 Napredni' },
{ id: 'skill-test', label: '📝 Test Znanja' },
{ id: 'find-tutor', label: '👨‍🏫 Tutor' },
{ id: 'resources', label: '📖 Resursi' },
{ id: 'home', label: '🏠', text: 'Početna' },
{ id: 'kids', label: '👦', text: 'Za Decu' },
{ id: 'advanced', label: '🎓', text: 'Napredni' },
{ id: 'skill-test', label: '📝', text: 'Test Znanja' },
{ id: 'find-tutor', label: '👨‍🏫', text: 'Tutori' },
{ id: 'resources', label: '📖', text: 'Resursi' },
];

function Navigation({ currentPage, setCurrentPage }) {
const { user } = useAuth();
const items = user?.role === 'admin'
? [...navItems, { id: 'admin', label: '⚙️', text: 'Admin' }]
: navItems;

return (
<nav style={{ background: '#34495e', display: 'flex', flexWrap: 'wrap' }}>
{navItems.map(item => (
<nav className="app-nav">
{items.map((item) => (
<button
key={item.id}
onClick={() => setCurrentPage(item.id)}
style={{
background: currentPage === item.id ? '#2c3e50' : 'transparent',
color: 'white',
border: 'none',
padding: '12px 16px',
cursor: 'pointer',
fontSize: '14px',
borderBottom: currentPage === item.id ? '2px solid #e74c3c' : '2px solid transparent',
}}
className={`nav-btn ${currentPage === item.id ? 'active' : ''}`}
>
{item.label}
<span className="nav-icon">{item.label}</span>
<span className="nav-text">{item.text}</span>
</button>
))}
</nav>
Expand Down
99 changes: 99 additions & 0 deletions src/contexts/AuthContext.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import React, { createContext, useContext, useState, useEffect } from 'react';

const AuthContext = createContext(null);

const DEFAULT_USERS = [
{ username: 'admin', password: 'admin123', name: 'Administrator', role: 'admin' },
{ username: 'korisnik', password: 'lozinka123', name: 'Test Korisnik', role: 'user' },
];

export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
const stored = localStorage.getItem('blebetalo_user');
if (stored) {
try {
setUser(JSON.parse(stored));
} catch {
localStorage.removeItem('blebetalo_user');
}
}
setLoading(false);
}, []);

const getUsers = () => {
const stored = localStorage.getItem('blebetalo_users');
if (!stored) {
localStorage.setItem('blebetalo_users', JSON.stringify(DEFAULT_USERS));
return DEFAULT_USERS;
}
try {
return JSON.parse(stored);
} catch {
return DEFAULT_USERS;
}
};

const login = (username, password) => {
const users = getUsers();
const found = users.find(
(u) => u.username === username && u.password === password
);
if (found) {
const userData = {
username: found.username,
name: found.name,
role: found.role,
provider: 'local',
};
setUser(userData);
localStorage.setItem('blebetalo_user', JSON.stringify(userData));
return { success: true };
}
return { success: false, error: 'Pogrešno korisničko ime ili lozinka.' };
};

const loginWithGoogle = () => {
const userData = {
username: 'google_user',
name: 'Google Korisnik',
role: 'user',
provider: 'google',
avatar: 'https://lh3.googleusercontent.com/a/default-user',
};
setUser(userData);
localStorage.setItem('blebetalo_user', JSON.stringify(userData));
return { success: true };
};

const register = (username, password, name) => {
const users = getUsers();
if (users.find((u) => u.username === username)) {
return { success: false, error: 'Korisničko ime već postoji.' };
}
const newUser = { username, password, name, role: 'user' };
const updated = [...users, newUser];
localStorage.setItem('blebetalo_users', JSON.stringify(updated));
const userData = { username, name, role: 'user', provider: 'local' };
setUser(userData);
localStorage.setItem('blebetalo_user', JSON.stringify(userData));
return { success: true };
};

const logout = () => {
setUser(null);
localStorage.removeItem('blebetalo_user');
};

return (
<AuthContext.Provider value={{ user, loading, login, loginWithGoogle, register, logout }}>
{children}
</AuthContext.Provider>
);
}

export function useAuth() {
return useContext(AuthContext);
}
Loading