Enhance Schedule component by resetting time fields on cancel and draft mode toggle; improve session scheduling logic to prevent conflicts by adjusting start times if necessary.

This commit is contained in:
2025-12-03 17:37:35 +05:00
parent 36d5c88bf0
commit b1557453be
2 changed files with 55 additions and 4 deletions
+11 -2
View File
@@ -96,6 +96,9 @@ function Schedule({ selectedDay, slots, events }: Props) {
function handleClickCancel() { function handleClickCancel() {
setDraftMode(false); setDraftMode(false);
setStartTime("");
setEndTime("");
setDuration(undefined);
} }
async function addSchesuledSession() { async function addSchesuledSession() {
@@ -129,6 +132,9 @@ function Schedule({ selectedDay, slots, events }: Props) {
function handleClickScheduleDemo() { function handleClickScheduleDemo() {
setDateForInstantStart(undefined); setDateForInstantStart(undefined);
setStartAt(new Date()); setStartAt(new Date());
setStartTime("");
setEndTime("");
setDuration(undefined);
setDraftMode(true); setDraftMode(true);
} }
@@ -184,9 +190,12 @@ function Schedule({ selectedDay, slots, events }: Props) {
useEffect(() => { useEffect(() => {
function handleEscKey(e: KeyboardEvent) { function handleEscKey(e: KeyboardEvent) {
if (!draftMode && e.key !== "Escape") return; if (!draftMode || e.key !== "Escape") return;
setDraftMode(false); setDraftMode(false);
setStartTime("");
setEndTime("");
setDuration(undefined);
} }
document.addEventListener("keyup", handleEscKey, false); document.addEventListener("keyup", handleEscKey, false);
@@ -194,7 +203,7 @@ function Schedule({ selectedDay, slots, events }: Props) {
return () => { return () => {
document.removeEventListener("keyup", handleEscKey, false); document.removeEventListener("keyup", handleEscKey, false);
}; };
}, []); }, [draftMode]);
return ( return (
<div className="relative h-screen overflow-y-auto bg-[#F2F2F2] text-sm"> <div className="relative h-screen overflow-y-auto bg-[#F2F2F2] text-sm">
+44 -2
View File
@@ -4,6 +4,7 @@ import Build from "../models/Build.js";
import Schedule from "../models/Schedule.js"; import Schedule from "../models/Schedule.js";
import { import {
addMinutes, addMinutes,
addSeconds,
areIntervalsOverlapping, areIntervalsOverlapping,
endOfDay, endOfDay,
isValid, isValid,
@@ -78,7 +79,7 @@ router.get("/:buildId", async (req, res) => {
router.post("/", async (req, res) => { router.post("/", async (req, res) => {
const { companyId, buildId, slot, startAt, client, duration } = req.body; const { companyId, buildId, slot, startAt, client, duration } = req.body;
const startAtISO = parseISO(startAt); let startAtISO = parseISO(startAt);
if (!isValid(startAtISO)) { if (!isValid(startAtISO)) {
return res.status(400).json({ return res.status(400).json({
@@ -96,9 +97,50 @@ router.post("/", async (req, res) => {
}); });
} }
// Check for conflicts at the same minute and adjust time if needed
// This prevents multiple sessions from starting simultaneously on the same server
// We check all sessions regardless of company, build, or slot to prevent server conflicts
let adjustedStartAt = startAtISO;
let maxAttempts = 100; // Prevent infinite loop
let attempts = 0;
while (attempts < maxAttempts) {
// Find sessions starting within 10 seconds of our target time
// This ensures sessions don't start simultaneously on the same session server
const conflictingSessions = await ScheduledSession.find({
startAt: {
$gte: adjustedStartAt,
$lt: addSeconds(adjustedStartAt, 10),
},
});
if (conflictingSessions.length === 0) {
// No conflict found, use this time
break;
}
// Find the latest conflicting session and schedule after it
// This gives the first build time to start and fill video memory
// so the second build will start on another server with more available memory
const latestSession = conflictingSessions.reduce((latest, session) =>
new Date(session.startAt) > new Date(latest.startAt) ? session : latest
);
adjustedStartAt = addSeconds(new Date(latestSession.startAt), 10);
attempts++;
}
if (attempts >= maxAttempts) {
return res.status(400).json({
status: "error",
message: "Не удалось найти свободное время для планирования сеанса",
});
}
startAtISO = adjustedStartAt;
const endAtISO = addMinutes(startAtISO, duration); const endAtISO = addMinutes(startAtISO, duration);
// Check for overlapping sessions first // Check for overlapping sessions in the same slot
const scheduledSessions = await ScheduledSession.find({ const scheduledSessions = await ScheduledSession.find({
companyId, companyId,
buildId, buildId,