|
|
|
|
@@ -597,10 +597,27 @@ function EventDetailModal({ event, onClose, onEdit, onAvailabilityChange, isTool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Event Types Panel ─────────────────────────────────────────────────────────
|
|
|
|
|
function EventTypesPanel({ eventTypes, userGroups, onUpdated }) {
|
|
|
|
|
function EventTypesPanel({ eventTypes, userGroups, onUpdated, isMobile=false }) {
|
|
|
|
|
const toast=useToast();
|
|
|
|
|
const [editingType,setEditingType]=useState(null);
|
|
|
|
|
const [showForm,setShowForm]=useState(false);
|
|
|
|
|
// Mobile bottom sheet state
|
|
|
|
|
const [sheetMode,setSheetMode]=useState(null); // null | 'create' | 'edit'
|
|
|
|
|
const [sheetName,setSheetName]=useState('');
|
|
|
|
|
const [sheetColour,setSheetColour]=useState('#6366f1');
|
|
|
|
|
const [sheetSaving,setSheetSaving]=useState(false);
|
|
|
|
|
const openCreateSheet=()=>{setSheetName('');setSheetColour('#6366f1');setSheetMode('create');};
|
|
|
|
|
const openEditSheet=(et)=>{setSheetName(et.name);setSheetColour(et.colour);setEditingType(et);setSheetMode('edit');};
|
|
|
|
|
const closeSheet=()=>{setSheetMode(null);setEditingType(null);};
|
|
|
|
|
const saveSheet=async()=>{
|
|
|
|
|
if(!sheetName.trim()) return;
|
|
|
|
|
setSheetSaving(true);
|
|
|
|
|
try{
|
|
|
|
|
if(sheetMode==='create') await api.createEventType({name:sheetName.trim(),colour:sheetColour});
|
|
|
|
|
else await api.updateEventType(editingType.id,{name:sheetName.trim(),colour:sheetColour});
|
|
|
|
|
onUpdated(); closeSheet();
|
|
|
|
|
}catch(e){} finally{setSheetSaving(false);}
|
|
|
|
|
};
|
|
|
|
|
const handleDel=async et=>{
|
|
|
|
|
if(!confirm(`Delete "${et.name}"?`)) return;
|
|
|
|
|
try{await api.deleteEventType(et.id);toast('Deleted','success');onUpdated();}catch(e){toast(e.message,'error');}
|
|
|
|
|
@@ -609,7 +626,10 @@ function EventTypesPanel({ eventTypes, userGroups, onUpdated }) {
|
|
|
|
|
<div style={{maxWidth:560}}>
|
|
|
|
|
<div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:16}}>
|
|
|
|
|
<div className="settings-section-label" style={{margin:0}}>Event Types</div>
|
|
|
|
|
<div style={{position:'relative'}}><button className="btn btn-primary btn-sm" onClick={()=>{setShowForm(v=>!v);setEditingType(null);}}>+ New Type</button>{showForm&&!editingType&&<EventTypePopup userGroups={userGroups} onSave={()=>onUpdated()} onClose={()=>setShowForm(false)}/>}</div>
|
|
|
|
|
<div style={{position:'relative'}}>
|
|
|
|
|
<button className="btn btn-primary btn-sm" onClick={()=>isMobile?openCreateSheet():(setShowForm(v=>!v),setEditingType(null))}>+ New Type</button>
|
|
|
|
|
{!isMobile&&showForm&&!editingType&&<EventTypePopup userGroups={userGroups} onSave={()=>onUpdated()} onClose={()=>setShowForm(false)}/>}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div style={{display:'flex',flexDirection:'column',gap:6}}>
|
|
|
|
|
{eventTypes.map(et=>(
|
|
|
|
|
@@ -619,14 +639,36 @@ function EventTypesPanel({ eventTypes, userGroups, onUpdated }) {
|
|
|
|
|
{et.default_duration_hrs&&<span style={{fontSize:12,color:'var(--text-tertiary)'}}>{et.default_duration_hrs}hr default</span>}
|
|
|
|
|
{!et.is_protected?(
|
|
|
|
|
<div style={{display:'flex',gap:6,position:'relative'}}>
|
|
|
|
|
<button className="btn btn-secondary btn-sm" onClick={()=>{setEditingType(et);setShowForm(true);}}>Edit</button>
|
|
|
|
|
{showForm&&editingType?.id===et.id&&<EventTypePopup editing={et} userGroups={userGroups} onSave={()=>{onUpdated();setShowForm(false);setEditingType(null);}} onClose={()=>{setShowForm(false);setEditingType(null);}}/>}
|
|
|
|
|
<button className="btn btn-secondary btn-sm" onClick={()=>isMobile?openEditSheet(et):(setEditingType(et),setShowForm(true))}>Edit</button>
|
|
|
|
|
{!isMobile&&showForm&&editingType?.id===et.id&&<EventTypePopup editing={et} userGroups={userGroups} onSave={()=>{onUpdated();setShowForm(false);setEditingType(null);}} onClose={()=>{setShowForm(false);setEditingType(null);}}/>}
|
|
|
|
|
<button className="btn btn-sm" style={{background:'var(--error)',color:'white'}} onClick={()=>handleDel(et)}>Delete</button>
|
|
|
|
|
</div>
|
|
|
|
|
):<span style={{fontSize:11,color:'var(--text-tertiary)'}}>{et.is_default?'Default':'Protected'}</span>}
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Mobile bottom sheet for create/edit event type */}
|
|
|
|
|
{isMobile && sheetMode && (
|
|
|
|
|
<div style={{position:'fixed',inset:0,zIndex:200,display:'flex',alignItems:'flex-end'}} onClick={e=>e.target===e.currentTarget&&closeSheet()}>
|
|
|
|
|
<div style={{width:'100%',background:'var(--surface)',borderRadius:'16px 16px 0 0',padding:20,boxShadow:'0 -4px 20px rgba(0,0,0,0.2)'}}>
|
|
|
|
|
<div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:16}}>
|
|
|
|
|
<span style={{fontWeight:700,fontSize:16}}>{sheetMode==='create'?'New Event Type':'Edit Event Type'}</span>
|
|
|
|
|
<button onClick={closeSheet} style={{background:'none',border:'none',cursor:'pointer',color:'var(--text-secondary)',fontSize:20,lineHeight:1}}>✕</button>
|
|
|
|
|
</div>
|
|
|
|
|
<input autoFocus value={sheetName} onChange={e=>setSheetName(e.target.value)} onKeyDown={e=>e.key==='Enter'&&saveSheet()} placeholder="Type name…"
|
|
|
|
|
style={{width:'100%',padding:'12px 14px',border:'1px solid var(--border)',borderRadius:'var(--radius)',fontSize:16,marginBottom:12,boxSizing:'border-box',background:'var(--background)',color:'var(--text-primary)'}}/>
|
|
|
|
|
<div style={{display:'flex',alignItems:'center',gap:12,marginBottom:16}}>
|
|
|
|
|
<label style={{fontSize:14,color:'var(--text-tertiary)',flexShrink:0}}>Colour</label>
|
|
|
|
|
<input type="color" value={sheetColour} onChange={e=>setSheetColour(e.target.value)} style={{flex:1,height:40,border:'1px solid var(--border)',borderRadius:'var(--radius)',padding:3,cursor:'pointer'}}/>
|
|
|
|
|
</div>
|
|
|
|
|
<button onClick={saveSheet} disabled={sheetSaving||!sheetName.trim()}
|
|
|
|
|
style={{width:'100%',padding:'14px',background:'var(--primary)',color:'white',border:'none',borderRadius:'var(--radius)',fontSize:16,fontWeight:700,cursor:'pointer',opacity:sheetSaving?0.6:1}}>
|
|
|
|
|
{sheetSaving?'Saving…':'Save'}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
@@ -1130,7 +1172,7 @@ export default function SchedulePage({ isToolManager, isMobile, onProfile, onHel
|
|
|
|
|
<h2 style={{ fontSize:17, fontWeight:700, margin:0 }}>Event Types</h2>
|
|
|
|
|
<button className="btn btn-secondary btn-sm" onClick={()=>setPanel('calendar')}>← Back</button>
|
|
|
|
|
</div>
|
|
|
|
|
<EventTypesPanel eventTypes={eventTypes} userGroups={userGroups} onUpdated={load}/>
|
|
|
|
|
<EventTypesPanel eventTypes={eventTypes} userGroups={userGroups} onUpdated={load} isMobile={isMobile}/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
{panel === 'bulkImport' && isToolManager && (
|
|
|
|
|
|