// Wellness · Sporočila · Skupine · Parking · Predaja smene · Room rack // ─── ROOM RACK (kompaktni tloris + tabela) ─────────────────────────────────── function RCRoomRack({ dark }) { const muted = dark?'#9A9A9A':'#707070'; const floors = [ {n:4, rooms:['401','402','403','404','405','406','407','408','409','410','411','412']}, {n:3, rooms:['301','302','303','304','305','306','307','308','309','310','311','312']}, {n:2, rooms:['201','202','203','204','205','206','207','208','209','210','211','212','213']}, {n:1, rooms:['101','102','103','104','105','106','107','108','109','110']}, ]; const stat = (r) => { const last = parseInt(r.slice(-2)); if (r==='305' || r==='412' || r==='418') return {s:'occ', g:'James S.', c:'#2196F3'}; if (last%5===0) return {s:'dirty', c:'#FF9800'}; if (last%7===0) return {s:'oo', c:'#9A9A9A'}; if (last%3===0) return {s:'ci', c:'#9C27B0'}; if (last%2===0) return {s:'occ', g:'Gost', c:'#2196F3'}; return {s:'clean', c:'#2DD164'}; }; const labels = {occ:'Zaseden',clean:'Čist',dirty:'Umazan',ci:'Prihaja',co:'Odhaja',oo:'Out of order'}; return <> Najdi sobo Premik gosta }/>
{Object.entries(labels).map(([k,l])=>
{l}
)}
{floors.map(f=>
{f.n}. nad.
{f.rooms.map(r=>{ const st = stat(r); return ; })}
)}
; } // ─── WELLNESS ──────────────────────────────────────────────────────────────── function RCWellness({ dark }) { const muted = dark?'#9A9A9A':'#707070'; const hours = ['09','10','11','12','13','14','15','16','17','18','19','20']; const therapists = ['Petra','Tanja','Klara']; return <> Pregled tedna Nova rezervacija }/>
Časovnica · masaže
{therapists.map(t=>
{t}
{hours.map((h,i)=>
0?`1px dashed ${dark?'#333':'#E5E5E5'}`:'none', fontSize:9, color:muted, padding:'2px 4px', fontFamily:'var(--font-mono)'}}>{h}
)} {WELLNESS.filter(w=>w.therapist===t).map((w,i)=>{ const start = (parseInt(w.time.split(':')[0]) - 9) + (parseInt(w.time.split(':')[1])/60); const dur = w.service.includes('50min')?0.83:1; return
{w.guest} · {w.time}
; })}
)}
{r.time}}, {key:'service', label:'Storitev'}, {key:'guest', label:'Gost'}, {key:'room', label:'Soba', render:r=>{r.room}}, {key:'therapist', label:'Terapevtka'}, {key:'status', label:'Status', render:r=>r.status==='confirmed'?Potrjeno:Odprto}, ]} rows={WELLNESS}/>
Bazen / sauna
{[['Bazen', 24, 50, '#00BCD4'],['Sauna finska', 4, 8, '#E91E63'],['Sauna parna', 2, 6, '#9C27B0'],['Whirlpool', 3, 6, '#2196F3']].map(([n,c,m,col])=>
{n} {c}/{m}
)}
Hitre rezervacije
{['Wellness 2h','Sauna 1h','Masaža 50min','Lymph drenaža'].map(n=> )}
; } // ─── MESSAGES / GUEST REQUESTS ─────────────────────────────────────────────── function RCMessages({ dark }) { const muted = dark?'#9A9A9A':'#707070'; const [active, setActive] = React.useState('305'); const threads = [...new Set(MESSAGES.map(m=>m.room))].map(room=>{ const msgs = MESSAGES.filter(m=>m.room===room); const last = msgs[msgs.length-1]; const unread = msgs.filter(m=>m.from==='guest' && !m.read).length; return {room, guest:msgs[0].guest, last, unread}; }); const conv = MESSAGES.filter(m=>m.room===active); return <> t.unread>0).length} novih · WhatsApp + SMS + chat`} actions={<> Predloge Novo sporočilo }/>
{/* threads */}
{threads.map(t=>{ const a = active===t.room; return ; })}
{/* conv */}
{conv[0]?.guest} · soba {active}
WhatsApp · spletni gost · DE
Označi opravljeno Folio
{conv.map(m=>
{m.text}
{m.time}{m.by?` · ${m.by}`:''}
)}
Pošlji
{/* context */}
Soba {active} · konteksti
{[['Junior Suite','Tip sobe'],['28.04 → 04.05','6 noči'],['VIP · Triglav Club','Loyalty'],['DE · vegetarijanec','Preference'],['€ 1.451,40','Trenutni folio']].map(([v,l])=>
{l}
{v}
)}
Predloge odgovorov
{['Brisače na poti — 10 min','Šampanjec v sobo — 15 min','Pozni check-out odobren','Taksi rezerviran','Restavracija — rezervirano'].map(t=> )}
; } // ─── GROUPS ────────────────────────────────────────────────────────────────── function RCGroups({ dark }) { const muted = dark?'#9A9A9A':'#707070'; return <> Filter Nova skupina }/>
s+g.rooms,0)}/> s+g.total,0)/1000).toFixed(1)}k`}/> s+(g.total-g.paid),0).toLocaleString('sl-SI')}`}/>
{r.id}}, {key:'name', label:'Skupina', render:r=>
{r.name}
vodja: {r.leader} · {r.contact}
}, {key:'dates', label:'Datumi', render:r=>{r.checkin}–{r.checkout}}, {key:'rooms', label:'Sobe / Pax', align:'right', render:r=>
{r.rooms} sob
{r.pax} oseb
}, {key:'total', label:'Vrednost', align:'right', render:r=>€ {r.total.toLocaleString('sl-SI')}}, {key:'paid', label:'Plačano', align:'right', render:r=>{ const p = (r.paid/r.total)*100; return
{p.toFixed(0)}% €{r.paid.toLocaleString('sl-SI')}
0?'#FF9800':'#E53935', borderRadius:3}}/>
; }}, {key:'status', label:'Status', render:r=>{ const map = {confirmed:['success','Potrjeno'], option:['warning','Opcija'], inquiry:['neutral','Povpraševanje']}; return {map[r.status][1]}; }}, {key:'act', label:'', render:_=>Odpri}, ]} rows={GROUPS}/> ; } // ─── PARKING ───────────────────────────────────────────────────────────────── function RCParking({ dark }) { const muted = dark?'#9A9A9A':'#707070'; const free = PARKING_SPOTS.filter(s=>!s.occupied).length; return <> Najdi vozilo Dodeli mesto }/>
Tloris klet -1
{/* row */}
VHOD ↗
{PARKING_SPOTS.slice(0,6).map(s=> )}
POT ⇄
{PARKING_SPOTS.slice(6).map(s=>{ const colors = {ev:'#00BCD4', handicap:'#9C27B0', bus:'#FF9800'}; return ; })}
{[['#2196F3','Standard'],['#00BCD4','EV polnilnica'],['#9C27B0','Invalid'],['#FF9800','Avtobus']].map(([c,l])=>
{l}
)}
Aktivna vozila ({PARKING_SPOTS.length-free})
{r.n}}, {key:'plate', label:'Tablica', render:r=>{r.plate}}, {key:'guest', label:'Gost', render:r=>
{r.guest}
soba {r.room}
}, {key:'from', label:'Od', render:r=>{r.from}}, ]} rows={PARKING_SPOTS.filter(s=>s.occupied)}/>
; } // ─── SHIFT HANDOVER + CASH COUNT ───────────────────────────────────────────── function RCHandover({ dark }) { const muted = dark?'#9A9A9A':'#707070'; const cashTotal = CASH_DENOMS.reduce((s,d)=>s + d.v*d.count, 0); const expected = 1284.50; const diff = cashTotal - expected; return <> Tiskaj zapisnik Zaključi predajo }/>
{/* Cash count */}
Cash count · gotovinska blagajna
Preštej bankovce in kovance — sistem izračuna razliko
{CASH_DENOMS.map(d=>
€ {d.v < 1 ? d.v.toFixed(2) : d.v} {d.v >= 5 ? 'bank.' : 'kov.'}
×
€ {(d.v*d.count).toFixed(2)}
)}
Pričakovano
€ {expected.toFixed(2)}
Prešteto
€ {cashTotal.toFixed(2)}
Razlika
0?'#FF9800':'#E53935', marginTop:2}}>{diff>=0?'+':''}€ {diff.toFixed(2)}
Povzetek smene
{[ ['Računi izdani', '34', '#2196F3'], ['Storno', '2', '#E53935'], ['Promet skupaj', '€ 4.280','#2DD164'], ['Gotovina', '€ 1.284,50', '#FFB300'], ['Kartica', '€ 2.180,00', '#00BCD4'], ['Soba (room charge)','€ 815,50', '#9C27B0'], ['Check-ini', '14', '#2DD164'], ['Check-outi', '11', '#FF9800'], ].map(([l,v,c],i)=>
0?`1px solid ${dark?'#2B2B2B':'#F5F5F5'}`:'none'}}>
{l}
{v}
)}
Opomnik za naslednjo smeno