diff --git a/sisvietnamvn_main/CheckNewsGrid.java b/sisvietnamvn_main/CheckNewsGrid.java
new file mode 100644
index 00000000..9380f1b3
--- /dev/null
+++ b/sisvietnamvn_main/CheckNewsGrid.java
@@ -0,0 +1,20 @@
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+
+public class CheckNewsGrid {
+ public static void main(String[] args) {
+ try {
+ Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/sisvietnam", "sisvietnam", "sisvietnam");
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("SELECT html_template FROM sis_component_template WHERE slug = 'news-grid'");
+ if (rs.next()) {
+ System.out.println("TEMPLATE:");
+ System.out.println(rs.getString("html_template"));
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/sisvietnamvn_main/CheckPosts.java b/sisvietnamvn_main/CheckPosts.java
new file mode 100644
index 00000000..7e473cca
--- /dev/null
+++ b/sisvietnamvn_main/CheckPosts.java
@@ -0,0 +1,19 @@
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+
+public class CheckPosts {
+ public static void main(String[] args) {
+ try {
+ Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/sisvietnam", "sisvietnam", "sisvietnam");
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("SELECT id, slug, featured_image FROM sis_post WHERE featured_image IS NOT NULL");
+ while (rs.next()) {
+ System.out.println("ID: " + rs.getLong("id") + " Slug: " + rs.getString("slug") + " IMG: " + rs.getString("featured_image"));
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/sisvietnamvn_main/CheckRender.java b/sisvietnamvn_main/CheckRender.java
new file mode 100644
index 00000000..26da9d2c
--- /dev/null
+++ b/sisvietnamvn_main/CheckRender.java
@@ -0,0 +1,32 @@
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.Scanner;
+
+public class CheckRender {
+ public static void main(String[] args) {
+ try {
+ URL url = new URL("http://localhost:8080/");
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("GET");
+ conn.connect();
+ if (conn.getResponseCode() == 200) {
+ InputStream is = conn.getInputStream();
+ Scanner scanner = new Scanner(is, "UTF-8").useDelimiter("\\A");
+ String html = scanner.hasNext() ? scanner.next() : "";
+
+ int idx = html.indexOf("news-card__image");
+ if (idx != -1) {
+ System.out.println(html.substring(idx, Math.min(idx + 300, html.length())));
+ } else {
+ System.out.println("Could not find news-card__image in HTML");
+ }
+ } else {
+ System.out.println("HTTP " + conn.getResponseCode());
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/sisvietnamvn_main/FixNewsGrid.java b/sisvietnamvn_main/FixNewsGrid.java
new file mode 100644
index 00000000..34f3e863
--- /dev/null
+++ b/sisvietnamvn_main/FixNewsGrid.java
@@ -0,0 +1,40 @@
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+
+public class FixNewsGrid {
+ public static void main(String[] args) {
+ try {
+ Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/sisvietnam", "sisvietnam", "sisvietnam");
+ String correctHtml = "{{#each news_posts}}\n" +
+ "
\n" +
+ "
\n" +
+ " \n" +
+ "
\n" +
+ "

\n" +
+ "
\n" +
+ "
\n" +
+ " \n" +
+ "
\n" +
+ "
\n" +
+ "
{{date}}
\n" +
+ "
\n" +
+ " \n" +
+ "
\n" +
+ "{{/each}}";
+
+ PreparedStatement pstmt = conn.prepareStatement("UPDATE sis_component_template SET html_template = ? WHERE slug = 'news-grid'");
+ pstmt.setString(1, correctHtml);
+ int updated = pstmt.executeUpdate();
+ System.out.println("Updated rows: " + updated);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/sisvietnamvn_main/FixPosts.java b/sisvietnamvn_main/FixPosts.java
new file mode 100644
index 00000000..71d6b2df
--- /dev/null
+++ b/sisvietnamvn_main/FixPosts.java
@@ -0,0 +1,33 @@
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class FixPosts {
+ public static void main(String[] args) {
+ try {
+ Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/sisvietnam", "sisvietnam", "sisvietnam");
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("SELECT id, content FROM sis_post WHERE featured_image IS NULL");
+ Pattern p = Pattern.compile("
]+src\\s*=\\s*['\"]([^'\"]+)['\"][^>]*>");
+ int count = 0;
+ while (rs.next()) {
+ String content = rs.getString("content");
+ if (content != null) {
+ Matcher m = p.matcher(content);
+ if (m.find()) {
+ String imgUrl = m.group(1);
+ Statement updateStmt = conn.createStatement();
+ updateStmt.executeUpdate("UPDATE sis_post SET featured_image = '" + imgUrl + "' WHERE id = " + rs.getLong("id"));
+ count++;
+ }
+ }
+ }
+ System.out.println("Updated " + count + " posts with featured images from content.");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/sisvietnamvn_main/TestDb.java b/sisvietnamvn_main/TestDb.java
new file mode 100644
index 00000000..c80e86e5
--- /dev/null
+++ b/sisvietnamvn_main/TestDb.java
@@ -0,0 +1,26 @@
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+
+public class TestDb {
+ public static void main(String[] args) {
+ try {
+ Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521/sisvietnam", "sisvietnam", "sisvietnam");
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("SELECT id, slug, html_template FROM sis_component_template");
+ while (rs.next()) {
+ System.out.println("--- ID: " + rs.getInt("id") + " SLUG: " + rs.getString("slug") + " ---");
+ String html = rs.getString("html_template");
+ if (html != null && html.contains("\\n")) {
+ System.out.println("CONTAINS LITERAL \\n!");
+ } else if (html != null && html.contains("\n")) {
+ System.out.println("Contains actual newlines.");
+ }
+ System.out.println(html);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/sisvietnamvn_main/TestReplace.java b/sisvietnamvn_main/TestReplace.java
new file mode 100644
index 00000000..1af64371
--- /dev/null
+++ b/sisvietnamvn_main/TestReplace.java
@@ -0,0 +1,10 @@
+import java.util.regex.Matcher;
+public class TestReplace {
+ public static void main(String[] args) {
+ String test = "Line 1\nLine 2";
+ System.out.println("Original:");
+ System.out.println(test);
+ System.out.println("Quote:");
+ System.out.println(Matcher.quoteReplacement(test));
+ }
+}
diff --git a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/NewsEventsSeeder.java b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/NewsEventsSeeder.java
deleted file mode 100644
index 27866262..00000000
--- a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/NewsEventsSeeder.java
+++ /dev/null
@@ -1,110 +0,0 @@
-package com.sisvietnamvn.web;
-
-import com.sisvietnamvn.web.domain.ComponentTemplate;
-import com.sisvietnamvn.web.repository.ComponentTemplateRepository;
-import org.springframework.boot.CommandLineRunner;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.util.Optional;
-
-@Component
-public class NewsEventsSeeder implements CommandLineRunner {
-
- private final ComponentTemplateRepository repository;
-
- public NewsEventsSeeder(ComponentTemplateRepository repository) {
- this.repository = repository;
- }
-
- @Override
- @Transactional
- public void run(String... args) throws Exception {
- seedNewsGrid();
- seedEventsList();
- }
-
- private void seedNewsGrid() {
- String slug = "news-grid";
- Optional opt = repository.findBySlug(slug);
- ComponentTemplate t = opt.orElse(new ComponentTemplate());
- t.setSlug(slug);
- t.setName("News Grid");
- t.setDescription("Dynamic grid layout for news.");
- t.setActive(true);
-
- String html =
- "\n" +
- "\n" +
- " {{#each news_posts}}\n" +
- "
\n" +
- "
\n" +
- "
\n" +
- "
TIN TỨC\n" +
- "
\n" +
- "
{{date}}
\n" +
- "
\n" +
- "
\n" +
- " {{/each}}\n" +
- "
\n";
-
- t.setHtmlTemplate(html);
- repository.save(t);
- System.out.println("====== SUCCESS: SEEDED news-grid ComponentTemplate =====");
- }
-
- private void seedEventsList() {
- String slug = "events-list";
- Optional opt = repository.findBySlug(slug);
- ComponentTemplate t = opt.orElse(new ComponentTemplate());
- t.setSlug(slug);
- t.setName("Events List");
- t.setDescription("Vertical list layout for events with event times.");
- t.setActive(true);
-
- String html =
- "\n" +
- "\n" +
- " {{#each event_posts}}\n" +
- "
\n" +
- "
\n" +
- " {{eventMonth}}\n" +
- " {{eventDay}}\n" +
- "
\n" +
- "
\n" +
- "
\n" +
- "
{{eventTimeStr}}
{{excerpt}}
\n" +
- "
\n" +
- "
\n" +
- " {{/each}}\n" +
- "
\n";
-
- t.setHtmlTemplate(html);
- repository.save(t);
- System.out.println("====== SUCCESS: SEEDED events-list ComponentTemplate =====");
- }
-}
diff --git a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/controller/manage/ManagePageController.java b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/controller/manage/ManagePageController.java
index 64e61ad5..3fe37544 100644
--- a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/controller/manage/ManagePageController.java
+++ b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/controller/manage/ManagePageController.java
@@ -149,4 +149,20 @@ public class ManagePageController {
redirectAttributes.addFlashAttribute("successMessage", "Page deleted successfully!");
return "redirect:/manage/pages";
}
+
+ /**
+ * POST /manage/pages/{id}/duplicate — Duplicate a page.
+ */
+ @PostMapping("/{id}/duplicate")
+ public String duplicatePage(@PathVariable Long id, RedirectAttributes redirectAttributes) {
+ LOG.debug("Request to duplicate Page : {}", id);
+ try {
+ pageService.duplicate(id);
+ redirectAttributes.addFlashAttribute("successMessage", "Page duplicated successfully!");
+ } catch (Exception e) {
+ LOG.error("Failed to duplicate page", e);
+ redirectAttributes.addFlashAttribute("errorMessage", "Failed to duplicate page.");
+ }
+ return "redirect:/manage/pages";
+ }
}
diff --git a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/controller/manage/ManagePostController.java b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/controller/manage/ManagePostController.java
index a346e0c6..f4c19a06 100644
--- a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/controller/manage/ManagePostController.java
+++ b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/controller/manage/ManagePostController.java
@@ -170,6 +170,22 @@ public class ManagePostController {
return "redirect:/manage/posts";
}
+ /**
+ * POST /manage/posts/{id}/duplicate — Duplicate a post.
+ */
+ @PostMapping("/{id}/duplicate")
+ public String duplicatePost(@PathVariable Long id, RedirectAttributes redirectAttributes) {
+ LOG.debug("Request to duplicate Post : {}", id);
+ try {
+ postService.duplicate(id);
+ redirectAttributes.addFlashAttribute("successMessage", "Post duplicated successfully!");
+ } catch (Exception e) {
+ LOG.error("Failed to duplicate post", e);
+ redirectAttributes.addFlashAttribute("errorMessage", "Failed to duplicate post.");
+ }
+ return "redirect:/manage/posts";
+ }
+
/**
* Populate common model attributes for the post form.
*/
diff --git a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/controller/manage/TestTemplateController.java b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/controller/manage/TestTemplateController.java
new file mode 100644
index 00000000..f6c75047
--- /dev/null
+++ b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/controller/manage/TestTemplateController.java
@@ -0,0 +1,23 @@
+package com.sisvietnamvn.web.controller.manage;
+
+import com.sisvietnamvn.web.domain.ComponentTemplate;
+import com.sisvietnamvn.web.repository.ComponentTemplateRepository;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import java.util.stream.Collectors;
+
+@RestController
+public class TestTemplateController {
+ private final ComponentTemplateRepository repo;
+
+ public TestTemplateController(ComponentTemplateRepository repo) {
+ this.repo = repo;
+ }
+
+ @GetMapping("/api/test-templates")
+ public String test() {
+ return repo.findAll().stream()
+ .map(t -> "SLUG: " + t.getSlug() + "\nHTML: " + t.getHtmlTemplate() + "\nHAS_BACKSLASH_N: " + (t.getHtmlTemplate().contains("\\n")))
+ .collect(Collectors.joining("\n\n"));
+ }
+}
diff --git a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/domain/Post.java b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/domain/Post.java
index b14a69d9..6876c182 100644
--- a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/domain/Post.java
+++ b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/domain/Post.java
@@ -146,6 +146,10 @@ public class Post extends AbstractAuditingEntity {
return layout;
}
+ public void setLayout(PostLayout layout) {
+ this.layout = layout;
+ }
+
public Category getCategory() {
return category;
}
@@ -162,6 +166,24 @@ public class Post extends AbstractAuditingEntity {
this.eventTime = eventTime;
}
+ @jakarta.persistence.Transient
+ public String getEventTimeLocal() {
+ if (this.eventTime != null) {
+ return java.time.LocalDateTime.ofInstant(this.eventTime, java.time.ZoneId.systemDefault())
+ .format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm"));
+ }
+ return null;
+ }
+
+ public void setEventTimeLocal(String eventTimeLocal) {
+ if (eventTimeLocal != null && !eventTimeLocal.isEmpty()) {
+ this.eventTime = java.time.LocalDateTime.parse(eventTimeLocal, java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm"))
+ .atZone(java.time.ZoneId.systemDefault()).toInstant();
+ } else {
+ this.eventTime = null;
+ }
+ }
+
public Set getTags() {
return tags;
}
diff --git a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/domain/PostLayout.java b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/domain/PostLayout.java
index 1cd69c56..782691f6 100644
--- a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/domain/PostLayout.java
+++ b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/domain/PostLayout.java
@@ -6,5 +6,6 @@ package com.sisvietnamvn.web.domain;
public enum PostLayout {
STANDARD,
SIDEBAR,
- FULL_WIDTH
+ FULL_WIDTH,
+ EVENT
}
diff --git a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/ComponentTemplateService.java b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/ComponentTemplateService.java
index aef72e83..78e55c54 100644
--- a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/ComponentTemplateService.java
+++ b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/ComponentTemplateService.java
@@ -215,9 +215,12 @@ public class ComponentTemplateService {
java.time.format.DateTimeFormatter timeFormatter = java.time.format.DateTimeFormatter.ofPattern("hh:mm a").withZone(java.time.ZoneId.systemDefault());
for (com.sisvietnamvn.web.domain.Post post : posts) {
String itemHtml = loopBody;
- itemHtml = itemHtml.replace("{{title}}", post.getTitle() != null ? post.getTitle() : "");
+ String safeTitle = post.getTitle() != null ? org.springframework.web.util.HtmlUtils.htmlEscape(post.getTitle()) : "";
+ String safeExcerpt = post.getExcerpt() != null ? org.springframework.web.util.HtmlUtils.htmlEscape(post.getExcerpt()) : "";
+
+ itemHtml = itemHtml.replace("{{title}}", safeTitle);
itemHtml = itemHtml.replace("{{slug}}", post.getSlug() != null ? post.getSlug() : "");
- itemHtml = itemHtml.replace("{{excerpt}}", post.getExcerpt() != null ? post.getExcerpt() : "");
+ itemHtml = itemHtml.replace("{{excerpt}}", safeExcerpt);
itemHtml = itemHtml.replace("{{featuredImageUrl}}", post.getFeaturedImage() != null ? post.getFeaturedImage() : "");
String dateStr = post.getCreatedDate() != null ? formatter.format(post.getCreatedDate()) : "";
itemHtml = itemHtml.replace("{{date}}", dateStr);
diff --git a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/ImportExportService.java b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/ImportExportService.java
index b43788ab..860abb21 100644
--- a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/ImportExportService.java
+++ b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/ImportExportService.java
@@ -240,6 +240,15 @@ public class ImportExportService {
post.setContent(content);
post.setExcerpt(excerpt);
post.setStatus(status);
+
+ // Extract first image from content as fallback featured image
+ if (content != null) {
+ java.util.regex.Matcher m = java.util.regex.Pattern.compile("
]+src\\s*=\\s*['\"]([^'\"]+)['\"][^>]*>").matcher(content);
+ if (m.find()) {
+ post.setFeaturedImage(m.group(1));
+ }
+ }
+
postRepository.save(post);
}
} else if ("page".equalsIgnoreCase(postType)) {
diff --git a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/PageService.java b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/PageService.java
index 92c77028..aa7f647f 100644
--- a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/PageService.java
+++ b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/PageService.java
@@ -158,4 +158,25 @@ public class PageService {
slug = slug.replaceAll("^-|-$", "");
return slug.toLowerCase(Locale.ENGLISH);
}
+
+ /**
+ * Duplicates an existing page.
+ * Creates a new draft copy with "(Copy)" appended to the title.
+ * @param id the ID of the page to duplicate
+ * @return the newly created page
+ */
+ public Page duplicate(Long id) {
+ LOG.debug("Request to duplicate Page : {}", id);
+ Page original = pageRepository.findById(id)
+ .orElseThrow(() -> new IllegalArgumentException("Invalid page Id:" + id));
+
+ Page copy = new Page();
+ copy.setTitle(original.getTitle() + " (Copy)");
+ copy.setContent(original.getContent());
+ copy.setPageType(original.getPageType());
+ copy.setDisplayOrder(original.getDisplayOrder());
+ copy.setStatus(PageStatus.DRAFT);
+
+ return save(copy);
+ }
}
diff --git a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/PostService.java b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/PostService.java
index e2e4cfa7..0d5766bd 100644
--- a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/PostService.java
+++ b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/PostService.java
@@ -171,4 +171,30 @@ public class PostService {
}
return postRepository.existsBySlugAndIdNot(slug, currentPostId);
}
+
+ /**
+ * Duplicates an existing post.
+ * Creates a new draft copy with "(Copy)" appended to the title.
+ * @param id the ID of the post to duplicate
+ * @return the newly created post
+ */
+ public Post duplicate(Long id) {
+ LOG.debug("Request to duplicate Post : {}", id);
+ Post original = postRepository.findById(id)
+ .orElseThrow(() -> new IllegalArgumentException("Invalid post Id:" + id));
+
+ Post copy = new Post();
+ copy.setTitle(original.getTitle() + " (Copy)");
+ copy.setContent(original.getContent());
+ copy.setExcerpt(original.getExcerpt());
+ copy.setFeaturedImage(original.getFeaturedImage());
+ copy.setLayout(original.getLayout());
+ copy.setCategory(original.getCategory());
+ copy.setTags(new HashSet<>(original.getTags()));
+ copy.setMetaDescription(original.getMetaDescription());
+ copy.setEventTime(original.getEventTime());
+ copy.setStatus(PageStatus.DRAFT);
+
+ return save(copy);
+ }
}
diff --git a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/sample.html b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/sample.html
index 18ab3c02..c7f12d38 100644
--- a/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/sample.html
+++ b/sisvietnamvn_main/src/main/java/com/sisvietnamvn/web/service/sample.html
@@ -1,10 +1,31 @@
-
-
-
- [component:news-grid data-source="posts:tag=news,limit=4,collection=news_posts"]
+
+
+
+
-
- [component:events-list data-source="posts:tag=event,limit=4,collection=event_posts"]
+
+
+
+
+
TIN MỚI NHẤT
+
+ [component:news-grid data-source="posts:tag=news,limit=4,collection=news_posts"]
+
+
+
+
+
Sự Kiện
+
+ [component:events-list data-source="posts:tag=event,limit=4,collection=event_posts"]
+
+
+
+
+
+
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/sisvietnamvn_main/src/main/resources/config/liquibase/changelog/20260714080000_seed_news_events_components.xml b/sisvietnamvn_main/src/main/resources/config/liquibase/changelog/20260714080000_seed_news_events_components.xml
new file mode 100644
index 00000000..0618790e
--- /dev/null
+++ b/sisvietnamvn_main/src/main/resources/config/liquibase/changelog/20260714080000_seed_news_events_components.xml
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+{{/each}}]]>
+
+
+
+
+
+
+
+
+
+
+ .campus-events-widget { background: transparent; padding: 0; font-family: inherit; }
+ .campus-events-widget .events-list { list-style: none; margin: 0; padding: 0; }
+ .campus-events-widget .teaser-item { padding: 20px 0; border-bottom: 1px solid #e5e5e5; }
+ .campus-events-widget .teaser-item:last-child { border-bottom: none; }
+ .campus-events-widget .event-wrapper { display: flex; gap: 20px; align-items: flex-start; }
+ .campus-events-widget .event-date { display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 0.125rem; padding: 0.25rem; background: #881C1C; width: 2.875rem; height: 3.125rem; box-sizing: border-box; }
+ .campus-events-widget .event-date__month { font-size: 1rem; font-weight: 800; line-height: 1.25rem; text-transform: uppercase; text-align: center; color: #fff; }
+ .campus-events-widget .event-date__day { font-size: 1rem; font-weight: 800; line-height: 1.25rem; text-transform: uppercase; text-align: center; color: #fff; }
+ .campus-events-widget .event-details { flex: 1; position: relative; }
+ .campus-events-widget .event-title { font-size: 1rem; font-weight: 800; line-height: 1.25rem; text-transform: uppercase; color: #000; }
+ .campus-events-widget .event-title a { color: inherit; text-decoration: none; }
+ .campus-events-widget .event-title a:hover { text-decoration: underline; }
+ .campus-events-widget .event-time { font-size: 16px; color: #000; margin-bottom: 4px; }
+ .campus-events-widget .event-location { font-size: 1rem; font-weight: 400; line-height: 1.5rem; font-style: normal; color: #000; }
+ @media (max-width: 768px) {
+ .campus-events-widget .event-title { font-size: 0.75rem; }
+ .campus-events-widget .event-time { font-size: 0.75rem; }
+ .campus-events-widget .event-location { font-size: 0.75rem; }
+ .campus-events-widget .event-wrapper { display: flex; flex: 1; padding-right: 10px; align-items: stretch; gap: 12px; border: 1px solid #CCC; }
+ .campus-events-widget .event-date { display: flex; padding: 4px 8px; flex-direction: column; justify-content: center; align-items: center; align-self: stretch; background: #881C1C; min-width: 48px; height: auto; }
+ .campus-events-widget .event-details { padding: 8px 10px 8px 0; }
+ .campus-events-widget .teaser-item { flex: 0 0 279px; display: flex; border: 0px solid #CCC; padding-bottom: 0; }
+ .campus-events-widget .events-list { display: flex; gap: 1rem; overflow-x: auto; padding-bottom: 1rem; }
+ }
+ @media (max-width: 480px) {
+ .campus-events-widget .event-wrapper { gap: 10px; }
+ .campus-events-widget .event-date__day { font-size: 18px; }
+ }
+
+]]>
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+{{/each}}]]>
+
slug = 'news-grid'
+
+
+
+
+ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.20.xsd">
+
+
+
+
+ slug IN ('news-grid', 'events-list')
+
+
+
diff --git a/sisvietnamvn_main/src/main/resources/config/liquibase/master.xml b/sisvietnamvn_main/src/main/resources/config/liquibase/master.xml
index bc0a1260..ecbe2296 100644
--- a/sisvietnamvn_main/src/main/resources/config/liquibase/master.xml
+++ b/sisvietnamvn_main/src/main/resources/config/liquibase/master.xml
@@ -36,4 +36,5 @@
+
diff --git a/sisvietnamvn_main/src/main/resources/templates/manage/pages/list.html b/sisvietnamvn_main/src/main/resources/templates/manage/pages/list.html
index 77036163..17efedee 100644
--- a/sisvietnamvn_main/src/main/resources/templates/manage/pages/list.html
+++ b/sisvietnamvn_main/src/main/resources/templates/manage/pages/list.html
@@ -67,16 +67,23 @@
|
|
-
+
Edit
+
|
diff --git a/sisvietnamvn_main/src/main/resources/templates/manage/posts/form.html b/sisvietnamvn_main/src/main/resources/templates/manage/posts/form.html
index 992c4463..884a5f7d 100644
--- a/sisvietnamvn_main/src/main/resources/templates/manage/posts/form.html
+++ b/sisvietnamvn_main/src/main/resources/templates/manage/posts/form.html
@@ -267,6 +267,14 @@
Controls the visual appearance on the frontend.
+
+
+
+
+ Specify the date and time for this event.
+