Commit 554c2752 authored by Phạm Quang Bảo's avatar Phạm Quang Bảo

update: so-do-to-chuc

parent b5c7d30e
...@@ -104,6 +104,9 @@ importers: ...@@ -104,6 +104,9 @@ importers:
cmdk: cmdk:
specifier: ^1.1.1 specifier: ^1.1.1
version: 1.1.1(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) version: 1.1.1(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
d3-org-chart:
specifier: ^3.1.1
version: 3.1.1
date-fns: date-fns:
specifier: ^4.1.0 specifier: ^4.1.0
version: 4.1.0 version: 4.1.0
...@@ -2142,6 +2145,69 @@ packages: ...@@ -2142,6 +2145,69 @@ packages:
csstype@3.1.3: csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
d3-array@3.2.4:
resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
engines: {node: '>=12'}
d3-color@3.1.0:
resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
engines: {node: '>=12'}
d3-dispatch@3.0.1:
resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
engines: {node: '>=12'}
d3-drag@3.0.0:
resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
engines: {node: '>=12'}
d3-ease@3.0.1:
resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
engines: {node: '>=12'}
d3-flextree@2.1.2:
resolution: {integrity: sha512-gJiHrx5uTTHq44bjyIb3xpbmmdZcWLYPKeO9EPVOq8EylMFOiH2+9sWqKAiQ4DcFuOZTAxPOQyv0Rnmji/g15A==}
d3-hierarchy@1.1.9:
resolution: {integrity: sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==}
d3-hierarchy@3.1.2:
resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==}
engines: {node: '>=12'}
d3-interpolate@3.0.1:
resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
engines: {node: '>=12'}
d3-org-chart@3.1.1:
resolution: {integrity: sha512-kDLk3cRqEw5x4KohwZlvTTiPMdY/vo+ROKktyOt9s2Eu0bX/cNlXP9UvM6wXLy9uHmx9D945klSqGlDRXzfK2Q==}
d3-path@3.1.0:
resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
engines: {node: '>=12'}
d3-selection@3.0.0:
resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
engines: {node: '>=12'}
d3-shape@3.2.0:
resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
engines: {node: '>=12'}
d3-timer@3.0.1:
resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
engines: {node: '>=12'}
d3-transition@3.0.1:
resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
engines: {node: '>=12'}
peerDependencies:
d3-selection: 2 - 3
d3-zoom@3.0.0:
resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
engines: {node: '>=12'}
damerau-levenshtein@1.0.8: damerau-levenshtein@1.0.8:
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
...@@ -2698,6 +2764,10 @@ packages: ...@@ -2698,6 +2764,10 @@ packages:
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
internmap@2.0.3:
resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
engines: {node: '>=12'}
is-array-buffer@3.0.5: is-array-buffer@3.0.5:
resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
...@@ -5942,6 +6012,69 @@ snapshots: ...@@ -5942,6 +6012,69 @@ snapshots:
csstype@3.1.3: {} csstype@3.1.3: {}
d3-array@3.2.4:
dependencies:
internmap: 2.0.3
d3-color@3.1.0: {}
d3-dispatch@3.0.1: {}
d3-drag@3.0.0:
dependencies:
d3-dispatch: 3.0.1
d3-selection: 3.0.0
d3-ease@3.0.1: {}
d3-flextree@2.1.2:
dependencies:
d3-hierarchy: 1.1.9
d3-hierarchy@1.1.9: {}
d3-hierarchy@3.1.2: {}
d3-interpolate@3.0.1:
dependencies:
d3-color: 3.1.0
d3-org-chart@3.1.1:
dependencies:
d3-array: 3.2.4
d3-flextree: 2.1.2
d3-hierarchy: 3.1.2
d3-selection: 3.0.0
d3-shape: 3.2.0
d3-zoom: 3.0.0
d3-path@3.1.0: {}
d3-selection@3.0.0: {}
d3-shape@3.2.0:
dependencies:
d3-path: 3.1.0
d3-timer@3.0.1: {}
d3-transition@3.0.1(d3-selection@3.0.0):
dependencies:
d3-color: 3.1.0
d3-dispatch: 3.0.1
d3-ease: 3.0.1
d3-interpolate: 3.0.1
d3-selection: 3.0.0
d3-timer: 3.0.1
d3-zoom@3.0.0:
dependencies:
d3-dispatch: 3.0.1
d3-drag: 3.0.0
d3-interpolate: 3.0.1
d3-selection: 3.0.0
d3-transition: 3.0.1(d3-selection@3.0.0)
damerau-levenshtein@1.0.8: {} damerau-levenshtein@1.0.8: {}
data-view-buffer@1.0.2: data-view-buffer@1.0.2:
...@@ -6666,6 +6799,8 @@ snapshots: ...@@ -6666,6 +6799,8 @@ snapshots:
hasown: 2.0.2 hasown: 2.0.2
side-channel: 1.1.0 side-channel: 1.1.0
internmap@2.0.3: {}
is-array-buffer@3.0.5: is-array-buffer@3.0.5:
dependencies: dependencies:
call-bind: 1.0.8 call-bind: 1.0.8
......
...@@ -21,6 +21,7 @@ import Fees from "./static-pages/Fees"; ...@@ -21,6 +21,7 @@ import Fees from "./static-pages/Fees";
import Locations from "./static-pages/Locations"; import Locations from "./static-pages/Locations";
import Contact from "./static-pages/Contact"; import Contact from "./static-pages/Contact";
import MemberDirectory from "./static-pages/MemberDirectory"; import MemberDirectory from "./static-pages/MemberDirectory";
import SoDoToChuc from "./static-pages/SoDoToChuc";
import { useGetApiV10Post } from "@/api/vcci-news/endpoints/post"; import { useGetApiV10Post } from "@/api/vcci-news/endpoints/post";
import { useGetApiV10Category } from "@/api/vcci-news/endpoints/category"; import { useGetApiV10Category } from "@/api/vcci-news/endpoints/category";
import type { DynamicCategoryRouteItem } from "./templates/types"; import type { DynamicCategoryRouteItem } from "./templates/types";
...@@ -63,7 +64,7 @@ export default function DynamicPageClient() { ...@@ -63,7 +64,7 @@ export default function DynamicPageClient() {
"quy-trinh-tiep-nhan-ho-so-cap-gcn-va-xac-nhan-cttm", "quy-trinh-tiep-nhan-ho-so-cap-gcn-va-xac-nhan-cttm",
"bieu-mau-gcn-va-noi-dung-khai-bao-gcn-cttm", "phi-cap-gcn-va-xac-nhan-cttm", "bieu-mau-gcn-va-noi-dung-khai-bao-gcn-cttm", "phi-cap-gcn-va-xac-nhan-cttm",
"diem-cap-va-thoi-gian-cap-gcn-va-xac-nhan-cttm", "thong-tin-lien-he", "diem-cap-va-thoi-gian-cap-gcn-va-xac-nhan-cttm", "thong-tin-lien-he",
"danh-ba-hoi-vien", "danh-ba-hoi-vien", "so-do-to-chuc",
].includes(endingSlug); ].includes(endingSlug);
const { data: categoryData, isLoading: categoryLoading } = useGetApiV10Category({ const { data: categoryData, isLoading: categoryLoading } = useGetApiV10Category({
...@@ -286,6 +287,13 @@ export default function DynamicPageClient() { ...@@ -286,6 +287,13 @@ export default function DynamicPageClient() {
allCategories={allCategories} allCategories={allCategories}
/> />
); );
case "so-do-to-chuc":
return (
<SoDoToChuc
category={matchedCategory}
allCategories={allCategories}
/>
);
} }
if (detailPost) { if (detailPost) {
......
...@@ -18,7 +18,7 @@ const STATIC_PAGE_SLUGS = new Set([ ...@@ -18,7 +18,7 @@ const STATIC_PAGE_SLUGS = new Set([
"quy-trinh-tiep-nhan-ho-so-cap-gcn-va-xac-nhan-cttm", "quy-trinh-tiep-nhan-ho-so-cap-gcn-va-xac-nhan-cttm",
"bieu-mau-gcn-va-noi-dung-khai-bao-gcn-cttm", "phi-cap-gcn-va-xac-nhan-cttm", "bieu-mau-gcn-va-noi-dung-khai-bao-gcn-cttm", "phi-cap-gcn-va-xac-nhan-cttm",
"diem-cap-va-thoi-gian-cap-gcn-va-xac-nhan-cttm", "thong-tin-lien-he", "diem-cap-va-thoi-gian-cap-gcn-va-xac-nhan-cttm", "thong-tin-lien-he",
"danh-ba-hoi-vien", "danh-ba-hoi-vien", "so-do-to-chuc",
]); ]);
const SITE_NAME = "VCCI HCM"; const SITE_NAME = "VCCI HCM";
...@@ -100,6 +100,11 @@ const STATIC_PAGE_METADATA: Record<string, { title: string; description: string ...@@ -100,6 +100,11 @@ const STATIC_PAGE_METADATA: Record<string, { title: string; description: string
description: description:
"Danh bạ hội viên VCCI-HCM: tra cứu thông tin doanh nghiệp hội viên và kết nối đối tác kinh doanh.", "Danh bạ hội viên VCCI-HCM: tra cứu thông tin doanh nghiệp hội viên và kết nối đối tác kinh doanh.",
}, },
"so-do-to-chuc": {
title: "Sơ đồ tổ chức",
description:
"Sơ đồ tổ chức VCCI-HCM: cơ cấu Ban Giám đốc và các phòng chuyên môn trực thuộc của Chi nhánh khu vực TP.HCM.",
},
}; };
/** /**
......
"use client";
import { useEffect, useRef } from "react";
import ListCategory from "@/components/base/list-category";
import { buildDynamicCategoryMenu } from "../templates/data";
import type { DynamicCategoryRouteItem } from "../templates/types";
import { OrgChart } from "d3-org-chart";
type OrgNode = {
id: string;
parentId: string | null;
name: string;
title: string;
note?: string;
};
const ORG_DIRECTOR: OrgNode = {
id: "director",
parentId: null,
name: "Trần Ngọc Liêm",
title: "Giám đốc",
};
const ORG_DEPUTIES: OrgNode[] = [
{ id: "deputy-nam", parentId: ORG_DIRECTOR.id, name: "Nguyễn Hữu Nam", title: "Phó Giám đốc" },
{ id: "deputy-ninh", parentId: ORG_DIRECTOR.id, name: "Bùi Thị Ninh", title: "Phó Giám đốc" },
{ id: "deputy-hue", parentId: ORG_DIRECTOR.id, name: "Nguyễn Thị Huệ", title: "Phó Giám đốc" },
{ id: "deputy-truong", parentId: ORG_DIRECTOR.id, name: "Nguyễn Hữu Trường", title: "Phó Giám đốc" },
{ id: "deputy-huyen", parentId: ORG_DIRECTOR.id, name: "Vũ Thị Thanh Huyền", title: "Phó Giám đốc" },
{ id: "deputy-thoai", parentId: ORG_DIRECTOR.id, name: "Nguyễn Xuân Bích Thoại", title: "Phó Giám đốc" },
];
const ORG_DEPARTMENTS: OrgNode[] = [
{ id: "dept-hoi-vien", parentId: ORG_DIRECTOR.id, name: "Phòng Hội viên Đào tạo và Truyền thông", title: "Trưởng phòng: Nguyễn Đoàn Thông\nPhó trưởng phòng: Bùi Mạnh Hùng" },
{ id: "dept-hanh-chinh", parentId: ORG_DIRECTOR.id, name: "Phòng Hành chính Quản trị", title: "Trưởng phòng: Nguyễn Văn Quân" },
{ id: "dept-tai-chinh-ke-toan", parentId: ORG_DIRECTOR.id, name: "Phòng Tài chính Kế toán", title: "Phó trưởng phòng phụ trách: Bùi Tường Vi\nPhó trưởng phòng: Nguyễn Hữu Nghĩa" },
{ id: "dept-to-chuc-can-bo", parentId: ORG_DIRECTOR.id, name: "Phòng Tổ chức Cán bộ", title: "Trưởng phòng: Lê Thanh Thúy\nPhó trưởng phòng: Trương Thanh Tần" },
{ id: "dept-phap-che-va-chung-tu", parentId: "deputy-nam", name: "Phòng Pháp chế và Xác nhận chứng từ thương mại", title: "Trưởng phòng: Vũ Xuân Hưng\nPhó trưởng phòng: Nguyễn Văn Đức\nPhó trưởng phòng: Nguyễn Khánh Linh" },
{ id: "dept-trung-tam-sme", parentId: "deputy-nam", name: "Trung tâm Hỗ trợ Doanh nghiệp Nhỏ và Vừa", title: "Phó Giám đốc phụ trách: Nguyễn Hữu Nam" },
{ id: "dept-giao-dich", parentId: "deputy-ninh", name: "Phòng Giao dịch Quốc tế", title: "Trưởng phòng: Trần Ngọc Đào\nPhó trưởng phòng: Hồ Ngọc Vinh" },
{ id: "dept-van-phong-gioi", parentId: "deputy-ninh", name: "Văn phòng Giới sử dụng lao động và Doanh nhân nữ", title: "Phó trưởng phòng: Nguyễn Lê Nhật Thanh" },
{ id: "dept-hanh-chinh-co-so-2", parentId: "deputy-hue", name: "Phòng Hành chính Quản trị - Cơ sở 2", title: "Trưởng phòng: Nguyễn Huy Hoàng\nPhó trưởng phòng: Vũ Thị Hiền" },
{ id: "dept-giao-dich-co-so-2", parentId: "deputy-truong", name: "Phòng Giao dịch Quốc tế - Cơ sở 2", title: "Trưởng phòng: Nguyễn Thị Như Quỳnh" },
{ id: "dept-hoi-vien-co-so-2", parentId: "deputy-huyen", name: "Phòng Hội viên Đào tạo và Truyền thông - Cơ sở 2", title: "Trưởng phòng: Đỗ Quang Huy\nPhó trưởng phòng: Nguyễn Đức Hường" },
{ id: "dept-phap-che-chung-tu-co-so-2", parentId: "deputy-thoai", name: "Phòng Pháp chế và Xác nhận chứng từ thương mại - Cơ sở 2", title: "Trưởng phòng: Ma Thị Hương" },
];
type SoDoToChucProps = {
category: DynamicCategoryRouteItem | null;
allCategories: DynamicCategoryRouteItem[];
};
export default function SoDoToChuc({ category, allCategories }: SoDoToChucProps) {
const chartContainerRef = useRef<HTMLDivElement>(null);
const categoryMenu = category ? buildDynamicCategoryMenu(category, allCategories) : [];
useEffect(() => {
const container = chartContainerRef.current;
if (!container) return;
const initiallyCollapsedDepartmentIds = new Set(ORG_DEPARTMENTS.map((department) => department.id));
const expandedDepartmentDetails = new Set<string>();
const previousDepartmentByManager = new Map<string, string>();
const chartData = [ORG_DIRECTOR, ...ORG_DEPUTIES, ...ORG_DEPARTMENTS].map((node) => {
const managerId = node.parentId;
if (!initiallyCollapsedDepartmentIds.has(node.id) || !managerId) return node;
if (managerId !== ORG_DIRECTOR.id && !managerId.startsWith("deputy-")) return node;
const previousDepartmentId = previousDepartmentByManager.get(managerId);
previousDepartmentByManager.set(managerId, node.id);
return previousDepartmentId ? { ...node, parentId: previousDepartmentId, _expanded: true } as OrgNode : node;
});
const chart = new OrgChart<OrgNode>()
.container(container)
.data(chartData)
.initialExpandLevel(2)
.nodeId((node) => node.id)
.parentNodeId((node) => node.parentId)
.nodeWidth(() => 260)
.nodeHeight(({ data }) => {
const isDepartment = initiallyCollapsedDepartmentIds.has(data.id);
const showDetails = !isDepartment || expandedDepartmentDetails.has(data.id);
if (isDepartment && !showDetails) return 82;
const titleLineCount = data.title.split("\n").reduce((count, line) => count + (line.includes(":") ? 2 : 1), 0);
return Math.max(118, 82 + titleLineCount * 23 + (data.note ? 20 : 0));
})
.childrenMargin(() => 36)
.siblingsMargin((node) => initiallyCollapsedDepartmentIds.has((node as { data?: OrgNode }).data?.id ?? "") ? 0 : 30)
.compact(false)
.duration(350)
.scaleExtent([0.35, 2.5])
.onNodeClick((node) => {
const nodeId = node.data.id;
if (!initiallyCollapsedDepartmentIds.has(nodeId)) return;
if (expandedDepartmentDetails.has(nodeId)) {
expandedDepartmentDetails.delete(nodeId);
} else {
expandedDepartmentDetails.add(nodeId);
}
chart.render();
})
.nodeContent(({ data }) => {
const featured = data.id === ORG_DIRECTOR.id;
const escapeHtml = (value: string) =>
value.replace(/[&<>"']/g, (character) => {
const entities: Record<string, string> = {
"&": "&",
"<": "<",
">": ">",
'"': "&#34;",
"'": "'",
};
return entities[character] ?? character;
});
const safeName = escapeHtml(data.name);
const safeTitle = escapeHtml(data.title);
const safeNote = data.note ? escapeHtml(data.note) : undefined;
const isDepartment = initiallyCollapsedDepartmentIds.has(data.id);
const showDetails = !isDepartment || expandedDepartmentDetails.has(data.id);
const titleLines = showDetails ? safeTitle.split("\n").map((line) => {
const separatorIndex = line.indexOf(":");
if (separatorIndex === -1) return `<div>${line}</div>`;
const role = line.slice(0, separatorIndex);
const person = line.slice(separatorIndex + 1).trim();
return `<div style="margin-top:4px"><span style="font-weight:600">${role}</span><br/><span>${person}</span></div>`;
}).join("") : "";
return `<div style="box-sizing:border-box;width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:${showDetails ? "18px" : "12px"};border:1px solid ${featured ? "#174a83" : "#d7e0e9"};background:${featured ? "#174a83" : "#fff"};color:${featured ? "#fff" : "#20364b"};text-align:center;box-shadow:0 5px 16px rgba(23,52,83,.08);font-family:Arial,sans-serif;cursor:${isDepartment ? "pointer" : "default"}">
<div style="font-size:${featured ? "19px" : "16px"};font-weight:700;line-height:1.4">${safeName}</div>
${showDetails && safeTitle ? `<div style="margin-top:8px;font-size:14px;line-height:1.55;color:${featured ? "#dbeafe" : "#65778a"}">${titleLines}</div>` : ""}
${showDetails && safeNote ? `<div style="margin-top:8px;color:#f4c45f;font-size:10px;font-weight:700;letter-spacing:1px">${safeNote}</div>` : ""}
</div>`;
})
.render()
.fit({ animate: false, scale: false });
return () => {
container.replaceChildren();
chart.clear();
};
}, []);
return (
<div className="min-h-screen bg-white">
{categoryMenu.length ? <ListCategory categories={categoryMenu} /> : null}
<div className="container mx-auto px-4 py-6 sm:px-6 lg:px-10 lg:pb-10">
<section>
<div className="overflow-auto rounded-sm border border-[#e1e7ed] bg-[#f7f9fb] p-2 sm:p-4">
<div ref={chartContainerRef} className="min-h-[680px] w-full" aria-label="Sơ đồ tổ chức VCCI-HCM" />
</div>
</section>
</div>
</div>
);
}
...@@ -296,6 +296,12 @@ function Header() { ...@@ -296,6 +296,12 @@ function Header() {
> >
Liên hệ Liên hệ
</Link> </Link>
<Link
className="px-3 py-1 text-[13px] font-medium text-white transition hover:opacity-80"
href="/so-do-to-chuc"
>
Sơ đồ tổ chức
</Link>
</div> </div>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
......
declare module "d3-org-chart" {
export class OrgChart<T> {
container(container: HTMLElement): this;
data(data: T[]): this;
nodeId(accessor: (node: T) => string): this;
parentNodeId(accessor: (node: T) => string | null): this;
nodeWidth(accessor: (node: { depth: number; data: T }) => number): this;
nodeHeight(accessor: (node: { depth: number; data: T }) => number): this;
childrenMargin(accessor: (node: unknown) => number): this;
siblingsMargin(accessor: (node: unknown) => number): this;
compact(compact: boolean): this;
duration(duration: number): this;
nodeContent(content: (node: { data: T }) => string): this;
onNodeClick(callback: (node: { data: T }) => void): this;
setExpanded(id: string, expanded?: boolean): this;
scaleExtent(extent: [number, number]): this;
initialExpandLevel(level: number): this;
render(): this;
fit(options?: { animate?: boolean; scale?: boolean }): this;
clear(): this;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment