import React from "react"; interface Conflicts { [index: string]: HTMLInputElement[]; } interface ConflictsTmp { [index: string]: Set; } interface IndexToElement { [index: string]: HTMLInputElement; } const indexToElement: IndexToElement = {}; const conflicts: Conflicts = {}; const marks: (HTMLInputElement | null)[][] = []; const Timetable = ({ user }) => { const [editable, setEditable] = React.useState(true); const ref = React.useRef(); const handleSelect = async (event: Event) => { const target: HTMLInputElement = event.target; console.log("select", target.name, target.checked); const changedInputs: any = []; // find whether there are checked input in conflict for (const input of conflicts[target.name]) { if (input.name === target.name) continue; if (input.checked) { alert("Error: Conflict select"); location.reload(); return; } } for (const input of conflicts[target.name]) { if (input.name === target.name) continue; if (target.checked) { if (input.getAttribute("disabled") === null) { input.setAttribute("disabled", "true"); changedInputs.push({ input, disable: false }); } } else { if (input.getAttribute("disabled") === "true") { input.removeAttribute("disabled"); changedInputs.push({ input, disable: true }); } } } // post request const resp = await fetch("/api/record", { method: "POST", headers: { "Content-Type": "appliction/json" }, body: JSON.stringify({ name: target.name, checked: target.checked, user, }), }); if (!resp.ok) { const json = await resp.json(); alert(json.error); // revert conflict changed input for (const { input, disable } of changedInputs) { if (disable) { input.setAttribute("disabled", "true"); } else { input.removeAttribute("disabled"); } } } }; const handleInput = (event: React.ChangeEvent): boolean => { const { target } = event; // validate if (target?.children[0]?.tagName !== "TABLE") { console.log("not a table"); return false; } console.log(target.innerHTML); // turn off editable setEditable(false); const table = target.children[0]; table.setAttribute("border", "1"); // mark cell const conflictsTmp: ConflictsTmp = {}; const tbody = table.children[table.children.length - 1]; for (const tr_index in tbody.children) { const tr = tbody.children[tr_index]; const row: (HTMLInputElement | null)[] = []; for (const td_index in tr.children) { const td = tr.children[td_index]; if (td.tagName !== "TD") continue; if (td.getAttribute("bgcolor")?.toUpperCase() !== "#39CEFF") { row.push(null); continue; } const index = `${tr_index},${td_index}`; const placeholders = td.textContent?.trim().split(","); if (placeholders === undefined) continue; if (conflictsTmp[index] === undefined) conflictsTmp[index] = new Set(); for (const ph of placeholders) conflictsTmp[index].add(ph); // mount click event const input = document.createElement("input"); input.setAttribute("type", "checkbox"); input.onchange = handleSelect; input.name = index; td.innerHTML = ""; td.appendChild(input); indexToElement[index] = input; row.push(input); } marks.push(row); } // resolve conflicts for (const index in conflictsTmp) { if (conflicts[index] === undefined) conflicts[index] = []; for (const ph of Array.from(conflictsTmp[index])) { for (const conflictIndex in conflictsTmp) { if (conflictsTmp[conflictIndex].has(ph)) conflicts[index].push(indexToElement[conflictIndex]); } } } console.log(conflicts); return true; }; const refresh = async () => { const resp = await fetch(`/api/record?name=${user}`); const json = await resp.json(); const occupied: string[] = json.occupied; const myselect: string[] = json.myselect; console.log(json); for (const index in indexToElement) { if (occupied.includes(index)) { indexToElement[index].style.display = "none"; } else { indexToElement[index].style.display = ""; } const includes = myselect.includes(index); indexToElement[index].checked = includes; // after checked, find conflicts input if (includes) { for (const input of conflicts[index]) { if (input.name === index) continue; input.setAttribute("disabled", "true"); } } } }; React.useEffect(() => { const interval = setInterval(() => { refresh(); }, 1000); return () => { clearInterval(interval); }; }); React.useEffect(() => { fetch("/api/html") .then((resp) => resp.json()) .then((json) => { console.log(ref); ref.current.innerHTML = json.html; }) .then(() => { handleInput({ target: ref.current }); refresh(); }); }, []); return ( <> Login as {user}
); }; export default Timetable;