<!DOCTYPE html>


<html lang="zh-Hant">

<head>

<meta charset="UTF-8"/>

<meta name="viewport" content="width=device-width,initial-scale=1.0"/>

<meta name="apple-mobile-web-app-capable" content="yes"/>

<title>拍攝順序表</title>

<link href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=Noto+Serif+TC:wght@400;500;700&display=swap" rel="stylesheet">

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.23.2/babel.min.js"></script>

<script type="module">

  import{initializeApp}from"https://www.gstatic.com/firebasejs/10.12.0/firebase-app.js";

  import{getFirestore,collection,onSnapshot,doc,setDoc,deleteDoc,getDoc}from"https://www.gstatic.com/firebasejs/10.12.0/firebase-firestore.js";

  import{getStorage,ref,uploadBytes,getDownloadURL}from"https://www.gstatic.com/firebasejs/10.12.0/firebase-storage.js";

  const cfg={apiKey:"AIzaSyD6w6kUV_19R9JAwXAn3AiD3o60lkrs9aI",authDomain:"shooting-schedule-44f99.firebaseapp.com",projectId:"shooting-schedule-44f99",storageBucket:"shooting-schedule-44f99.firebasestorage.app",messagingSenderId:"186340238913",appId:"1:186340238913:web:89d8858db47a81e24888c0"};

  const app=initializeApp(cfg);

  window.__fb={db:getFirestore(app),storage:getStorage(app),collection,onSnapshot,doc,setDoc,deleteDoc,getDoc,ref,uploadBytes,getDownloadURL};

</script>

<style>

*{box-sizing:border-box;margin:0;padding:0}

html{font-size:18px}

