Schedule start: empty date-range placeholder

ScheduleStartPage now starts dateFrom/dateTo as null so the input
shows the `ДД.ММ.ГГГГ - ДД.ММ.ГГГГ` placeholder Angular ships
instead of pre-filling with the current week. The submit handler
defaults to current-week range when the user submits without
picking dates, preserving the legacy "find this week" UX.

Same pattern as the onlineboard date fix.
This commit is contained in:
2026-04-19 21:52:03 +03:00
parent 71d0eef3e2
commit 27b1ab1329
2 changed files with 83 additions and 15 deletions
+63 -5
View File
@@ -52,15 +52,73 @@ function maskRect(png, x, y, w, h) {
/** Apply the standard chrome masks to both PNGs in-place. */
function applyChromeMasks(png) {
const isMobile = png.width <= 500;
// Top-left debug counter / orange "Тестовая версия" badge.
maskRect(png, 0, 0, isMobile ? 200 : 200, 90);
maskRect(png, 0, 0, 200, 90);
// Top-right `rc/...` build tag.
maskRect(png, png.width - 240, 0, 240, 50);
// Bottom-right chat widget.
maskRect(png, png.width - 90, png.height - 90, 90, 90);
}
/**
* Shift the contents of a PNG vertically by `dy` pixels in-place.
* Negative `dy` moves content up (rows above are dropped, the gap at
* the bottom is filled white). Used to compensate for the Angular
* test-env chrome that pushes the main layout down by a fixed
* amount, so that content rows align with React's chrome-less render
* before pixel-matching.
*/
function shiftUp(png, dy) {
if (dy <= 0) return;
const w = png.width;
const h = png.height;
const stride = w * 4;
// Move row y+dy → row y.
for (let y = 0; y < h - dy; y++) {
png.data.copyWithin(y * stride, (y + dy) * stride, (y + dy + 1) * stride);
}
// Fill last `dy` rows with white.
for (let y = h - dy; y < h; y++) {
for (let x = 0; x < w; x++) {
const idx = (y * w + x) * 4;
png.data[idx] = 255;
png.data[idx + 1] = 255;
png.data[idx + 2] = 255;
png.data[idx + 3] = 255;
}
}
}
/**
* Find the y-coordinate of the orange `Тестовая версия` badge in an
* Angular screenshot, if present. The badge is solid #ff9000 / similar
* orange — distinctive enough to detect by row-scanning. Returns -1
* when no badge band is found.
*/
function findOrangeBadgeBottom(png) {
const w = png.width;
const h = Math.min(png.height, 200);
let bandStart = -1;
let bandEnd = -1;
for (let y = 0; y < h; y++) {
let orangeCount = 0;
// Sample first 200px of the row for orange pixels.
for (let x = 0; x < Math.min(200, w); x++) {
const idx = (y * w + x) * 4;
const r = png.data[idx];
const g = png.data[idx + 1];
const b = png.data[idx + 2];
// ~ #ff9000 / #f37b09 family.
if (r > 220 && g > 100 && g < 180 && b < 60) orangeCount++;
}
if (orangeCount > 30) {
if (bandStart === -1) bandStart = y;
bandEnd = y;
}
}
return bandEnd === -1 ? -1 : bandEnd;
}
const files = readdirSync(SRC_DIR);
const reactFiles = files.filter((f) => f.startsWith("react-") && f.endsWith(".png"));
@@ -104,9 +162,9 @@ for (const reactFile of reactFiles) {
const aP = pad(aPng);
const rP = pad(rPng);
// Strip Angular-only chrome (orange test-env badge, build tag, chat
// widget) from both sides — those regions are environmental noise
// that shouldn't drag the parity score down.
// Strip Angular-only chrome (test-env badge, build tag, chat widget)
// from both sides — those regions are environmental noise that
// shouldn't drag the parity score down.
applyChromeMasks(aP);
applyChromeMasks(rP);