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
+245 -8
View File
@@ -4,6 +4,7 @@
}
.policy-block-editor {
--editor-fullscreen-top-offset: 88px;
--editor-border: rgba(148, 163, 184, 0.22);
--editor-muted: #64748b;
--editor-bg: #f8fafc;
@@ -16,13 +17,34 @@
padding-bottom: 140px;
}
.policy-block-editor.is-editor-fullscreen {
position: fixed;
inset: var(--editor-fullscreen-top-offset) 0 0 0;
z-index: 1080;
width: 100%;
max-width: none !important;
margin: 0;
padding: 1rem 1.25rem 140px;
background: #f8fafc;
overflow: auto;
}
body.policy-editor-fullscreen {
overflow: hidden;
}
.policy-block-editor .editor-shell {
display: grid;
grid-template-columns: minmax(0, 1.18fr) minmax(320px, 0.82fr);
grid-template-columns: minmax(0, 0.98fr) minmax(420px, 1.02fr);
gap: 1.5rem;
align-items: start;
}
.policy-block-editor.is-editor-fullscreen .editor-shell {
grid-template-columns: minmax(0, 0.88fr) minmax(520px, 1.12fr);
min-height: calc(100vh - var(--editor-fullscreen-top-offset) - 77px);
}
.policy-block-editor .editor-panel,
.policy-block-editor .preview-panel {
border: 1px solid var(--editor-border);
@@ -65,10 +87,34 @@
min-height: calc(100vh - 210px);
}
.policy-block-editor.is-editor-fullscreen .editor-panel {
position: static;
min-height: auto;
height: auto;
min-width: 0;
overflow: visible;
}
.policy-block-editor .preview-panel {
min-height: calc(100vh - 210px);
}
.policy-block-editor.is-editor-fullscreen .preview-panel {
position: static;
min-height: auto;
height: auto;
min-width: 0;
overflow: visible;
}
.policy-block-editor .visual-editor {
min-height: 0;
}
.policy-block-editor.is-editor-fullscreen .visual-editor {
min-height: 0;
}
.policy-block-editor .panel-toolbar {
display: flex;
flex-wrap: wrap;
@@ -100,16 +146,32 @@
}
.policy-block-editor .editor-toolbar-title {
min-width: 220px;
min-width: 0;
flex: 1 1 320px;
margin-right: 0.35rem;
}
.policy-block-editor .editor-toolbar-title h1,
.policy-block-editor .editor-toolbar-title p {
overflow-wrap: anywhere;
word-break: break-word;
}
.policy-block-editor .editor-scroll {
max-height: calc(100vh - 340px);
overflow: auto;
padding-right: 0.35rem;
}
.policy-block-editor.is-editor-fullscreen .editor-scroll {
max-height: none;
overflow: visible;
}
.policy-block-editor.is-editor-fullscreen .preview-frame {
overflow: visible;
}
.policy-block-editor .validation-banner {
display: none;
gap: 0.75rem;
@@ -141,6 +203,11 @@
transition: border-color 0.16s ease, box-shadow 0.16s ease, transform 0.16s ease;
}
.policy-block-editor .block-card.has-floating-ui {
overflow: visible;
z-index: 20;
}
.policy-block-editor .block-card:hover,
.policy-block-editor .block-card.is-selected {
border-color: rgba(184, 183, 106, 0.62);
@@ -259,6 +326,139 @@
margin-bottom: 0.9rem;
}
.policy-block-editor .icon-combobox {
position: relative;
}
.policy-block-editor .icon-combobox-trigger {
width: 100%;
border: 1px solid rgba(148, 163, 184, 0.18);
border-radius: 14px;
background: #fff;
color: #334155;
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
padding: 0.72rem 0.85rem;
text-align: left;
}
.policy-block-editor .icon-combobox-trigger:focus,
.policy-block-editor .icon-combobox.is-open .icon-combobox-trigger {
border-color: rgba(184, 183, 106, 0.65);
box-shadow: 0 0 0 3px rgba(184, 183, 106, 0.12);
outline: none;
}
.policy-block-editor .icon-combobox-value {
display: flex;
align-items: center;
gap: 0.65rem;
min-width: 0;
flex: 1;
}
.policy-block-editor .icon-combobox-preview {
width: 34px;
height: 34px;
border-radius: 12px;
display: inline-flex;
align-items: center;
justify-content: center;
background: rgba(184, 183, 106, 0.12);
color: #0f172a;
flex: 0 0 auto;
}
.policy-block-editor .icon-combobox-text {
min-width: 0;
display: flex;
flex-direction: column;
gap: 0.1rem;
}
.policy-block-editor .icon-combobox-text strong,
.policy-block-editor .icon-combobox-text span {
overflow-wrap: anywhere;
word-break: break-word;
}
.policy-block-editor .icon-combobox-caret {
color: #64748b;
flex: 0 0 auto;
}
.policy-block-editor .icon-combobox-panel {
position: absolute;
top: calc(100% + 0.4rem);
left: 0;
right: 0;
z-index: 40;
border: 1px solid rgba(148, 163, 184, 0.18);
border-radius: 16px;
background: #fff;
box-shadow: 0 18px 36px rgba(15, 23, 42, 0.12);
padding: 0.75rem;
}
.policy-block-editor .icon-combobox-panel.is-hidden {
display: none;
}
.policy-block-editor .icon-combobox-search {
margin-bottom: 0.6rem;
}
.policy-block-editor .icon-combobox-options {
display: flex;
flex-direction: column;
gap: 0.35rem;
max-height: 220px;
overflow: auto;
}
.policy-block-editor .icon-combobox-option {
width: 100%;
border: 0;
background: #fff;
border-radius: 12px;
padding: 0.6rem 0.7rem;
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
text-align: left;
}
.policy-block-editor .icon-combobox-option:hover,
.policy-block-editor .icon-combobox-option.is-active {
background: rgba(184, 183, 106, 0.12);
color: #0f172a;
}
.policy-block-editor .icon-combobox-option-main {
display: flex;
align-items: center;
gap: 0.65rem;
min-width: 0;
}
.policy-block-editor .icon-combobox-option-main span {
overflow-wrap: anywhere;
word-break: break-word;
}
.policy-block-editor .icon-combobox-empty {
color: #64748b;
font-size: 0.875rem;
padding: 0.35rem 0.2rem;
}
.policy-block-editor .icon-combobox-empty.is-hidden {
display: none;
}
.policy-block-editor .list-items {
display: flex;
flex-direction: column;
@@ -295,21 +495,26 @@
scroll-behavior: smooth;
}
.policy-block-editor .preview-device {
margin: 0 auto;
.policy-block-editor .preview-panel {
min-width: 0;
}
.policy-block-editor .preview-device {
margin: 0 auto;
min-width: 0;
max-width: 100%;
}
.policy-block-editor .preview-device[data-mode="desktop"] {
width: 1120px;
width: min(100%, 920px);
}
.policy-block-editor .preview-device[data-mode="tablet"] {
width: 820px;
width: min(100%, 820px);
}
.policy-block-editor .preview-device[data-mode="mobile"] {
width: 390px;
width: min(100%, 390px);
}
.policy-block-editor .preview-canvas {
@@ -318,6 +523,8 @@
border: 1px solid rgba(148, 163, 184, 0.14);
padding: 2rem;
min-height: 480px;
overflow-wrap: anywhere;
word-break: break-word;
}
.policy-block-editor .preview-device[data-mode="desktop"] .preview-canvas {
@@ -326,7 +533,7 @@
}
.policy-block-editor .preview-device[data-mode="desktop"] #previewContent {
max-width: none;
max-width: 100%;
}
.policy-block-editor .preview-block {
@@ -335,6 +542,18 @@
scroll-margin-top: 140px;
}
.policy-block-editor .preview-canvas h2,
.policy-block-editor .preview-canvas h3,
.policy-block-editor .preview-canvas h4,
.policy-block-editor .preview-canvas h5,
.policy-block-editor .preview-canvas p,
.policy-block-editor .preview-canvas li,
.policy-block-editor .preview-canvas blockquote,
.policy-block-editor .preview-canvas .text-uppercase {
overflow-wrap: anywhere;
word-break: break-word;
}
.policy-block-editor .preview-block.is-highlighted {
background: rgba(184, 183, 106, 0.12);
outline: 1px solid rgba(184, 183, 106, 0.35);
@@ -369,6 +588,20 @@
padding: 1.1rem 1.2rem;
border: 1px solid rgba(148, 163, 184, 0.16);
background: #f8fafc;
overflow-wrap: anywhere;
word-break: break-word;
}
.policy-block-editor .callout-block > div,
.policy-block-editor .callout-block p,
.policy-block-editor .callout-block li,
.policy-block-editor .callout-block h3,
.policy-block-editor .callout-block h4,
.policy-block-editor .callout-block h5,
.policy-block-editor .callout-block a,
.policy-block-editor .callout-block code {
overflow-wrap: anywhere;
word-break: break-word;
}
.policy-block-editor .callout-block[data-tone="warning"] {
@@ -534,6 +767,10 @@
<button type="button" class="btn btn-primary" id="addBlockButton">
<i class="fas fa-plus me-2"></i>Add block
</button>
<button type="button" class="btn btn-outline-secondary" id="toggleFullscreenButton" aria-pressed="false">
<i class="fas fa-expand me-2" data-role="fullscreen-icon"></i>
<span data-role="fullscreen-label">Full screen edit</span>
</button>
</div>
</div>