body{background:#f2efe9;color:#1a1612;font-family:'Noto Serif TC',serif;min-height:100vh}

input,select,textarea,button{font-family:'Noto Serif TC',serif;font-size:16px}

input::placeholder,textarea::placeholder{color:#bbb}


/* ── TABS ── */

.tabs{display:flex;background:#1a1612;padding:0 32px}

.tab{padding:16px 28px;font-size:17px;font-weight:700;color:#a09070;cursor:pointer;border-bottom:3px solid transparent;transition:all .15s;letter-spacing:.04em}

.tab.active{color:#f4f1ec;border-bottom-color:#c8a96e}

.tab:hover:not(.active){color:#d0c0a0}


/* ── ANNOUNCEMENT ── */

.ann-bar{background:#1e0a3c;padding:20px 32px;display:flex;align-items:center;gap:16px;border-bottom:3px solid #7c3aed;animation:slideDown .3s ease}

@keyframes slideDown{from{opacity:0;transform:translateY(-100%)}to{opacity:1;transform:translateY(0)}}

.ann-msg{font-size:22px;font-weight:700;color:#ede9fe;flex:1}

.ann-close{background:#7c3aed;border:none;color:#fff;border-radius:8px;padding:10px 20px;font-size:16px;cursor:pointer;font-family:inherit;font-weight:700}


/* ── PROGRESS ── */

.prog-hero{background:#fff;border-bottom:2px solid #e5e0d8;padding:28px 36px}

.prog-inner{max-width:1400px;margin:0 auto;display:flex;align-items:center;gap:48px;flex-wrap:wrap}

.prog-pct{font-family:‘DM Mono’,monospace;font-size:80px;font-weight:500;color:#1a1612;line-height:1;letter-spacing:-.04em}

.prog-pct-label{font-size:14px;color:#888;letter-spacing:.15em;text-transform:uppercase;font-family:‘DM Mono’,monospace;margin-top:4px}

.prog-bar-area{flex:1;min-width:260px}

.prog-bar-title{font-size:14px;color:#666;letter-spacing:.15em;text-transform:uppercase;font-family:‘DM Mono’,monospace;margin-bottom:12px}

.prog-track{background:#e5e0d8;border-radius:8px;height:16px;overflow:hidden}

.prog-fill{height:100%;border-radius:8px;background:linear-gradient(90deg,#1a1612,#8b6a40);transition:width 1s cubic-bezier(.16,1,.3,1)}

.prog-nums{display:flex;gap:32px;margin-top:16px;flex-wrap:wrap}

.pn-val{font-family:‘DM Mono’,monospace;font-size:26px;font-weight:500;color:#1a1612}

.pn-label{font-size:13px;color:#888;letter-spacing:.1em;text-transform:uppercase;margin-top:2px}

.now-card{background:#fffbeb;border:2px solid #f59e0b;border-radius:14px;padding:18px 28px;text-align:center}

.now-label{font-size:13px;color:#92400e;letter-spacing:.2em;text-transform:uppercase;font-family:‘DM Mono’,monospace;margin-bottom:8px}

.now-val{font-family:‘DM Mono’,monospace;font-size:28px;font-weight:500;color:#92400e}


/* ── ANN PANEL ── */

.ann-panel{background:#f9f7f3;border-bottom:2px solid #e5e0d8;padding:16px 36px;display:flex;gap:10px;align-items:center;flex-wrap:wrap}

.ann-toggle{background:#1e0a3c;border:none;color:#ede9fe;border-radius:8px;padding:12px 22px;font-size:16px;font-weight:700;cursor:pointer;white-space:nowrap}

.ann-input{flex:1;min-width:240px;background:#fff;border:2px solid #d1d5db;border-radius:8px;color:#1a1612;padding:11px 16px;font-size:17px;outline:none;transition:border-color .2s}

.ann-input:focus{border-color:#7c3aed}

.ann-send{background:#7c3aed;border:none;color:#fff;border-radius:8px;padding:12px 26px;font-size:16px;font-weight:700;cursor:pointer;white-space:nowrap}

.ann-clear{background:#fff;border:2px solid #e5e0d8;color:#888;border-radius:8px;padding:11px 18px;font-size:15px;cursor:pointer;white-space:nowrap}


/* ── HEADER ── */

.header{background:#fff;border-bottom:2px solid #e5e0d8;position:sticky;top:0;z-index:100;box-shadow:0 2px 16px rgba(0,0,0,.07)}

.header-inner{max-width:1400px;margin:0 auto;padding:16px 36px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px}

.site-title{font-size:24px;font-weight:700;color:#1a1612;cursor:pointer;border-bottom:2px solid transparent;transition:border-color .2s}

.site-title:hover{border-bottom-color:#1a1612}

.title-edit{background:transparent;border:none;border-bottom:2px solid #1a1612;color:#1a1612;font-family:inherit;font-size:24px;font-weight:700;outline:none;width:360px}

.live-badge{display:flex;align-items:center;gap:8px;background:#dcfce7;border:2px solid #86efac;border-radius:20px;padding:6px 16px}

.live-dot{width:8px;height:8px;border-radius:50%;background:#22c55e;animation:pulse 2s infinite}

@keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}

.live-txt{font-size:14px;color:#15803d;font-family:‘DM Mono’,monospace;font-weight:500}


/* ── CONTENT ── */

.content{max-width:1400px;margin:0 auto;padding:28px 36px 60px}


/* ── PHOTO UPLOAD ── */

.photo-section{margin-bottom:32px}

.section-title{font-size:20px;font-weight:700;color:#1a1612;margin-bottom:14px;letter-spacing:.02em}

.photo-drop{

border:3px dashed #d1c9be;border-radius:16px;

background:#fff;padding:36px;

text-align:center;cursor:pointer;

transition:all .2s;position:relative;overflow:hidden;

}

.photo-drop:hover,.photo-drop.drag-over{border-color:#1a1612;background:#faf8f5}

.photo-drop-icon{font-size:40px;margin-bottom:12px}

.photo-drop-text{font-size:18px;font-weight:700;color:#555;margin-bottom:4px}

.photo-drop-sub{font-size:15px;color:#aaa}

.photo-input{position:absolute;inset:0;opacity:0;cursor:pointer}

.photo-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:12px;margin-top:16px}

.photo-item{position:relative;border-radius:10px;overflow:hidden;aspect-ratio:4/3;border:2px solid #e5e0d8}

.photo-item img{width:100%;height:100%;object-fit:cover}

.photo-del{position:absolute;top:6px;right:6px;background:rgba(0,0,0,.7);color:#fff;border:none;border-radius:50%;width:28px;height:28px;cursor:pointer;font-size:14px;display:flex;align-items:center;justify-content:center}

.photo-uploading{display:flex;align-items:center;justify-content:center;height:100%;background:#f4f1ec;font-size:13px;color:#888;font-family:‘DM Mono’,monospace}


/* ── CREW ── */

.crew-section{margin-bottom:32px}

.crew-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px}

.crew-card{background:#fff;border:2px solid #e5e0d8;border-radius:14px;padding:20px 22px}

.crew-role{font-size:13px;font-weight:700;color:#888;letter-spacing:.2em;text-transform:uppercase;font-family:‘DM Mono’,monospace;margin-bottom:8px}

.crew-name{font-size:20px;font-weight:700;color:#1a1612;margin-bottom:6px}

.crew-phone{font-size:18px;color:#555;font-family:‘DM Mono’,monospace;margin-bottom:4px}

.crew-note{font-size:15px;color:#888}

.crew-edit-btn{background:transparent;border:2px solid #e5e0d8;color:#888;border-radius:8px;padding:6px 14px;font-size:14px;cursor:pointer;margin-top:10px;transition:all .15s}

.crew-edit-btn:hover{border-color:#1a1612;color:#1a1612}

.crew-add-card{background:#faf8f5;border:2px dashed #d1c9be;border-radius:14px;padding:20px 22px;display:flex;align-items:center;justify-content:center;cursor:pointer;min-height:120px;transition:all .2s;font-size:17px;color:#bbb;font-weight:700}

.crew-add-card:hover{border-color:#1a1612;color:#1a1612;background:#fff}

.crew-ei{background:#f9f7f3;border:2px solid #e5e0d8;border-radius:8px;color:#1a1612;padding:8px 12px;font-size:16px;outline:none;width:100%;margin-bottom:8px;transition:border-color .2s}

.crew-ei:focus{border-color:#1a1612}


/* ── DAYS ── */

.day-section{margin-bottom:24px}

.day-header{

display:flex;align-items:center;gap:16px;

background:#1a1612;color:#f4f1ec;

border-radius:12px;padding:16px 24px;

margin-bottom:0;cursor:pointer;

transition:background .15s;

}

.day-header:hover{background:#2d2520}

.day-num{font-family:‘DM Mono’,monospace;font-size:22px;font-weight:500;letter-spacing:.05em;flex-shrink:0}

.day-date-edit{background:transparent;border:none;border-bottom:1px solid #666;color:#c8a96e;font-family:‘DM Mono’,monospace;font-size:16px;outline:none;width:120px;letter-spacing:.04em}

.day-date-text{font-family:‘DM Mono’,monospace;font-size:16px;color:#c8a96e;letter-spacing:.04em;cursor:text}

.day-location{font-size:17px;color:#c8a96e;flex:1}

.day-stats{font-family:‘DM Mono’,monospace;font-size:14px;color:#888;margin-left:auto;white-space:nowrap}

.day-collapse{font-size:18px;color:#888;margin-left:8px;flex-shrink:0}

.day-add-scene{

background:#fff;border:2px dashed #d1c9be;

border-radius:0 0 12px 12px;

padding:12px 24px;text-align:center;

cursor:pointer;font-size:16px;color:#aaa;

transition:all .15s;margin-top:0;

}

.day-add-scene:hover{border-color:#1a1612;color:#1a1612;background:#faf8f5}

.add-day-btn{

background:#fff;border:3px dashed #d1c9be;

border-radius:14px;padding:20px;

text-align:center;cursor:pointer;

font-size:18px;font-weight:700;color:#bbb;

transition:all .2s;width:100%;

}

.add-day-btn:hover{border-color:#1a1612;color:#1a1612;background:#faf8f5}


/* ── SCENE TABLE ── */

.scene-table{background:#fff;border:2px solid #e5e0d8;border-radius:0 0 12px 12px;overflow:hidden;margin-top:0}

.scene-table-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch}

.s-head{display:grid;grid-template-columns:var(–g);background:#f4f1ec;border-bottom:2px solid #e5e0d8;padding:0 20px;min-width:1000px}

.s-th{padding:12px 10px;font-family:‘DM Mono’,monospace;font-size:11px;letter-spacing:.18em;color:#666;text-transform:uppercase;font-weight:500}

.s-th-sub{font-size:10px;color:#aaa;letter-spacing:.1em;margin-top:1px}

.s-row{display:grid;grid-template-columns:var(–g);padding:0 20px;border-bottom:2px solid #f0ebe3;min-width:1000px;transition:background .12s;animation:rowIn .2s ease forwards;opacity:0}

@keyframes rowIn{from{opacity:0;transform:translateY(3px)}to{opacity:1;transform:translateY(0)}}

.s-row:last-child{border-bottom:none}

.s-row:hover{background:#faf8f5}

.s-row.shooting{background:#fffbeb;border-left:4px solid #f59e0b}

.s-row.done{opacity:.5}

.s-row.dragging{opacity:.2}

.s-row.dragover{outline:2px solid #1a1612;background:#f4f1ec}

.s-td{padding:16px 10px;display:flex;align-items:center;min-width:0}

.s-tdc{flex-direction:column;align-items:flex-start;justify-content:center;gap:5px}


/* scene cell text */

.c-order{font-family:‘DM Mono’,monospace;font-size:13px;color:#ccc;user-select:none}

.c-scene{font-family:‘DM Mono’,monospace;font-size:18px;font-weight:500;color:#1a1612;letter-spacing:.06em}

.c-loc{font-size:17px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

.c-cast{font-size:15px;color:#666;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

.c-pages{font-family:‘DM Mono’,monospace;font-size:16px;color:#888}

.c-time{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;border-radius:8px;font-size:16px;font-weight:700;border:2px solid transparent}

.c-shot-a{font-size:16px;color:#1d4ed8;font-weight:500}

.c-shot-b{font-family:‘DM Mono’,monospace;font-size:13px;color:#888}

.c-cam-a{font-size:16px;color:#15803d;font-weight:500}

.c-cam-b{font-size:13px;color:#888}

.c-vfx{display:inline-block;padding:4px 12px;border-radius:20px;font-size:13px;font-weight:700;letter-spacing:.04em;font-family:‘DM Mono’,monospace;border:2px solid transparent}

.c-vfx-note{font-size:12px;color:#888;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:110px}

.c-stat{display:inline-flex;align-items:center;gap:4px;padding:5px 14px;border-radius:20px;font-size:13px;font-weight:700;letter-spacing:.06em;font-family:‘DM Mono’,monospace;white-space:nowrap;border:2px solid transparent}

.c-qbtns{display:flex;gap:4px;flex-wrap:wrap;margin-top:4px}

.c-qbtn{font-size:12px;padding:3px 9px;border-radius:10px;border:2px solid currentColor;background:transparent;cursor:pointer;font-family:‘DM Mono’,monospace;opacity:.55;transition:opacity .12s;letter-spacing:.04em;white-space:nowrap}

.c-qbtn:hover{opacity:1}

.c-acts{display:flex;flex-direction:column;gap:5px}

.c-abtn{font-size:13px;font-family:‘DM Mono’,monospace;padding:6px 10px;border-radius:6px;cursor:pointer;letter-spacing:.04em;border:2px solid currentColor;background:transparent;transition:opacity .12s;white-space:nowrap}

.c-abtn:hover{opacity:.7}

.c-ae{color:#92400e}.c-an{color:#1d4ed8}.c-ad{color:#b91c1c}.c-as{color:#15803d}.c-ac{color:#888}


/* edit inputs */

.ei{background:#fff;border:2px solid #d1d5db;border-radius:6px;color:#1a1612;padding:7px 10px;font-family:inherit;font-size:16px;outline:none;width:100%;transition:border-color .2s}

.ei:focus{border-color:#1a1612}

.es{background:#fff;border:2px solid #d1d5db;border-radius:6px;color:#1a1612;padding:7px 8px;font-family:inherit;font-size:16px;outline:none;width:100%;cursor:pointer}


/* notes */

.s-notes{background:#faf8f5;border-bottom:2px solid #f0ebe3;padding:12px 56px 16px;display:flex;gap:32px;flex-wrap:wrap;min-width:1000px}

.nc-label{font-family:‘DM Mono’,monospace;font-size:10px;letter-spacing:.2em;color:#aaa;text-transform:uppercase;margin-bottom:4px}

.nc-val{font-size:16px;color:#555}


/* new scene row */

.s-new{display:grid;grid-template-columns:var(–g);padding:0 20px;background:#fffbeb;border-top:2px solid #f59e0b44;min-width:1000px}


/* controls */

.controls{display:flex;gap:8px;flex-wrap:wrap;align-items:center;padding:14px 36px;background:#f9f7f3;border-bottom:2px solid #e5e0d8}

.search{flex:1;min-width:200px;background:#fff;border:2px solid #d1d5db;border-radius:8px;color:#1a1612;padding:11px 16px;font-size:17px;font-family:inherit;outline:none;transition:border-color .2s}

.search:focus{border-color:#1a1612}

.fbtn{font-family:‘DM Mono’,monospace;font-size:14px;background:#fff;border:2px solid #d1d5db;color:#666;border-radius:7px;padding:9px 16px;cursor:pointer;transition:all .12s;white-space:nowrap}

.fbtn:hover{border-color:#1a1612;color:#1a1612}

.fbtn.fat{background:#1a1612;border-color:#1a1612;color:#fff;font-weight:600}

.fbtn.fas{background:#dcfce7;border-color:#16a34a;color:#15803d}


/* modal */

.modal-bg{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:200;display:flex;align-items:center;justify-content:center;padding:24px}

.modal{background:#fff;border-radius:18px;padding:32px;width:100%;max-width:480px;box-shadow:0 24px 80px rgba(0,0,0,.2)}

.modal-title{font-size:22px;font-weight:700;margin-bottom:24px}

.modal-row{margin-bottom:16px}

.modal-label{font-size:14px;color:#888;letter-spacing:.1em;text-transform:uppercase;font-family:‘DM Mono’,monospace;margin-bottom:6px}

.modal-btns{display:flex;gap:10px;margin-top:24px}

.mbtn-save{flex:1;background:#1a1612;border:none;color:#f4f1ec;border-radius:8px;padding:13px;font-size:17px;font-weight:700;cursor:pointer}

.mbtn-cancel{background:#fff;border:2px solid #e5e0d8;color:#888;border-radius:8px;padding:13px 20px;font-size:17px;cursor:pointer}

.mbtn-del{background:#fee2e2;border:2px solid #fca5a5;color:#b91c1c;border-radius:8px;padding:13px 16px;font-size:17px;cursor:pointer}


/* legend */

.legend{display:flex;justify-content:space-between;align-items:flex-start;margin-top:20px;flex-wrap:wrap;gap:14px}

.lg{display:flex;gap:24px;flex-wrap:wrap}

.ls-label{font-family:‘DM Mono’,monospace;font-size:11px;letter-spacing:.2em;color:#aaa;text-transform:uppercase;margin-bottom:6px}

.litems{display:flex;gap:14px;flex-wrap:wrap}

.li{display:flex;align-items:center;gap:6px;font-family:‘DM Mono’,monospace;font-size:13px;color:#666}

.ldot{width:9px;height:9px;border-radius:2px}

.tip{font-family:‘DM Mono’,monospace;font-size:13px;color:#bbb;align-self:flex-end}

.empty{text-align:center;padding:40px;color:#bbb;font-size:16px;font-family:‘DM Mono’,monospace;letter-spacing:.08em}

.loading{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;gap:16px;color:#666;font-family:‘DM Mono’,monospace;font-size:14px;letter-spacing:.3em;text-transform:uppercase}

</style>


</head>

<body>

<div id="root"><div class="loading"><div class="live-dot" style="width:14px;height:14px;background:#1a1612"></div>連線中</div></div>


<script type="text/babel" data-type="module">

const{useState,useEffect,useRef,useCallback}=React;


const TL=["日","夜","晨","昏"];

const TC={"日":{bg:"#fef9c3",bd:"#fbbf24",tx:"#92400e"},"夜":{bg:"#dbeafe",bd:"#60a5fa",tx:"#1e40af"},"晨":{bg:"#ffedd5",bd:"#fb923c",tx:"#9a3412"},"昏":{bg:"#fee2e2",bd:"#f87171",tx:"#991b1b"}};

const SHOTS=["特寫","大特寫","中近景","中景","中全景","全景","大全景","過肩","主觀鏡","空拍"];

const LENS=["14mm","24mm","35mm","50mm","85mm","100mm","135mm","變焦"];

const CAMS=["A機","B機","A機+B機","手持A機","手持B機","空拍機","特殊攝影"];

const MOVS=["固定","跟拍","推軌","升降","手持","搖鏡","Steadicam","空拍"];

const VFX=["無","合成背景","螢幕合成","去背/摳像","CG生物","爆炸特效","天氣特效","時間加速","其他"];

const VS={"無":{bg:"#dcfce7",bd:"#86efac",tx:"#15803d"},"合成背景":{bg:"#ede9fe",bd:"#a78bfa",tx:"#5b21b6"},"螢幕合成":{bg:"#dbeafe",bd:"#60a5fa",tx:"#1e40af"},"去背/摳像":{bg:"#ffedd5",bd:"#fb923c",tx:"#9a3412"},"CG生物":{bg:"#fee2e2",bd:"#f87171",tx:"#991b1b"},"爆炸特效":{bg:"#fff7ed",bd:"#f97316",tx:"#9a3412"},"天氣特效":{bg:"#ecfeff",bd:"#22d3ee",tx:"#155e75"},"時間加速":{bg:"#fdf4ff",bd:"#e879f9",tx:"#86198f"},"其他":{bg:"#f1f5f9",bd:"#94a3b8",tx:"#475569"}};

const ST=["待拍","拍攝中","已完成","跳過"];

const SS={"待拍":{bg:"#f9fafb",bd:"#d1d5db",tx:"#6b7280"},"拍攝中":{bg:"#fffbeb",bd:"#fbbf24",tx:"#92400e"},"已完成":{bg:"#dcfce7",bd:"#86efac",tx:"#15803d"},"跳過":{bg:"#f9fafb",bd:"#d1d5db",tx:"#9ca3af"}};

const SGRID="40px 88px 1fr 56px 1fr 52px 114px 114px 120px 140px 96px";


const CREW_ROLES=["導演","副導演","攝影師","製片","場記","燈光師","收音師","美術","化妝","服裝","演員統籌","其他"];


let _id=Date.now();

const uid=()=>"x"+(++_id);

const empScene=(dayId,order)=>({id:uid(),dayId,scene:"",location:"",time:"日",cast:"",pages:1.0,notes:"",shotType:"中景",lens:"50mm",camera:"A機",movement:"固定",vfx:"無",vfxNote:"",status:"待拍",order});

const empDay=(order)=>({id:uid(),order,date:"",location:"",collapsed:false});

const empCrew=()=>({id:uid(),role:"導演",name:"",phone:"",note:""});


function waitFB(){return new Promise(r=>{const c=()=>window.__fb?r(window.__fb):setTimeout(c,50);c();})}


function Sel({opts,val,set}){return<select className="es" value={val} onChange={e=>set(e.target.value)}>{opts.map(o=><option key={o}>{o}</option>)}</select>}

function In({v,set,ph,type="text",step,min,w,cls="ei"}){return<input className={cls} type={type} step={step} min={min} value={v} onChange={e=>set(e.target.value)} placeholder={ph} style={w?{width:w}:{}}/>}


/* ── CREW MODAL ── */

function CrewModal({crew,onSave,onDelete,onClose}){

  const[form,setForm]=useState(crew||empCrew());

  const s=fn=>setForm(f=>fn(f));

  return(

    <div className="modal-bg" onClick={e=>{if(e.target===e.currentTarget)onClose()}}>

      <div className="modal">

        <div className="modal-title">{crew?"編輯組員":"新增組員"}</div>

        <div className="modal-row"><div className="modal-label">職稱</div><Sel opts={CREW_ROLES} val={form.role} set={v=>s(f=>({...f,role:v}))}/></div>

        <div className="modal-row"><div className="modal-label">姓名</div><In cls="ei" v={form.name} set={v=>s(f=>({...f,name:v}))} ph="姓名"/></div>

        <div className="modal-row"><div className="modal-label">電話</div><In cls="ei" v={form.phone} set={v=>s(f=>({...f,phone:v}))} ph="手機號碼" type="tel"/></div>

        <div className="modal-row"><div className="modal-label">備註</div><In cls="ei" v={form.note} set={v=>s(f=>({...f,note:v}))} ph="備註(可留空)"/></div>

        <div className="modal-btns">

          {crew&&<button className="mbtn-del" onClick={()=>onDelete(crew.id)}>刪除</button>}

          <button className="mbtn-cancel" onClick={onClose}>取消</button>

          <button className="mbtn-save" onClick={()=>onSave(form)}>儲存</button>

        </div>

      </div>

    </div>

  );

}


/* ── DAY MODAL ── */

function DayModal({day,onSave,onDelete,onClose}){

  const[form,setForm]=useState(day||empDay(1));

  const s=fn=>setForm(f=>fn(f));

  return(

    <div className="modal-bg" onClick={e=>{if(e.target===e.currentTarget)onClose()}}>

      <div className="modal">

        <div className="modal-title">{day?"編輯拍攝日":"新增拍攝日"}</div>

        <div className="modal-row"><div className="modal-label">日期</div><In cls="ei" v={form.date} set={v=>s(f=>({...f,date:v}))} ph="例如:2026/05/20(週三)"/></div>

        <div className="modal-row"><div className="modal-label">拍攝地點概述</div><In cls="ei" v={form.location} set={v=>s(f=>({...f,location:v}))} ph="例如:台北內湖攝影棚"/></div>

        <div className="modal-btns">

          {day&&<button className="mbtn-del" onClick={()=>onDelete(day.id)}>刪除此拍攝日</button>}

          <button className="mbtn-cancel" onClick={onClose}>取消</button>

          <button className="mbtn-save" onClick={()=>onSave(form)}>儲存</button>

        </div>

      </div>

    </div>

  );

}


function App(){

  const[days,setDays]=useState([]);

  const[scenes,setScenes]=useState([]);

  const[crew,setCrew]=useState([]);

  const[photos,setPhotos]=useState([]);

  const[meta,setMeta]=useState({projectName:"《無名之城》拍攝順序表",banner:"",bannerActive:false,currentScene:""});

  const[ready,setReady]=useState(false);

  const[tab,setTab]=useState("schedule");

  const[eId,setEId]=useState(null);

  const[eForm,setEForm]=useState(null);

  const[exp,setExp]=useState(null);

  const[drag,setDrag]=useState(null);

  const[over,setOver]=useState(null);

  const[sFilter,setSFilter]=useState("全部");

  const[annTxt,setAnnTxt]=useState("");

  const[showAnn,setShowAnn]=useState(false);

  const[editT,setEditT]=useState(false);

  const[crewModal,setCrewModal]=useState(null);// null | 'new' | crew obj

  const[dayModal,setDayModal]=useState(null);

  const[photoDrag,setPhotoDrag]=useState(false);

  const[uploading,setUploading]=useState(false);

  const[collapsed,setCollapsed]=useState({});

  const fb=useRef(null);

  const seeded=useRef(false);

  const fileRef=useRef();


  useEffect(()=>{

    waitFB().then(f=>{

      fb.current=f;setReady(true);

      const{db,collection,onSnapshot,doc}=f;

      onSnapshot(collection(db,"days"),snap=>{setDays(snap.docs.map(d=>d.data()).sort((a,b)=>a.order-b.order));});

      onSnapshot(collection(db,"scenes"),snap=>{

        if(snap.empty&&!seeded.current){

          seeded.current=true;

          const d1={id:"d1",order:1,date:"2026/05/20(週三)",location:"台北內湖攝影棚"};

          const d2={id:"d2",order:2,date:"2026/05/21(週四)",location:"信義區外景"};

          f.setDoc(doc(db,"days","d1"),d1);f.setDoc(doc(db,"days","d2"),d2);

          [{id:"s1",dayId:"d1",scene:"A-01",location:"咖啡廳內景",time:"日",cast:"主角、女配角",pages:2.5,notes:"需要特調咖啡道具",shotType:"中景",lens:"50mm",camera:"A機",movement:"固定",vfx:"無",vfxNote:"",status:"待拍",order:1},

           {id:"s2",dayId:"d1",scene:"A-02",location:"吧台特寫",time:"日",cast:"主角",pages:1.0,notes:"",shotType:"特寫",lens:"85mm",camera:"A機",movement:"固定",vfx:"無",vfxNote:"",status:"待拍",order:2},

           {id:"s3",dayId:"d2",scene:"B-03",location:"街道外景",time:"夜",cast:"主角",pages:1.0,notes:"需備雨衣",shotType:"全景",lens:"35mm",camera:"A機+B機",movement:"跟拍",vfx:"合成背景",vfxNote:"城市燈光",status:"待拍",order:1}

          ].forEach(s=>f.setDoc(doc(db,"scenes",s.id),s));

          return;

        }

        setScenes(snap.docs.map(d=>d.data()));

      });

      onSnapshot(collection(db,"crew"),snap=>{setCrew(snap.docs.map(d=>d.data()));});

      onSnapshot(collection(db,"photos"),snap=>{setPhotos(snap.docs.map(d=>d.data()).sort((a,b)=>a.order-b.order));});

      onSnapshot(doc(db,"meta","main"),snap=>{if(snap.exists())setMeta(snap.data());});

    });

  },[]);


  const fset=(col,id,data)=>fb.current.setDoc(fb.current.doc(fb.current.db,col,id),data);

  const fdel=(col,id)=>fb.current.deleteDoc(fb.current.doc(fb.current.db,col,id));

  const fm=p=>{const n={...meta,...p};setMeta(n);fset("meta","main",n);};

  const send=()=>{if(!annTxt.trim())return;fm({banner:annTxt,bannerActive:true});setAnnTxt("");setShowAnn(false);};


  // scenes for a day

  const dayScenes=dayId=>scenes.filter(s=>s.dayId===dayId).sort((a,b)=>a.order-b.order);


  const startEdit=s=>{setEId(s.id);setEForm({...s});};

  const saveEdit=()=>{fset("scenes",eForm.id,eForm);setEId(null);setEForm(null);};

  const cancelEdit=()=>{setEId(null);setEForm(null);};

  const delScene=id=>{if(!confirm("確定刪除?"))return;fdel("scenes",id);};

  const addScene=dayId=>{

    const ds=dayScenes(dayId);

    const max=ds.length?Math.max(...ds.map(s=>s.order)):0;

    const s=empScene(dayId,max+1);

    fset("scenes",s.id,s);

    setEId(s.id);setEForm({...s});

  };

  const setStatus=async(sc,status)=>{await fset("scenes",sc.id,{...sc,status});if(status==="拍攝中")fm({currentScene:sc.scene});};

  const moveScene=async(fId,tId,dayId)=>{

    if(fId===tId)return;

    const arr=dayScenes(dayId);

    const fi=arr.findIndex(s=>s.id===fId),ti=arr.findIndex(s=>s.id===tId);

    if(fi<0||ti<0)return;

    const[m]=arr.splice(fi,1);arr.splice(ti,0,m);

    await Promise.all(arr.map((s,i)=>fset("scenes",s.id,{...s,order:i+1})));

  };


  // crew

  const saveCrew=form=>{fset("crew",form.id,form);setCrewModal(null);};

  const delCrew=id=>{if(!confirm("確定刪除?"))return;fdel("crew",id);setCrewModal(null);};


  // days

  const saveDay=form=>{fset("days",form.id,form);setDayModal(null);};

  const delDay=id=>{if(!confirm("確定刪除此拍攝日?所有場次不受影響。"))return;fdel("days",id);setDayModal(null);};

  const addDay=()=>{

    const max=days.length?Math.max(...days.map(d=>d.order)):0;

    const d=empDay(max+1);

    setDayModal({mode:"new",day:d});

  };

  const saveDayModal=form=>{fset("days",form.id,form);setDayModal(null);};


  // photos

  const handleFiles=async files=>{

    if(!files||!files.length)return;

    setUploading(true);

    const{storage,ref,uploadBytes,getDownloadURL}=fb.current;

    for(const file of Array.from(files)){

      if(!file.type.startsWith("image/"))continue;

      const r=ref(storage,`photos/${Date.now()}_${file.name}`);

      await uploadBytes(r,file);

      const url=await getDownloadURL(r);

      const max=photos.length?Math.max(...photos.map(p=>p.order||0)):0;

      const ph={id:uid(),url,name:file.name,order:max+1};

      fset("photos",ph.id,ph);

    }

    setUploading(false);

  };

  const delPhoto=id=>{if(!confirm("確定刪除照片?"))return;fdel("photos",id);};


  const tot=scenes.reduce((s,r)=>s+Number(r.pages),0);

  const done=scenes.filter(s=>s.status==="已完成").length;

  const vfxN=scenes.filter(s=>s.vfx!=="無").length;

  const pct=scenes.length?Math.round(done/scenes.length*100):0;


  if(!ready)return<div className="loading"><div className="live-dot" style={{width:"14px",height:"14px",background:"#1a1612"}}/><span>連線中</span></div>;


  const ef=eForm||{};

  const se=fn=>setEForm(f=>fn(f));


  return(

    <div>

      {/* ── TABS ── */}

      <div className="tabs">

        {[["schedule","📋 拍攝順序表"],["crew","👥 組員聯絡"],["photos","📷 現場照片"]].map(([k,l])=>(

          <div key={k} className={`tab${tab===k?" active":""}`} onClick={()=>setTab(k)}>{l}</div>

        ))}

      </div>


      {/* ── ANNOUNCEMENT ── */}

      {meta.bannerActive&&meta.banner&&(

        <div className="ann-bar">

          <span style={{fontSize:"28px"}}>📢</span>

          <span className="ann-msg">{meta.banner}</span>

          <button className="ann-close" onClick={()=>fm({bannerActive:false})}>✕ 關閉</button>

        </div>

      )}


      {/* ── PROGRESS ── */}

      <div className="prog-hero">

        <div className="prog-inner">

          <div style={{textAlign:"center"}}>

            <div className="prog-pct">{pct}%</div>

            <div className="prog-pct-label">完成進度</div>

          </div>

          <div className="prog-bar-area">

            <div className="prog-bar-title">拍攝完成進度</div>

            <div className="prog-track"><div className="prog-fill" style={{width:`${pct}%`}}/></div>

            <div className="prog-nums">

              <div><div className="pn-val">{done}/{scenes.length}</div><div className="pn-label">已完成</div></div>

              <div><div className="pn-val">{days.length}</div><div className="pn-label">拍攝天數</div></div>

              <div><div className="pn-val">{tot.toFixed(1)}</div><div className="pn-label">總頁數</div></div>

              <div><div className="pn-val">{vfxN}</div><div className="pn-label">特效場</div></div>

            </div>

          </div>

          {meta.currentScene&&(

            <div className="now-card">

              <div className="now-label">NOW SHOOTING</div>

              <div className="now-val">🎬 {meta.currentScene}</div>

            </div>

          )}

        </div>

      </div>


      {/* ── ANN PANEL ── */}

      <div className="ann-panel">

        <button className="ann-toggle" onClick={()=>setShowAnn(p=>!p)}>📢 {showAnn?"收合":"發送全體公告"}</button>

        {meta.bannerActive&&<button className="ann-clear" onClick={()=>fm({bannerActive:false})}>✕ 關閉公告</button>}

        {showAnn&&<>

          <input className="ann-input" value={annTxt} onChange={e=>setAnnTxt(e.target.value)}

            placeholder="輸入公告… 例如:全體放飯!30 分鐘後集合 🍱"

            onKeyDown={e=>{if(e.key==="Enter")send();}} autoFocus/>

          <button className="ann-send" onClick={send}>立即發送</button>

        </>}

      </div>


      {/* ── HEADER ── */}

      <div className="header">

        <div className="header-inner">

          <div>

            {editT

              ?<input className="title-edit" autoFocus value={meta.projectName} onChange={e=>fm({projectName:e.target.value})} onBlur={()=>setEditT(false)} onKeyDown={e=>{if(e.key==="Enter")setEditT(false);}}/>

              :<div className="site-title" onClick={()=>setEditT(true)}>{meta.projectName}</div>

            }

          </div>

          <div className="live-badge"><span className="live-dot"/><span className="live-txt">即時同步</span></div>

        </div>

      </div>


      {/* ══════ TAB: SCHEDULE ══════ */}

      {tab==="schedule"&&(

        <>

          <div className="controls">

            <div style={{display:"flex",gap:"4px",flexWrap:"wrap"}}>

              {["全部",...ST].map(t=>(

                <button key={t} className={`fbtn${sFilter===t?" fas":""}`} onClick={()=>setSFilter(t)}>{t==="全部"?"狀態:全":t}</button>

              ))}

            </div>

          </div>


          <div className="content">

            {days.map(day=>{

              const ds=dayScenes(day.id).filter(s=>sFilter==="全部"||s.status===sFilter);

              const isCollapsed=collapsed[day.id];

              const dayDone=dayScenes(day.id).filter(s=>s.status==="已完成").length;

              return(

                <div className="day-section" key={day.id}>

                  {/* Day Header */}

                  <div className="day-header">

                    <div className="day-num" onClick={()=>setDayModal({mode:"edit",day})}>第 {day.order} 天</div>

                    <div style={{display:"flex",flexDirection:"column",gap:"2px",flex:1}}>

                      <div className="day-date-text" onClick={()=>setDayModal({mode:"edit",day})}>{day.date||"(點擊設定日期)"}</div>

                      {day.location&&<div style={{fontSize:"15px",color:"#888"}}>{day.location}</div>}

                    </div>

                    <div className="day-stats">{dayDone}/{dayScenes(day.id).length} 場完成</div>

                    <button style={{background:"transparent",border:"1px solid #444",color:"#888",borderRadius:"6px",padding:"6px 12px",cursor:"pointer",fontSize:"13px",fontFamily:"'DM Mono',monospace"}} onClick={()=>setDayModal({mode:"edit",day})}>✎ 編輯</button>

                    <div className="day-collapse" onClick={()=>setCollapsed(c=>({...c,[day.id]:!c[day.id]}))}>

                      {isCollapsed?"▼":"▲"}

                    </div>

                  </div>


                  {!isCollapsed&&(

                    <div className="scene-table">

                      <div className="scene-table-scroll">

                        {/* Head */}

                        <div className="s-head" style={{"--g":SGRID}}>

                          {[["#",""],["場號",""],["地點",""],["時",""],["演員",""],["頁",""],["景別","鏡頭"],["攝影機","運動"],["視效","VFX"],["狀態",""],["操作",""]].map(([h,s],i)=>(

                            <div key={i} className="s-th">{h}{s&&<div className="s-th-sub">{s}</div>}</div>

                          ))}

                        </div>

                        {ds.length===0&&<div className="empty">— 尚無場景,請點下方「+ 新增場景」—</div>}

                        {ds.map((sc,idx)=>{

                          const isE=eId===sc.id,isX=exp===sc.id;

                          const rc=["s-row",sc.status==="拍攝中"?"shooting":"",sc.status==="已完成"?"done":"",drag===sc.id?"dragging":"",over===sc.id?"dragover":""].filter(Boolean).join(" ");

                          const tc=TC[sc.time]||TC["日"],vs=VS[sc.vfx]||VS["其他"],ss=SS[sc.status]||SS["待拍"];

                          return(

                            <div key={sc.id}>

                              <div className={rc} style={{"--g":SGRID,animationDelay:`${idx*.03}s`}}

                                draggable={!isE}

                                onDragStart={e=>{setDrag(sc.id);e.dataTransfer.effectAllowed="move";}}

                                onDragOver={e=>{e.preventDefault();setOver(sc.id);}}

                                onDrop={e=>{e.preventDefault();if(drag)moveScene(drag,sc.id,day.id);setDrag(null);setOver(null);}}

                                onDragEnd={()=>{setDrag(null);setOver(null);}}>

                                <div className="s-td"><span className="c-order">⠿{sc.order}</span></div>

                                <div className="s-td">{isE?<input className="ei" value={ef.scene} onChange={e=>se(f=>({...f,scene:e.target.value}))} placeholder="場號"/>:<span className="c-scene">{sc.scene}</span>}</div>

                                <div className="s-td">{isE?<input className="ei" value={ef.location} onChange={e=>se(f=>({...f,location:e.target.value}))} placeholder="地點"/>:<span className="c-loc">{sc.location}</span>}</div>

                                <div className="s-td">{isE?<Sel opts={TL} val={ef.time} set={v=>se(f=>({...f,time:v}))}/>:<span className="c-time" style={{background:tc.bg,borderColor:tc.bd,color:tc.tx}}>{sc.time}</span>}</div>

                                <div className="s-td">{isE?<input className="ei" value={ef.cast} onChange={e=>se(f=>({...f,cast:e.target.value}))} placeholder="演員"/>:<span className="c-cast">{sc.cast}</span>}</div>

                                <div className="s-td">{isE?<input className="ei" type="number" step="0.5" min="0" value={ef.pages} onChange={e=>se(f=>({...f,pages:e.target.value}))} style={{width:"52px"}}/>:<span className="c-pages">{Number(sc.pages).toFixed(1)}</span>}</div>

                                <div className="s-td s-tdc">{isE?(<><Sel opts={SHOTS} val={ef.shotType} set={v=>se(f=>({...f,shotType:v}))}/><Sel opts={LENS} val={ef.lens} set={v=>se(f=>({...f,lens:v}))}/></>):(<><span className="c-shot-a">{sc.shotType}</span><span className="c-shot-b">{sc.lens}</span></>)}</div>

                                <div className="s-td s-tdc">{isE?(<><Sel opts={CAMS} val={ef.camera} set={v=>se(f=>({...f,camera:v}))}/><Sel opts={MOVS} val={ef.movement} set={v=>se(f=>({...f,movement:v}))}/></>):(<><span className="c-cam-a">{sc.camera}</span><span className="c-cam-b">{sc.movement}</span></>)}</div>

                                <div className="s-td s-tdc">{isE?(<><Sel opts={VFX} val={ef.vfx} set={v=>se(f=>({...f,vfx:v}))}/><input className="ei" value={ef.vfxNote} onChange={e=>se(f=>({...f,vfxNote:e.target.value}))} placeholder="說明"/></>):(<><span className="c-vfx" style={{background:vs.bg,borderColor:vs.bd,color:vs.tx}}>{sc.vfx}</span>{sc.vfxNote&&<span className="c-vfx-note">{sc.vfxNote}</span>}</>)}</div>

                                <div className="s-td s-tdc">{isE?<Sel opts={ST} val={ef.status} set={v=>se(f=>({...f,status:v}))}/>:(<><span className="c-stat" style={{background:ss.bg,borderColor:ss.bd,color:ss.tx}}>{sc.status==="拍攝中"&&"🎬 "}{sc.status}</span><div className="c-qbtns">{ST.filter(s=>s!==sc.status).map(s=><button key={s} className="c-qbtn" style={{color:SS[s].tx,borderColor:SS[s].bd}} onClick={()=>setStatus(sc,s)}>→{s}</button>)}</div></>)}</div>

                                <div className="s-td c-acts">{isE?(<><button className="c-abtn c-as" onClick={saveEdit}>✓ 儲存</button><button className="c-abtn c-ac" onClick={cancelEdit}>✕ 取消</button></>):(<><button className="c-abtn c-ae" onClick={()=>startEdit(sc)}>✎ 編輯</button><button className="c-abtn c-an" onClick={()=>setExp(isX?null:sc.id)}>{isX?"▲":"▼"} 備註</button><button className="c-abtn c-ad" onClick={()=>delScene(sc.id)}>✕ 刪除</button></>)}</div>

                              </div>

                              {isX&&!isE&&(

                                <div className="s-notes">

                                  {sc.notes&&<div><div className="nc-label">拍攝備註</div><div className="nc-val">{sc.notes}</div></div>}

                                  {sc.vfxNote&&<div><div className="nc-label">特效說明</div><div className="nc-val">{sc.vfxNote}</div></div>}

                                  {!sc.notes&&!sc.vfxNote&&<span style={{color:"#bbb",fontFamily:"'DM Mono',monospace",fontSize:"14px"}}>— 無備註 —</span>}

                                </div>

                              )}

                            </div>

                          );

                        })}

                      </div>

                      <div className="day-add-scene" onClick={()=>addScene(day.id)}>+ 新增場景到第 {day.order} 天</div>

                    </div>

                  )}

                </div>

              );

            })}


            <button className="add-day-btn" onClick={addDay}>+ 新增拍攝日</button>


            <div className="legend" style={{marginTop:"28px"}}>

              <div className="lg">

                <div><div className="ls-label">時段</div><div className="litems">{TL.map(t=><div key={t} className="li"><div className="ldot" style={{background:TC[t].bd}}/>{t}</div>)}</div></div>

                <div><div className="ls-label">狀態</div><div className="litems">{ST.map(s=><div key={s} className="li"><div className="ldot" style={{background:SS[s].bd}}/>{s}</div>)}</div></div>

                <div><div className="ls-label">視效</div><div className="litems">{Object.entries(VS).filter(([k])=>k!=="無").map(([k,v])=><div key={k} className="li"><div className="ldot" style={{background:v.bd}}/>{k}</div>)}</div></div>

              </div>

              <div className="tip">⠿ 拖曳排序 · ▼ 展開備註 · 🔴 Firebase 即時同步</div>

            </div>

          </div>

        </>

      )}


      {/* ══════ TAB: CREW ══════ */}

      {tab==="crew"&&(

        <div className="content">

          <div className="crew-section">

            <div className="section-title">組員聯絡資訊</div>

            <div className="crew-grid">

              {crew.sort((a,b)=>CREW_ROLES.indexOf(a.role)-CREW_ROLES.indexOf(b.role)).map(c=>(

                <div className="crew-card" key={c.id}>

                  <div className="crew-role">{c.role}</div>

                  <div className="crew-name">{c.name||"(未填寫)"}</div>

                  {c.phone&&<div className="crew-phone">📞 {c.phone}</div>}

                  {c.note&&<div className="crew-note">{c.note}</div>}

                  <button className="crew-edit-btn" onClick={()=>setCrewModal({mode:"edit",crew:c})}>✎ 編輯</button>

                </div>

              ))}

              <div className="crew-add-card" onClick={()=>setCrewModal({mode:"new",crew:null})}>+ 新增組員</div>

            </div>

          </div>

        </div>

      )}


      {/* ══════ TAB: PHOTOS ══════ */}

      {tab==="photos"&&(

        <div className="content">

          <div className="photo-section">

            <div className="section-title">現場照片</div>

            <div

              className={`photo-drop${photoDrag?" drag-over":""}`}

              onDragOver={e=>{e.preventDefault();setPhotoDrag(true);}}

              onDragLeave={()=>setPhotoDrag(false)}

              onDrop={e=>{e.preventDefault();setPhotoDrag(false);handleFiles(e.dataTransfer.files);}}

              onClick={()=>fileRef.current&&fileRef.current.click()}

            >

              <input ref={fileRef} type="file" accept="image/*" multiple className="photo-input"

                onChange={e=>handleFiles(e.target.files)}/>

              <div className="photo-drop-icon">📸</div>

              <div className="photo-drop-text">{uploading?"上傳中…":"拖放照片到這裡,或點擊選擇"}</div>

              <div className="photo-drop-sub">{uploading?"請稍候":"支援 JPG、PNG、HEIC 等格式,可多選"}</div>

            </div>

            {photos.length>0&&(

              <div className="photo-grid">

                {photos.map(p=>(

                  <div className="photo-item" key={p.id}>

                    <img src={p.url} alt={p.name} loading="lazy"/>

                    <button className="photo-del" onClick={()=>delPhoto(p.id)}>✕</button>

                  </div>

                ))}

              </div>

            )}

            {photos.length===0&&!uploading&&(

              <div style={{textAlign:"center",padding:"40px",color:"#bbb",fontFamily:"'DM Mono',monospace",fontSize:"15px"}}>— 尚未上傳任何照片 —</div>

            )}

          </div>

        </div>

      )}


      {/* ── MODALS ── */}

      {crewModal&&(

        <CrewModal

          crew={crewModal.mode==="edit"?crewModal.crew:null}

          onSave={saveCrew}

          onDelete={delCrew}

          onClose={()=>setCrewModal(null)}

        />

      )}

      {dayModal&&(

        <DayModal

          day={dayModal.mode==="edit"?dayModal.day:null}

          onSave={form=>{if(dayModal.mode==="new"){fset("days",form.id,form);}else{fset("days",form.id,form);}setDayModal(null);}}

          onDelete={delDay}

          onClose={()=>setDayModal(null)}

        />

      )}

    </div>

  );

}


ReactDOM.createRoot(document.getElementById("root")).render(<App/>);

</script>


</body>

</html>