feat(cms): enhance policy editor with fullscreen mode and date picking

Improve the policy content management experience by adding a dedicated fullscreen editing mode and a structured date picker for effective dates.
- Implement fullscreen toggle in `policies-block-editor.js` with corresponding CSS for an expanded workspace
- Add `date` field type to `sharedFields.js` and integrate it into `policiesConfig.js`
- Implement date parsing and formatting utilities in `editor-script.ejs` to handle "Effective Date" strings
- Update `policiesController.js` to use `updateOne` for more reliable document persistence and state reloading
- Add support for `ICON_OPTIONS` in the policy editor configuration
- Refine editor UI layout and overflow handling for better stability with long content strings
This commit is contained in:
Tống Thành Đạt
2026-04-22 12:02:00 +07:00
parent cdaddddfeb
commit a44d005c13
7 changed files with 620 additions and 25 deletions
@@ -408,6 +408,23 @@
return;
}
if (schema.type === "date") {
const input = document.createElement("input");
input.className = "form-control";
input.type = "date";
input.value = toDateInputValue(parent[key]);
input.addEventListener("input", function () {
parent[key] = fromDateInputValue(input.value);
});
input.addEventListener("change", function () {
parent[key] = fromDateInputValue(input.value);
});
col.appendChild(input);
appendHelp(col, schema, parent[key]);
container.appendChild(col);
return;
}
if (schema.type === "image") {
const group = document.createElement("div");
group.className = "input-group";
@@ -805,6 +822,79 @@
return "col-md-6";
}
function toDateInputValue(value) {
const parsed = parseDateValue(value);
if (!parsed) {
return "";
}
const year = parsed.getUTCFullYear();
const month = String(parsed.getUTCMonth() + 1).padStart(2, "0");
const day = String(parsed.getUTCDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}
function fromDateInputValue(value) {
const normalized = String(value || "").trim();
if (!normalized) {
return "";
}
const [year, month, day] = normalized.split("-").map(Number);
if (!year || !month || !day) {
return "";
}
const date = new Date(Date.UTC(year, month - 1, day));
return `Effective Date: ${new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric",
timeZone: "UTC",
}).format(date)}`;
}
function parseDateValue(value) {
const raw = String(value || "").trim();
if (!raw) {
return null;
}
if (/^\d{4}-\d{2}-\d{2}$/.test(raw)) {
const isoDate = new Date(`${raw}T00:00:00Z`);
return Number.isNaN(isoDate.getTime()) ? null : isoDate;
}
const withoutPrefix = raw.replace(/^Effective Date:\s*/i, "").trim();
const textMatch = withoutPrefix.match(
/^([A-Za-z]+)\s+(\d{1,2}),\s*(\d{4})$/,
);
if (textMatch) {
const monthIndex = [
"january",
"february",
"march",
"april",
"may",
"june",
"july",
"august",
"september",
"october",
"november",
"december",
].indexOf(textMatch[1].toLowerCase());
if (monthIndex !== -1) {
return new Date(
Date.UTC(Number(textMatch[3]), monthIndex, Number(textMatch[2])),
);
}
}
const fallbackDate = new Date(`${withoutPrefix} UTC`);
return Number.isNaN(fallbackDate.getTime()) ? null : fallbackDate;
}
function resolveImageUrl(path) {
if (!path) return "";
if (/^https?:\/\//i.test(path)) return path;