// Main App: top bar, view router, drawer mounting. const APPIcons = window.Icons; const SESSION_KEY = 'akis_pm_session_v1'; const API_BASE = './api'; async function apiRequest(path, options = {}) { const res = await fetch(`${API_BASE}${path}`, { credentials: 'include', headers: { 'Content-Type': 'application/json', ...(options.headers || {}), }, ...options, }); const text = await res.text(); const data = text ? JSON.parse(text) : {}; if (!res.ok || data.ok === false) { const err = new Error(data.error || `API error ${res.status}`); err.status = res.status; throw err; } return data; } function App() { const store = window.PMStore.useStore(); const { state } = store; const [session, setSession] = React.useState(() => { try { const raw = localStorage.getItem(SESSION_KEY); return raw ? JSON.parse(raw) : null; } catch (e) { return null; } }); const [query, setQuery] = React.useState(''); const [selectedTaskId, setSelectedTaskId] = React.useState(null); const [syncStatus, setSyncStatus] = React.useState('local'); const [remoteReady, setRemoteReady] = React.useState(false); const [sidebarCollapsed, setSidebarCollapsed] = React.useState(() => { try { return localStorage.getItem('akis_sidebar_collapsed') === '1'; } catch (e) { return false; } }); const loadRemoteState = async () => { const data = await apiRequest('/state.php'); if (data.state) { store.replaceState(data.state); setRemoteReady(true); setSyncStatus('online'); } }; const login = async (user) => { const cleanEmail = user.email; const publicUser = { email: cleanEmail, name: user.name, remember: user.remember, signedInAt: user.signedInAt, }; let nextUser = publicUser; try { const data = await apiRequest('/auth.php?action=login', { method: 'POST', body: JSON.stringify({ email: cleanEmail, password: user.password }), }); nextUser = data.user || user; localStorage.setItem(SESSION_KEY, JSON.stringify(nextUser)); setSession(nextUser); await loadRemoteState(); } catch (e) { if (e.status === 401 || e.status === 422) { localStorage.removeItem(SESSION_KEY); throw e; } nextUser = { ...publicUser, offline: true }; localStorage.setItem(SESSION_KEY, JSON.stringify(nextUser)); setSession(nextUser); setRemoteReady(false); setSyncStatus('local'); } }; const logout = async () => { try { await apiRequest('/auth.php?action=logout', { method: 'POST', body: '{}' }); } catch (e) {} localStorage.removeItem(SESSION_KEY); setSelectedTaskId(null); setRemoteReady(false); setSyncStatus('local'); setSession(null); }; const clearLocalSession = () => { localStorage.removeItem(SESSION_KEY); setRemoteReady(false); setSyncStatus('local'); setSession(null); }; const refreshCloudSession = async () => { try { const me = await apiRequest('/auth.php?action=me'); if (me.user) { localStorage.setItem(SESSION_KEY, JSON.stringify(me.user)); setSession(me.user); } await loadRemoteState(); } catch (e) { setRemoteReady(false); setSyncStatus('local'); if (e.status === 401) clearLocalSession(); } }; const toggleSidebar = () => { setSidebarCollapsed(v => { const next = !v; try { localStorage.setItem('akis_sidebar_collapsed', next ? '1' : '0'); } catch (e) {} return next; }); }; const selectedTask = selectedTaskId ? state.tasks.find(t => t.id === selectedTaskId) : null; const openTask = (id) => setSelectedTaskId(id); const closeTask = () => setSelectedTaskId(null); const quickAddTask = (data = {}) => { store.addTask({ title: 'Yeni görev', status: data.status || 'todo', priority: 'medium', projectId: data.projectId || state.activeProjectId, ...data, }); }; // Open newest task after add const prevTaskCount = React.useRef(state.tasks.length); React.useEffect(() => { if (state.tasks.length > prevTaskCount.current) { const newest = [...state.tasks] .filter(t => t.createdAt) .sort((a,b) => new Date(b.createdAt) - new Date(a.createdAt))[0]; if (newest) setSelectedTaskId(newest.id); } prevTaskCount.current = state.tasks.length; }, [state.tasks.length]); React.useEffect(() => { if (!session) return; refreshCloudSession(); }, [session?.email]); React.useEffect(() => { if (!session || !remoteReady) return; const timer = setTimeout(() => { apiRequest('/state.php', { method: 'PUT', body: JSON.stringify({ state }), }) .then(() => setSyncStatus('online')) .catch(() => setSyncStatus('offline')); }, 700); return () => clearTimeout(timer); }, [state, session?.email, remoteReady]); if (!session) { return ; } const activeProject = state.projects.find(p => p.id === state.activeProjectId); const isProjectView = state.activeView === 'kanban' || state.activeView === 'list'; const viewTitle = (() => { if (state.activeView === 'dashboard') return 'Panel'; if (state.activeView === 'all-tasks') return 'Tüm Görevler'; if (state.activeView === 'calendar') return 'Takvim'; if (state.activeView === 'kanban') return activeProject?.name || 'Kanban'; if (state.activeView === 'list') return activeProject?.name || 'Liste'; return ''; })(); const viewSubtitle = (() => { if (state.activeView === 'dashboard') return 'Genel bakış'; if (state.activeView === 'all-tasks') return 'Tüm projeler · gruplama ve filtreleme'; if (state.activeView === 'calendar') return 'Aylık görünüm'; if (isProjectView && activeProject) { const count = state.tasks.filter(t => t.projectId === activeProject.id).length; return `${count} görev`; } return ''; })(); return (
quickAddTask({ projectId: state.activeProjectId })} onLogout={logout} />
{isProjectView && activeProject && (
{window.projectAbbr(activeProject.name)}
)}

