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:
@@ -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">
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user