324 lines
10 KiB
React
324 lines
10 KiB
React
import { motion, LayoutGroup } from "motion/react";
|
|
|
|
import { useAtom } from "jotai";
|
|
import { useState, useEffect } from "react";
|
|
import Logo from "../assets/logo.svg";
|
|
import { AdminUserFRequest, classUserFRequest } from "../trash/req_mgr";
|
|
import {
|
|
BiCalendar,
|
|
BiCalendarEdit,
|
|
BiExit,
|
|
BiPencil,
|
|
BiTime,
|
|
BiTrash,
|
|
BiX,
|
|
} from "react-icons/bi";
|
|
import { formatDate, parseApiDate } from "../trash/dengi";
|
|
import {
|
|
DengiYesOrNoDialog,
|
|
TextPreloader,
|
|
// CoolMouseBg
|
|
} from "../trash/trash-components";
|
|
import { TableEditor } from "../trash/editorTable";
|
|
import { ifstyle } from "../trash/helpers";
|
|
import { API_BASE_URL } from "../App";
|
|
import { useWebHaptics } from "web-haptics/react";
|
|
|
|
export default function EditorPage(props) {
|
|
const [classId, _setUserId] = useAtom(props.classIDAtom);
|
|
const [adminId, _setAdminId] = useAtom(props.adminAtom);
|
|
const [pickmeMode, _setPickmeMode] = useAtom(props.pickmeModeAtom);
|
|
const [route, setRoute] = useAtom(props.routeAtom);
|
|
const dateRaw = props.secondPathSegment(route);
|
|
useEffect(() => {
|
|
if (
|
|
dateRaw == "" ||
|
|
adminId == "" ||
|
|
!RegExp(/\d{2}-\d{2}-\d{2}$/gm).test(dateRaw)
|
|
) {
|
|
props.setsetRoute(setRoute, "/app");
|
|
}
|
|
}, [dateRaw]);
|
|
const date = parseApiDate(dateRaw);
|
|
const [userFrNames, setUserFrNames] = useState({
|
|
admin: false,
|
|
class: false,
|
|
});
|
|
|
|
useEffect(() => {
|
|
(async () => {
|
|
let cancelled = false;
|
|
const promises = [];
|
|
if (classId !== "")
|
|
// TODO: rewrite this bullshit into one pretty request
|
|
promises.push(
|
|
props.dengi_cash.withCacheAsync(`userfr-${classId}`, async () =>
|
|
classUserFRequest(API_BASE_URL, classId),
|
|
),
|
|
);
|
|
else promises.push(Promise.resolve([0]));
|
|
if (adminId !== "")
|
|
promises.push(
|
|
props.dengi_cash.withCacheAsync(`adminfr-${adminId}`, async () =>
|
|
AdminUserFRequest(API_BASE_URL, adminId),
|
|
),
|
|
);
|
|
else promises.push(Promise.resolve([0]));
|
|
Promise.all(promises).then(([classRes, adminRes]) => {
|
|
if (cancelled) return;
|
|
const classF = classRes[0] === 1 ? classRes[1].userclassname : false;
|
|
const adminF = adminRes[0] === 1 ? adminRes[1].adminName : false;
|
|
setUserFrNames({ admin: adminF, class: classF });
|
|
});
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
})();
|
|
}, [classId, adminId]);
|
|
// on rerenderr
|
|
useEffect(() => {
|
|
// console.log("editor cache clean");
|
|
props.dengi_cash.delete(
|
|
"editor_schedule",
|
|
"editor_changes",
|
|
"editor_rings",
|
|
);
|
|
}, []); // clean on full rerender
|
|
|
|
const { trigger } = useWebHaptics();
|
|
|
|
// tabs
|
|
const tabs = [
|
|
[
|
|
<>
|
|
<BiCalendarEdit />
|
|
замены
|
|
</>,
|
|
<>{/* content */}</>,
|
|
],
|
|
[
|
|
<>
|
|
<BiCalendar />
|
|
расписание
|
|
</>,
|
|
<>{/* content */}</>,
|
|
],
|
|
[
|
|
<>
|
|
<BiTime />
|
|
звонки
|
|
</>,
|
|
<>{/* content */}</>,
|
|
],
|
|
];
|
|
const [selectedTab, selectTab] = useState(0);
|
|
const [changesWereMade, setChangesWereMade] = useState(false);
|
|
const [tabDiagShow, setTabDiagShow] = useState(false);
|
|
const [tabDiagRequestTab, setTabDiagRequestTab] = useState(0); // 404 for exit
|
|
return (
|
|
<>
|
|
<div className="overflow-x-hidden">
|
|
<DengiYesOrNoDialog
|
|
show={tabDiagShow}
|
|
content={{
|
|
message: "изменения будут удалены. продолжить?",
|
|
yesBtn: "да",
|
|
noBtn: "нет",
|
|
yesIcon: <BiTrash />,
|
|
noIcon: <BiX size={20} />,
|
|
bgDissmis: true,
|
|
yesBtnStyle: "bg-red-500! text-white! md:hover:bg-red-600!",
|
|
}}
|
|
onAnswer={(answ) => {
|
|
if (answ && tabDiagRequestTab == 404) location.hash = "/app";
|
|
else if (answ) selectTab(tabDiagRequestTab);
|
|
setTabDiagShow(0);
|
|
setTabDiagRequestTab(0);
|
|
}}
|
|
/>
|
|
</div>
|
|
<div className="w-full h-screen flex flex-col items-center justify-center z-5">
|
|
<motion.div
|
|
className="w-screen h-screen flex flex-col items-center sm:max-w-[500px] sm:max-h-[90vh] gap-3"
|
|
initial={{ scale: 0.99, opacity: 0, y: 5 }}
|
|
animate={{ scale: 1, opacity: 1, y: 0 }}
|
|
>
|
|
<motion.div
|
|
className="flex items-center w-full justify-between px-5"
|
|
layout="position"
|
|
>
|
|
<motion.img
|
|
src={Logo}
|
|
alt="logo"
|
|
width={100}
|
|
draggable={false}
|
|
layout="position"
|
|
/>
|
|
<motion.div className="flex gap-5 items-center">
|
|
<motion.span
|
|
className="font-light flex gap-2 items-center"
|
|
variants={{ hidden: { opacity: 0 }, show: { opacity: 1 } }}
|
|
initial="hidden"
|
|
animate="show"
|
|
exit="hidden"
|
|
>
|
|
<span>
|
|
{userFrNames["class"] ? userFrNames["class"] : classId}
|
|
</span>
|
|
<span
|
|
className={`block h-[1px] w-[20px] -rotate-45 ${
|
|
pickmeMode ? "bg-pink-600" : "bg-zinc-600"
|
|
} `}
|
|
/>
|
|
<span>
|
|
{userFrNames["admin"] ? (
|
|
userFrNames["admin"]
|
|
) : (
|
|
<TextPreloader />
|
|
)}
|
|
</span>
|
|
</motion.span>
|
|
<motion.span
|
|
layout="position"
|
|
className={"rounded-full w-fit h-fit"}
|
|
></motion.span>
|
|
</motion.div>
|
|
</motion.div>
|
|
<motion.div
|
|
layout="position"
|
|
className="flex w-full px-5 flex-col gap-5"
|
|
>
|
|
<div className="flex w-full justify-between">
|
|
<div
|
|
className={
|
|
"flex items-center gap-2 transition-colors " +
|
|
ifstyle(
|
|
selectedTab == 2,
|
|
"text-transparent pointer-events-none select-none",
|
|
)
|
|
}
|
|
>
|
|
<BiPencil
|
|
color={pickmeMode ? "#FF0078" : undefined}
|
|
size={25}
|
|
/>
|
|
{formatDate(date)}
|
|
</div>
|
|
<div>
|
|
<a
|
|
href="#/app"
|
|
className={`flex items-center justify-center border p-1 gap-2 px-2 cursor-pointer rounded-xl transition-colors ${
|
|
pickmeMode
|
|
? "border-pink-400/30 text-pink-300/80 lg:hover:text-white lg:hover:border-pink-500"
|
|
: "border-zinc-700 text-zinc-400 lg:hover:text-white lg:hover:border-white"
|
|
} `}
|
|
onClick={(e) => {
|
|
if (changesWereMade) {
|
|
e.preventDefault();
|
|
setTabDiagRequestTab(404);
|
|
setTabDiagShow(true);
|
|
}
|
|
}}
|
|
>
|
|
<BiExit /> назад
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div
|
|
className={`flex items-center justify-center gap-5 select-none bg-grain ${
|
|
pickmeMode ? "bg-pink-900/35" : "bg-zinc-800/35"
|
|
} p-2 rounded-xl`}
|
|
>
|
|
<LayoutGroup>
|
|
{tabs.map((item, idx) => (
|
|
<div
|
|
className={
|
|
`flex items-center justify-center gap-1 p-2 rounded-2xl ${
|
|
pickmeMode
|
|
? "md:hover:bg-pink-400/10"
|
|
: "md:hover:bg-white/10"
|
|
} transition-colors relative md:hover:text-white cursor-pointer ` +
|
|
(idx == selectedTab ? "text-white" : "text-zinc-500")
|
|
}
|
|
key={idx}
|
|
onClick={() => {
|
|
if (selectedTab == idx) return;
|
|
trigger([{ duration: 35 }], { intensity: 1 });
|
|
if (!changesWereMade) {
|
|
selectTab(idx);
|
|
} else {
|
|
setTabDiagRequestTab(idx);
|
|
setTabDiagShow(true);
|
|
}
|
|
}}
|
|
>
|
|
{item}
|
|
{idx == selectedTab && (
|
|
<motion.div
|
|
layoutId="editor_tabs_underline"
|
|
className={`absolute -bottom-[4px] left-0 h-[3px] ${
|
|
pickmeMode ? "bg-pink-500/70" : "bg-white/70"
|
|
} rounded-full w-full`}
|
|
/>
|
|
)}
|
|
</div>
|
|
))}
|
|
</LayoutGroup>
|
|
</div>
|
|
|
|
<motion.div className="flex flex-col gap-2 pb-10">
|
|
{
|
|
<TableEditor
|
|
{...{
|
|
selectedTab,
|
|
setChangesWereMade,
|
|
date,
|
|
adminId,
|
|
pickmeMode,
|
|
}}
|
|
dengiCash={props.dengi_cash}
|
|
/>
|
|
}
|
|
</motion.div>
|
|
</motion.div>
|
|
</motion.div>
|
|
{/* <CoolMouseBg /> */}
|
|
</div>
|
|
<DefaultEditorBG pickmeMode={pickmeMode} />
|
|
</>
|
|
);
|
|
}
|
|
function DefaultEditorBG({ pickmeMode }) {
|
|
return (
|
|
<div
|
|
className={`w-full h-screen bg-gradient-to-br fixed top-0 left-0 via-black via-50% gap-5 overflow-hidden ${
|
|
!pickmeMode ? "from-white/2" : "from-pink-400/10"
|
|
} to-blue-500/10 -z-5 bg-fixed`}
|
|
/>
|
|
);
|
|
}
|
|
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|
|
// ВАНЕЧКА ПИКМИЛЁВ <3
|