{viewTitle}

{viewSubtitle &&
{viewSubtitle}
}
{isProjectView && (
)}
setQuery(e.target.value)} />
{syncStatus === 'online' ? 'Bulut' : syncStatus === 'offline' ? 'Çevrimdışı' : 'Yerel'}
{state.activeView === 'dashboard' && ( )} {state.activeView === 'all-tasks' && ( )} {state.activeView === 'kanban' && ( quickAddTask(data)} /> )} {state.activeView === 'list' && ( )} {state.activeView === 'calendar' && ( )}
{selectedTask && ( { store.deleteTask(id); closeTask(); }} onAddSubtask={store.addSubtask} onUpdateSubtask={store.updateSubtask} onRemoveSubtask={store.removeSubtask} /> )}
); } function TabletBottomNav({ state, setActiveView, setActiveProject }) { const activeProject = state.projects.find(p => p.id === state.activeProjectId); const goProject = () => { if (!state.activeProjectId && state.projects[0]) setActiveProject(state.projects[0].id); setActiveView('kanban'); }; return ( ); } function LoginScreen({ onLogin }) { const [email, setEmail] = React.useState('admin@aktasweb.io'); const [password, setPassword] = React.useState(''); const [remember, setRemember] = React.useState(true); const [error, setError] = React.useState(''); const [loading, setLoading] = React.useState(false); const submit = async (e) => { e.preventDefault(); setError(''); const cleanEmail = email.trim() || 'admin@aktasweb.io'; if (!password.trim()) { setError('Şifre gerekli.'); return; } const name = cleanEmail.split('@')[0] .split(/[._-]/) .filter(Boolean) .map(part => part.charAt(0).toLocaleUpperCase('tr-TR') + part.slice(1)) .join(' ') || 'Hasan Aktaş'; setLoading(true); try { await onLogin({ email: cleanEmail, password, name, remember, signedInAt: new Date().toISOString() }); } catch (e) { setError(e.message || 'Giriş yapılamadı.'); } finally { setLoading(false); } }; return (
A
Akış
Proje Yönetimi

Çalışma alanına giriş

Projeler, görevler ve takvim tek düzenli akışta.

{error &&
{error}
}
Demo alanı
); } ReactDOM.createRoot(document.getElementById('root')).render();