import { useState, useRef, useEffect } from "react"; /* ─── CONSTANTS ─── */ const ADMIN_U = "VLAD", ADMIN_P = "smart152527"; const COURSES = ["Програмування","Українська мова"]; const CI = {"Програмування":"💻","Українська мова":"📖"}; const SOCIALS = ["Zoom","Google Meet","Skype","Telegram","Viber","WhatsApp"]; const PKG = [{n:1,p:10},{n:2,p:18},{n:5,p:45},{n:8,p:70},{n:10,p:80},{n:15,p:130},{n:25,p:200},{n:40,p:360}]; const PHONE = "+380 95 891 0937"; const uid = () => Math.random().toString(36).slice(2,9); const now = () => Date.now(); const fdt = t => t ? new Date(t).toLocaleString("uk-UA",{day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit"}) : "—"; const fd = t => t ? new Date(t).toLocaleDateString("uk-UA",{day:"2-digit",month:"2-digit",year:"numeric"}) : "—"; const ft = t => t ? new Date(t).toLocaleTimeString("uk-UA",{hour:"2-digit",minute:"2-digit"}) : "—"; const nl = n => n===1?"урок":n<5?"уроки":"уроків"; const tod = () => new Date().toISOString().split("T")[0]; const fDT = (d,t) => new Date(`${d}T${t}`).getTime(); const toD = t => { const d=new Date(t); return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,"0")}-${String(d.getDate()).padStart(2,"0")}`; }; const toT = t => { const d=new Date(t); return `${String(d.getHours()).padStart(2,"0")}:${String(d.getMinutes()).padStart(2,"0")}`; }; const fileToB64 = file => new Promise(r=>{ const fr=new FileReader(); fr.onload=e=>r(e.target.result); fr.readAsDataURL(file); }); /* ─── COLORS ─── */ const C = { bg:"#0d1b2a", primary:"#1d72f5", acc:"#38bdf8", green:"#10b981", red:"#ef4444", yellow:"#f59e0b", purple:"#8b5cf6", gray:"#64748b", gL:"#e2e8f0", dark:"#0f1c2e", tD:"#1e3a5f", tM:"#475569" }; /* ─── STYLE HELPERS ─── */ const Btn = (v="primary",ex={}) => ({ padding:"10px 20px",borderRadius:12,border:"none",cursor:"pointer", fontWeight:700,fontSize:14,fontFamily:"inherit",transition:"all .15s", ...(v==="primary"&&{background:C.primary,color:"#fff",boxShadow:"0 3px 12px rgba(29,114,245,.3)"}), ...(v==="outline"&&{background:"transparent",color:C.primary,border:`2px solid ${C.primary}`}), ...(v==="ghost" &&{background:"rgba(255,255,255,.1)",color:"#fff",border:"1px solid rgba(255,255,255,.2)"}), ...(v==="danger" &&{background:C.red,color:"#fff"}), ...(v==="gray" &&{background:C.gL,color:C.tD}), ...(v==="green" &&{background:C.green,color:"#fff"}), ...(v==="purple" &&{background:C.purple,color:"#fff"}), ...ex }); const Inp = (ex={}) => ({ width:"100%",padding:"10px 13px",borderRadius:10,border:`2px solid ${C.gL}`, fontSize:14,outline:"none",boxSizing:"border-box",fontFamily:"inherit", color:C.tD,background:"#fff",...ex }); const Card = (ex={}) => ({ background:"#fff",borderRadius:18,padding:18, boxShadow:"0 2px 18px rgba(0,0,0,.07)",...ex }); const Label = ({children,mb=6}) => (
{children}
); /* ─── STAR RATING ─── */ const Stars = ({value,onChange,size=22}) => (
{[1,2,3,4,5].map(n=>( onChange&&onChange(n)} style={{fontSize:size,cursor:onChange?"pointer":"default",color:n<=value?C.yellow:"#d1d5db",transition:"color .1s"}}>★ ))}
); /* ════════════════════════════════════════ ROOT APP ════════════════════════════════════════ */ export default function App() { const [screen,setScreen] = useState("landing"); const [curId,setCurId] = useState(null); const [isAdmin,setIsAdmin] = useState(false); const [users,setUsers] = useState([]); const [lsns,setLsns] = useState({}); // {uid:[lesson,...]} const [chats,setChats] = useState({}); // {uid:[msg,...]} const addUser = u => setUsers(p=>[...p,u]); const updUser = (id,patch) => setUsers(p=>p.map(u=>u.id===id?{...u,...patch}:u)); const delUser = id => setUsers(p=>p.filter(u=>u.id!==id)); const getLsns = id => lsns[id]||[]; const setULsns = (id,ls) => setLsns(p=>({...p,[id]:ls})); const updLsn = (uid,lid,patch) => setLsns(p=>({...p,[uid]:(p[uid]||[]).map(l=>l.id===lid?{...l,...patch}:l)})); const getMsgs = id => chats[id]||[]; const addMsg = (id,m) => setChats(p=>({...p,[id]:[...(p[id]||[]),m]})); const doLogin = (u,admin) => { setCurId(u?u.id:null); setIsAdmin(admin); setScreen(admin?"admin":"student"); }; const doLogout = () => { setCurId(null); setIsAdmin(false); setScreen("landing"); }; const ctx = {users,addUser,updUser,delUser,getLsns,setULsns,updLsn,getMsgs,addMsg,doLogin,doLogout}; const curUser = users.find(u=>u.id===curId); return (
{screen==="landing" && setScreen("register")} onLogin={()=>setScreen("login")}/>} {screen==="register" && setScreen("landing")} onDone={()=>setScreen("login")}/>} {screen==="login" && setScreen("landing")}/>} {screen==="student" && curUser && } {screen==="admin" && }
); } /* ════════════════════════════════════════ LANDING ════════════════════════════════════════ */ function Landing({onStart,onLogin}){ return(
VladSmart
Онлайн репетитор · Програмування та Українська мова
Ласкаво просимо 🎓
Перший урок — безкоштовно!
Домашні завдання, чат, гнучкий розклад
{[["💡","1-й урок безкоштовно"],["📅","Гнучкий розклад"],["💬","Чат з фото"],["⭐","Оцінки та відгуки"]].map(([ic,t])=>(
{ic}
{t}
))}
); } /* ════════════════════════════════════════ REGISTER (3 steps + extra info) ════════════════════════════════════════ */ function Register({ctx,onBack,onDone}){ const [step,setStep] = useState(1); const [course,setCourse] = useState(""); const [date,setDate] = useState(""); const [time,setTime] = useState(""); const [name,setName] = useState(""); const [pass,setPass] = useState(""); const [pass2,setPass2] = useState(""); const [phone,setPhone] = useState(""); const [social,setSocial] = useState(""); const [city,setCity] = useState(""); const [comment,setComment] = useState(""); const [err,setErr] = useState(""); const Wrap=({title,ch})=>(
Реєстрація
Крок {step}/3 — {title}
{ch}
); if(step===1) return
Який курс вас цікавить?
{COURSES.map(c=>( ))} }/>; if(step===2) return setName(e.target.value)} style={Inp({marginBottom:12})}/> setPass(e.target.value)} style={Inp({marginBottom:12})}/> setPass2(e.target.value)} style={Inp({marginBottom:12})}/> setPhone(e.target.value)} style={Inp({marginBottom:12})}/> setCity(e.target.value)} style={Inp({marginBottom:12})}/>