fix: Improve formatting and spacing in blog edit and index pages

This commit is contained in:
Wini_Fy
2026-02-03 17:04:54 +07:00
parent 538317eade
commit 8ceb5684e4
2 changed files with 137 additions and 127 deletions

View File

@@ -211,28 +211,30 @@
<label class="form-label fw-medium">Categories</label> <label class="form-label fw-medium">Categories</label>
<div class="d-flex gap-2 mb-2"> <div class="d-flex gap-2 mb-2">
<input type="text" class="form-control form-control-sm" <input type="text" class="form-control form-control-sm"
id="newCategoryInput" id="newCategoryInput" placeholder="Enter new category name">
placeholder="Enter new category name">
<button type="button" class="btn btn-sm btn-outline-primary" <button type="button" class="btn btn-sm btn-outline-primary"
id="addCategoryBtn"> id="addCategoryBtn">
<i class="fas fa-plus me-1"></i>Add <i class="fas fa-plus me-1"></i>Add
</button> </button>
</div> </div>
<div class="form-check-group" id="categoriesContainer" style="max-height: 300px; overflow-y: auto;"> <div class="form-check-group" id="categoriesContainer"
style="max-height: 300px; overflow-y: auto;">
<% categories.forEach(category=> { %> <% categories.forEach(category=> { %>
<div class="form-check d-flex align-items-center justify-content-between mb-2" data-category-id="<%= category._id %>"> <div class="form-check d-flex align-items-center justify-content-between mb-2"
data-category-id="<%= category._id %>">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<input class="form-check-input" type="checkbox" <input class="form-check-input" type="checkbox"
name="category" value="<%= category.name %>" name="category" value="<%= category.name %>"
id="category_<%= category._id %>" <%=blog.category && id="category_<%= category._id %>" <%=blog.category
blog.category.includes(category.name) ? 'checked' : '' && blog.category.includes(category.name) ? 'checked'
%>> : '' %>>
<label class="form-check-label ms-2" <label class="form-check-label ms-2"
for="category_<%= category._id %>"> for="category_<%= category._id %>">
<%= category.name %> <%= category.name %>
</label> </label>
</div> </div>
<button type="button" class="btn btn-sm btn-outline-danger delete-category-btn" <button type="button"
class="btn btn-sm btn-outline-danger delete-category-btn"
data-category-id="<%= category._id %>" data-category-id="<%= category._id %>"
data-category-name="<%= category.name %>" data-category-name="<%= category.name %>"
title="Delete category"> title="Delete category">
@@ -241,7 +243,8 @@
</div> </div>
<% }); %> <% }); %>
</div> </div>
<div class="form-text">Select one or more categories for this blog post.</div> <div class="form-text">Select one or more categories for this blog post.
</div>
</div> </div>
<!-- Tags Column --> <!-- Tags Column -->
@@ -249,29 +252,31 @@
<label class="form-label fw-medium">Tags</label> <label class="form-label fw-medium">Tags</label>
<div class="d-flex gap-2 mb-2"> <div class="d-flex gap-2 mb-2">
<input type="text" class="form-control form-control-sm" <input type="text" class="form-control form-control-sm"
id="newTagInput" id="newTagInput" placeholder="Enter new tag name">
placeholder="Enter new tag name">
<button type="button" class="btn btn-sm btn-outline-primary" <button type="button" class="btn btn-sm btn-outline-primary"
id="addTagBtn"> id="addTagBtn">
<i class="fas fa-plus me-1"></i>Add <i class="fas fa-plus me-1"></i>Add
</button> </button>
</div> </div>
<div class="form-check-group" id="tagsContainer" style="max-height: 300px; overflow-y: auto;"> <div class="form-check-group" id="tagsContainer"
style="max-height: 300px; overflow-y: auto;">
<% tags.forEach(tag=> { %> <% tags.forEach(tag=> { %>
<div class="form-check d-flex align-items-center justify-content-between mb-2" data-tag-id="<%= tag._id %>"> <div class="form-check d-flex align-items-center justify-content-between mb-2"
data-tag-id="<%= tag._id %>">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<input class="form-check-input" type="checkbox" name="tags" <input class="form-check-input" type="checkbox"
value="<%= tag.name %>" id="tag_<%= tag._id %>" name="tags" value="<%= tag.name %>"
<%=blog.tags && blog.tags.includes(tag.name) ? 'checked' id="tag_<%= tag._id %>" <%=blog.tags &&
: '' %>> blog.tags.includes(tag.name) ? 'checked' : '' %>>
<label class="form-check-label ms-2" for="tag_<%= tag._id %>"> <label class="form-check-label ms-2"
<%= tag.name %> for="tag_<%= tag._id %>">
</label> <%= tag.name %>
</label>
</div> </div>
<button type="button" class="btn btn-sm btn-outline-danger delete-tag-btn" <button type="button"
class="btn btn-sm btn-outline-danger delete-tag-btn"
data-tag-id="<%= tag._id %>" data-tag-id="<%= tag._id %>"
data-tag-name="<%= tag.name %>" data-tag-name="<%= tag.name %>" title="Delete tag">
title="Delete tag">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</button> </button>
</div> </div>
@@ -314,7 +319,8 @@
Mark as Featured Post Mark as Featured Post
</label> </label>
</div> </div>
<div class="form-text">Featured posts can be highlighted on the blog page.</div> <div class="form-text">Featured posts can be highlighted on the blog
page.</div>
</div> </div>
</div> </div>
</div> </div>
@@ -371,27 +377,27 @@
try { try {
<% if (blog.content) { %> <% if (blog.content) { %>
const blogContentRaw = <%- JSON.stringify(blog.content) %>; const blogContentRaw = <% - JSON.stringify(blog.content) %>;
if (blogContentRaw) { if (blogContentRaw) {
if (typeof blogContentRaw === 'string' && blogContentRaw.trim()) { if (typeof blogContentRaw === 'string' && blogContentRaw.trim()) {
// Try to parse as JSON string // Try to parse as JSON string
try { try {
initialContentData = JSON.parse(blogContentRaw); initialContentData = JSON.parse(blogContentRaw);
// Validate it has blocks property // Validate it has blocks property
if (!initialContentData.blocks || !Array.isArray(initialContentData.blocks)) { if (!initialContentData.blocks || !Array.isArray(initialContentData.blocks)) {
throw new Error('Invalid EditorJS format'); throw new Error('Invalid EditorJS format');
} }
} catch (e) { } catch (e) {
// If not JSON, convert to EditorJS format // If not JSON, convert to EditorJS format
initialContentData = { initialContentData = {
blocks: [{ blocks: [{
type: 'paragraph', type: 'paragraph',
data: { data: {
text: blogContentRaw text: blogContentRaw
} }
}] }]
}; };
} }
} else if (typeof blogContentRaw === 'object' && blogContentRaw !== null) { } else if (typeof blogContentRaw === 'object' && blogContentRaw !== null) {
// Already an object, check if it's valid EditorJS format // Already an object, check if it's valid EditorJS format
if (blogContentRaw.blocks && Array.isArray(blogContentRaw.blocks)) { if (blogContentRaw.blocks && Array.isArray(blogContentRaw.blocks)) {
@@ -416,27 +422,27 @@
try { try {
<% if (blog.contentAfterQuote) { %> <% if (blog.contentAfterQuote) { %>
const blogContentAfterQuoteRaw = <%- JSON.stringify(blog.contentAfterQuote) %>; const blogContentAfterQuoteRaw = <% - JSON.stringify(blog.contentAfterQuote) %>;
if (blogContentAfterQuoteRaw) { if (blogContentAfterQuoteRaw) {
if (typeof blogContentAfterQuoteRaw === 'string' && blogContentAfterQuoteRaw.trim()) { if (typeof blogContentAfterQuoteRaw === 'string' && blogContentAfterQuoteRaw.trim()) {
// Try to parse as JSON string // Try to parse as JSON string
try { try {
initialContentAfterQuoteData = JSON.parse(blogContentAfterQuoteRaw); initialContentAfterQuoteData = JSON.parse(blogContentAfterQuoteRaw);
// Validate it has blocks property // Validate it has blocks property
if (!initialContentAfterQuoteData.blocks || !Array.isArray(initialContentAfterQuoteData.blocks)) { if (!initialContentAfterQuoteData.blocks || !Array.isArray(initialContentAfterQuoteData.blocks)) {
throw new Error('Invalid EditorJS format'); throw new Error('Invalid EditorJS format');
} }
} catch (e) { } catch (e) {
// If not JSON, convert to EditorJS format // If not JSON, convert to EditorJS format
initialContentAfterQuoteData = { initialContentAfterQuoteData = {
blocks: [{ blocks: [{
type: 'paragraph', type: 'paragraph',
data: { data: {
text: blogContentAfterQuoteRaw text: blogContentAfterQuoteRaw
} }
}] }]
}; };
} }
} else if (typeof blogContentAfterQuoteRaw === 'object' && blogContentAfterQuoteRaw !== null) { } else if (typeof blogContentAfterQuoteRaw === 'object' && blogContentAfterQuoteRaw !== null) {
// Already an object, check if it's valid EditorJS format // Already an object, check if it's valid EditorJS format
if (blogContentAfterQuoteRaw.blocks && Array.isArray(blogContentAfterQuoteRaw.blocks)) { if (blogContentAfterQuoteRaw.blocks && Array.isArray(blogContentAfterQuoteRaw.blocks)) {
@@ -461,54 +467,54 @@
// Build tools object, only including plugins that are loaded // Build tools object, only including plugins that are loaded
const tools = { const tools = {
header: { header: {
class: Header, class: Header,
config: { config: {
levels: [2, 3, 4], levels: [2, 3, 4],
defaultLevel: 2 defaultLevel: 2
} }
}, },
paragraph: { paragraph: {
class: Paragraph, class: Paragraph,
inlineToolbar: true inlineToolbar: true
}, },
list: { list: {
class: List, class: List,
inlineToolbar: true, inlineToolbar: true,
config: { config: {
defaultStyle: 'unordered' defaultStyle: 'unordered'
} }
}, },
image: { image: {
class: Image, class: Image,
config: { config: {
endpoints: { endpoints: {
byFile: '/admin/upload/image?imageType=blog' byFile: '/admin/upload/image?imageType=blog'
}
}
},
quote: {
class: Quote,
inlineToolbar: true,
shortcut: 'CMD+SHIFT+O',
config: {
quotePlaceholder: 'Enter a quote',
captionPlaceholder: 'Quote\'s author'
}
},
marker: {
class: Marker,
shortcut: 'CMD+SHIFT+M'
},
embed: {
class: Embed,
config: {
services: {
youtube: true,
vimeo: true
}
}
} }
}
},
quote: {
class: Quote,
inlineToolbar: true,
shortcut: 'CMD+SHIFT+O',
config: {
quotePlaceholder: 'Enter a quote',
captionPlaceholder: 'Quote\'s author'
}
},
marker: {
class: Marker,
shortcut: 'CMD+SHIFT+M'
},
embed: {
class: Embed,
config: {
services: {
youtube: true,
vimeo: true
}
}
}
}; };
// Add optional plugins if they're loaded // Add optional plugins if they're loaded
@@ -750,7 +756,7 @@
const newCategoryInput = document.getElementById('newCategoryInput'); const newCategoryInput = document.getElementById('newCategoryInput');
const categoriesContainer = document.getElementById('categoriesContainer'); const categoriesContainer = document.getElementById('categoriesContainer');
addCategoryBtn.addEventListener('click', async function() { addCategoryBtn.addEventListener('click', async function () {
const categoryName = newCategoryInput.value.trim(); const categoryName = newCategoryInput.value.trim();
if (!categoryName) { if (!categoryName) {
alert('Please enter a category name'); alert('Please enter a category name');
@@ -815,7 +821,7 @@
}); });
// Allow Enter key to add category // Allow Enter key to add category
newCategoryInput.addEventListener('keypress', function(e) { newCategoryInput.addEventListener('keypress', function (e) {
if (e.key === 'Enter') { if (e.key === 'Enter') {
e.preventDefault(); e.preventDefault();
addCategoryBtn.click(); addCategoryBtn.click();
@@ -827,7 +833,7 @@
const newTagInput = document.getElementById('newTagInput'); const newTagInput = document.getElementById('newTagInput');
const tagsContainer = document.getElementById('tagsContainer'); const tagsContainer = document.getElementById('tagsContainer');
addTagBtn.addEventListener('click', async function() { addTagBtn.addEventListener('click', async function () {
const tagName = newTagInput.value.trim(); const tagName = newTagInput.value.trim();
if (!tagName) { if (!tagName) {
alert('Please enter a tag name'); alert('Please enter a tag name');
@@ -891,7 +897,7 @@
}); });
// Allow Enter key to add tag // Allow Enter key to add tag
newTagInput.addEventListener('keypress', function(e) { newTagInput.addEventListener('keypress', function (e) {
if (e.key === 'Enter') { if (e.key === 'Enter') {
e.preventDefault(); e.preventDefault();
addTagBtn.click(); addTagBtn.click();
@@ -902,8 +908,8 @@
</script> </script>
<!-- Delete Category Confirmation Modal --> <!-- Delete Category Confirmation Modal -->
<div class="modal fade" id="deleteCategoryModal" tabindex="-1" aria-labelledby="deleteCategoryModalLabel" aria-hidden="true" <div class="modal fade" id="deleteCategoryModal" tabindex="-1" aria-labelledby="deleteCategoryModalLabel"
data-bs-backdrop="true" data-bs-keyboard="true"> aria-hidden="true" data-bs-backdrop="true" data-bs-keyboard="true">
<div class="modal-dialog modal-dialog-centered"> <div class="modal-dialog modal-dialog-centered">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header bg-danger text-white"> <div class="modal-header bg-danger text-white">
@@ -914,7 +920,8 @@
aria-label="Close"></button> aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p>Are you sure you want to delete the category "<span id="deleteCategoryName" class="fw-bold"></span>"?</p> <p>Are you sure you want to delete the category "<span id="deleteCategoryName" class="fw-bold"></span>"?
</p>
<p class="text-danger mb-0"> <p class="text-danger mb-0">
<small> <small>
<i class="fas fa-exclamation-triangle me-1"></i>This action cannot be undone.</small> <i class="fas fa-exclamation-triangle me-1"></i>This action cannot be undone.</small>
@@ -961,16 +968,19 @@
<style> <style>
/* Fix modal z-index */ /* Fix modal z-index */
#deleteCategoryModal, #deleteTagModal { #deleteCategoryModal,
#deleteTagModal {
z-index: 2050 !important; z-index: 2050 !important;
} }
#deleteCategoryModal .modal-content, #deleteTagModal .modal-content { #deleteCategoryModal .modal-content,
#deleteTagModal .modal-content {
z-index: 2070 !important; z-index: 2070 !important;
position: relative; position: relative;
} }
#deleteCategoryModal.show, #deleteTagModal.show { #deleteCategoryModal.show,
#deleteTagModal.show {
display: block !important; display: block !important;
} }
</style> </style>
@@ -989,7 +999,7 @@
let currentCategoryBtn = null; let currentCategoryBtn = null;
// Handle category delete buttons // Handle category delete buttons
document.addEventListener('click', function(e) { document.addEventListener('click', function (e) {
if (e.target.closest('.delete-category-btn')) { if (e.target.closest('.delete-category-btn')) {
e.preventDefault(); e.preventDefault();
const btn = e.target.closest('.delete-category-btn'); const btn = e.target.closest('.delete-category-btn');
@@ -1006,7 +1016,7 @@
}); });
// Handle confirm delete category // Handle confirm delete category
document.getElementById('confirmDeleteCategoryBtn').addEventListener('click', async function() { document.getElementById('confirmDeleteCategoryBtn').addEventListener('click', async function () {
if (!currentCategoryId || !currentCategoryBtn) return; if (!currentCategoryId || !currentCategoryBtn) return;
const btn = currentCategoryBtn; const btn = currentCategoryBtn;
@@ -1070,7 +1080,7 @@
let currentTagBtn = null; let currentTagBtn = null;
// Handle tag delete buttons // Handle tag delete buttons
document.addEventListener('click', function(e) { document.addEventListener('click', function (e) {
if (e.target.closest('.delete-tag-btn')) { if (e.target.closest('.delete-tag-btn')) {
e.preventDefault(); e.preventDefault();
const btn = e.target.closest('.delete-tag-btn'); const btn = e.target.closest('.delete-tag-btn');
@@ -1087,7 +1097,7 @@
}); });
// Handle confirm delete tag // Handle confirm delete tag
document.getElementById('confirmDeleteTagBtn').addEventListener('click', async function() { document.getElementById('confirmDeleteTagBtn').addEventListener('click', async function () {
if (!currentTagId || !currentTagBtn) return; if (!currentTagId || !currentTagBtn) return;
const btn = currentTagBtn; const btn = currentTagBtn;

View File

@@ -136,21 +136,21 @@
</td> </td>
<td> <td>
<div class="btn-group" role="group"> <div class="btn-group" role="group">
<% if (typeof frontendUrl !== 'undefined') { %> <% if (typeof frontendUrl !=='undefined' ) { %>
<a href="<%= frontendUrl %>/blog/<%= blog.slug %>" target="_blank" <a href="<%= frontendUrl %>/blog/<%= blog.slug %>" target="_blank"
class="btn btn-sm btn-outline-secondary"> class="btn btn-sm btn-outline-secondary">
<i class="fas fa-external-link-alt me-1"></i>View <i class="fas fa-external-link-alt me-1"></i>View
</a> </a>
<% } %> <% } %>
<a href="/admin/blog/<%= blog._id %>/edit" <a href="/admin/blog/<%= blog._id %>/edit"
class="btn btn-sm btn-outline-primary"> class="btn btn-sm btn-outline-primary">
<i class="fas fa-edit me-1"></i>Edit <i class="fas fa-edit me-1"></i>Edit
</a> </a>
<button type="button" class="btn btn-sm btn-outline-danger" <button type="button" class="btn btn-sm btn-outline-danger"
data-custom-modal="open" data-id="<%= blog._id %>" data-custom-modal="open" data-id="<%= blog._id %>"
data-title="<%= blog.title %>"> data-title="<%= blog.title %>">
<i class="fas fa-trash-alt me-1"></i>Delete <i class="fas fa-trash-alt me-1"></i>Delete
</button> </button>
</div> </div>
</td> </td>
</tr> </tr>