nvis-fe-cms-libs 1.1.3 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +240 -12
- package/dist/nvis-fe-cms-libs.es.js +19 -15
- package/dist/nvis-fe-cms-libs.es.js.map +1 -1
- package/dist/nvis-fe-cms-libs.umd.js +19 -15
- package/dist/nvis-fe-cms-libs.umd.js.map +1 -1
- package/package.json +4 -8
- package/dist/nvis-fe-cms-libs.css +0 -501
package/README.md
CHANGED
|
@@ -1,12 +1,240 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
# 📦 nvis-fe-cms-lib
|
|
2
|
+
|
|
3
|
+
Thư viện các component React được đóng gói để sử dụng trong dự án CMS Landing Page.
|
|
4
|
+
Thay vì viết lại code cho từng section, bạn có thể import trực tiếp các component từ thư viện này.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 🚀 Bước 1: Khởi Tạo Dự Án Thư Viện
|
|
9
|
+
|
|
10
|
+
Sử dụng Vite để tạo một dự án mới:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm create vite@latest nvis-fe-cms-lib -- --template react
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Cài đặt dependencies:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
cd nvis-fe-cms-lib
|
|
20
|
+
npm install
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## ⚙️ Bước 2: Cấu Hình Dự Án cho Thư Viện
|
|
26
|
+
|
|
27
|
+
### 1. Sửa vite.config.js
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
import { defineConfig } from 'vite';
|
|
31
|
+
import react from '@vitejs/plugin-react';
|
|
32
|
+
import path from 'path';
|
|
33
|
+
|
|
34
|
+
export default defineConfig({
|
|
35
|
+
plugins: [react()],
|
|
36
|
+
build: {
|
|
37
|
+
lib: {
|
|
38
|
+
entry: path.resolve(__dirname, 'src/index.js'),
|
|
39
|
+
name: 'AsaCmsSections',
|
|
40
|
+
fileName: (format) => `nvis-fe-cms-libs.${format}.js`,
|
|
41
|
+
formats: ['es', 'umd']
|
|
42
|
+
},
|
|
43
|
+
rollupOptions: {
|
|
44
|
+
external: ['react', 'react-dom'],
|
|
45
|
+
output: {
|
|
46
|
+
globals: {
|
|
47
|
+
'react': 'React',
|
|
48
|
+
'react-dom': 'ReactDOM'
|
|
49
|
+
},
|
|
50
|
+
// Đảm bảo export đúng format
|
|
51
|
+
exports: 'named'
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
// Tối ưu cho library
|
|
55
|
+
target: 'es2015',
|
|
56
|
+
sourcemap: true,
|
|
57
|
+
// Tắt minification để debug dễ hơn (có thể bật lại sau)
|
|
58
|
+
minify: false
|
|
59
|
+
},
|
|
60
|
+
// Cấu hình cho development
|
|
61
|
+
resolve: {
|
|
62
|
+
alias: {
|
|
63
|
+
'@': path.resolve(__dirname, 'src')
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2. Sửa package.json
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"name": "nvis-fe-cms-libs",
|
|
74
|
+
"version": "1.0.0",
|
|
75
|
+
"type": "module",
|
|
76
|
+
"main": "dist/nvis-fe-cms-libs.umd.js",
|
|
77
|
+
"module": "dist/nvis-fe-cms-libs.es.js",
|
|
78
|
+
"types": "dist/index.d.ts",
|
|
79
|
+
"files": [
|
|
80
|
+
"dist",
|
|
81
|
+
"README.md"
|
|
82
|
+
],
|
|
83
|
+
"scripts": {
|
|
84
|
+
"dev": "vite",
|
|
85
|
+
"build": "vite build",
|
|
86
|
+
"lint": "eslint .",
|
|
87
|
+
"preview": "vite preview",
|
|
88
|
+
"prepublishOnly": "npm run build"
|
|
89
|
+
},
|
|
90
|
+
"exports": {
|
|
91
|
+
".": {
|
|
92
|
+
"import": "./dist/nvis-fe-cms-libs.es.js",
|
|
93
|
+
"require": "./dist/nvis-fe-cms-libs.umd.js",
|
|
94
|
+
"types": "./dist/index.d.ts"
|
|
95
|
+
},
|
|
96
|
+
"./dist/style.css": "./dist/style.css"
|
|
97
|
+
},
|
|
98
|
+
"keywords": [
|
|
99
|
+
"react",
|
|
100
|
+
"cms",
|
|
101
|
+
"components",
|
|
102
|
+
"ui",
|
|
103
|
+
"tailwindcss"
|
|
104
|
+
],
|
|
105
|
+
"author": "NVIS FE Team",
|
|
106
|
+
"license": "MIT",
|
|
107
|
+
"repository": {
|
|
108
|
+
"type": "git",
|
|
109
|
+
"url": "https://github.com/NVISToolkit/nvis-fe-cms-lib.git"
|
|
110
|
+
},
|
|
111
|
+
"dependencies": {
|
|
112
|
+
"i18next": "^25.5.2",
|
|
113
|
+
"i18next-browser-languagedetector": "^8.2.0",
|
|
114
|
+
"react-i18next": "^15.7.3"
|
|
115
|
+
},
|
|
116
|
+
"devDependencies": {
|
|
117
|
+
"@eslint/js": "^9.33.0",
|
|
118
|
+
"@types/react": "^19.1.10",
|
|
119
|
+
"@types/react-dom": "^19.1.7",
|
|
120
|
+
"@vitejs/plugin-react": "^5.0.0",
|
|
121
|
+
"autoprefixer": "^10.4.21",
|
|
122
|
+
"eslint": "^9.33.0",
|
|
123
|
+
"eslint-plugin-react-hooks": "^5.2.0",
|
|
124
|
+
"eslint-plugin-react-refresh": "^0.4.20",
|
|
125
|
+
"globals": "^16.3.0",
|
|
126
|
+
"postcss": "^8.5.6",
|
|
127
|
+
"tailwindcss": "^3.4.17",
|
|
128
|
+
"vite": "^7.1.2"
|
|
129
|
+
},
|
|
130
|
+
"peerDependencies": {
|
|
131
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
132
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
- `exports` giúp các công cụ build tìm đúng file.
|
|
137
|
+
- `peerDependencies` đảm bảo dự án dùng thư viện có sẵn React, tránh xung đột.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 🧩 Bước 3: Code và Cấu Trúc Component
|
|
142
|
+
|
|
143
|
+
### Ví dụ Component (AboutCompanySection.jsx)
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
// src/components/AboutCompanySection.jsx
|
|
147
|
+
import React from "react";
|
|
148
|
+
import CompanyValues from "./CompanyValues";
|
|
149
|
+
|
|
150
|
+
const AboutCompanySection = ({ data, t, isDarkMode }) => {
|
|
151
|
+
// Kiểm tra t function
|
|
152
|
+
const safeT = typeof t === 'function' ? t : (key, options) => {
|
|
153
|
+
console.warn(`Translation function not provided for key: ${key}`);
|
|
154
|
+
return key;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const companyData = data?.sectionDataBindingItems?.[0]?.data;
|
|
158
|
+
|
|
159
|
+
if (!companyData) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const name = companyData.name || safeT('aboutCompany.defaults.companyName');
|
|
164
|
+
const shortName = companyData.shortName || safeT('aboutCompany.defaults.shortName');
|
|
165
|
+
const shortDescription = companyData.shortDescription || safeT('aboutCompany.defaults.shortDescription');
|
|
166
|
+
const establishedYear = companyData.establishedYear || "----";
|
|
167
|
+
const experienceYears = companyData.experienceYears || "0";
|
|
168
|
+
const description = companyData.description || "";
|
|
169
|
+
....
|
|
170
|
+
|
|
171
|
+
export default AboutCompanySection;
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### File Đầu Vào (src/index.js)
|
|
175
|
+
|
|
176
|
+
```javascript
|
|
177
|
+
// src/index.js
|
|
178
|
+
import AboutCompanySection from './sections/aboutCompany/AboutCompanySection';
|
|
179
|
+
import CompanyValues from './sections/aboutCompany/CompanyValues';
|
|
180
|
+
|
|
181
|
+
// Export các components
|
|
182
|
+
export { AboutCompanySection, CompanyValues };
|
|
183
|
+
|
|
184
|
+
// Default export (tuỳ chọn)
|
|
185
|
+
export default {
|
|
186
|
+
AboutCompanySection,
|
|
187
|
+
CompanyValues
|
|
188
|
+
};
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## 📦 Bước 4: Đóng Gói và Publish lên npm
|
|
194
|
+
|
|
195
|
+
1. Tăng phiên bản trong `package.json` (ví dụ 1.0.2 → 1.0.3).
|
|
196
|
+
2. Chạy build để tạo thư mục dist:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
npm run build
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
3. Đăng nhập và publish:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
npm login
|
|
206
|
+
npm publish
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## 🌐 Bước 5: Sử Dụng Thư Viện trong Dự Án CMS
|
|
212
|
+
|
|
213
|
+
Cài đặt:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
npm install nvis-fe-cms-lib@1.0.3
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Sử dụng trong `SectionRenderer.jsx`:
|
|
220
|
+
|
|
221
|
+
```javascript
|
|
222
|
+
import React from 'react';
|
|
223
|
+
import { AboutCompanySection } from 'nvis-fe-cms-libs';
|
|
224
|
+
import { useTheme } from './contexts/ThemeContext';
|
|
225
|
+
import { useTranslation } from 'react-i18next';
|
|
226
|
+
|
|
227
|
+
const SectionRenderer = ({ section }) => {
|
|
228
|
+
const { isDarkMode } = useTheme();
|
|
229
|
+
const { t } = useTranslation();
|
|
230
|
+
|
|
231
|
+
return <AboutCompanySection data={section} t={t} isDarkMode={isDarkMode} />;
|
|
232
|
+
};
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## 🎉 Kết Luận
|
|
238
|
+
|
|
239
|
+
Thư viện **nvis-fe-cms-libs** giúp tách rời các section thành module độc lập, dễ tái sử dụng và bảo trì.
|
|
240
|
+
Bạn có thể mở rộng bằng cách thêm component mới và publish phiên bản tiếp theo lên npm.
|
|
@@ -513,7 +513,7 @@ const AboutCompanySection = ({ data, t, isDarkMode }) => {
|
|
|
513
513
|
console.warn(`Translation function not provided for key: ${key}`);
|
|
514
514
|
return key;
|
|
515
515
|
};
|
|
516
|
-
const companyData = (_b = (_a = data == null ? void 0 : data.sectionDataBindingItems) == null ? void 0 : _a
|
|
516
|
+
const companyData = ((_b = (_a = data == null ? void 0 : data.sectionDataBindingItems) == null ? void 0 : _a.filter((item) => item == null ? void 0 : item.data)) == null ? void 0 : _b.map((item) => item.data)) || [];
|
|
517
517
|
if (!companyData) {
|
|
518
518
|
return null;
|
|
519
519
|
}
|
|
@@ -668,7 +668,7 @@ const AboutCompanySection = ({ data, t, isDarkMode }) => {
|
|
|
668
668
|
};
|
|
669
669
|
const TimelineSection = ({ data, t, isDarkMode }) => {
|
|
670
670
|
var _a;
|
|
671
|
-
const timelineData = ((_a = data == null ? void 0 : data.sectionDataBindingItems) == null ? void 0 : _a.map((item) => item.data)) || [];
|
|
671
|
+
const timelineData = ((_a = data == null ? void 0 : data.sectionDataBindingItems) == null ? void 0 : _a.filter((item) => item == null ? void 0 : item.data).map((item) => item.data)) || [];
|
|
672
672
|
const [timelineVisible, setTimelineVisible] = useState([]);
|
|
673
673
|
const timelineRefs = useRef([]);
|
|
674
674
|
useEffect(() => {
|
|
@@ -967,8 +967,8 @@ function useWindowSize$1() {
|
|
|
967
967
|
return width;
|
|
968
968
|
}
|
|
969
969
|
const PartnersSection = ({ data, t, isDarkMode, imageBaseUrl = "" }) => {
|
|
970
|
-
var _a;
|
|
971
|
-
const partnersData = ((_a = data == null ? void 0 : data.sectionDataBindingItems) == null ? void 0 : _a.map((item) => item.data)) || [];
|
|
970
|
+
var _a, _b;
|
|
971
|
+
const partnersData = ((_b = (_a = data == null ? void 0 : data.sectionDataBindingItems) == null ? void 0 : _a.filter((item) => item == null ? void 0 : item.data)) == null ? void 0 : _b.map((item) => item.data)) || [];
|
|
972
972
|
const [currentPartnerSlide, setCurrentPartnerSlide] = useState(0);
|
|
973
973
|
const [partnersVisible, setPartnersVisible] = useState(false);
|
|
974
974
|
const partnersRef = useRef(null);
|
|
@@ -1087,7 +1087,7 @@ const MilestoneSection = ({ data, t, isDarkMode, getMilestoneTimeline }) => {
|
|
|
1087
1087
|
const timelineRefs = useRef([]);
|
|
1088
1088
|
useEffect(() => {
|
|
1089
1089
|
const fetchMilestones = () => __async(null, null, function* () {
|
|
1090
|
-
var _a;
|
|
1090
|
+
var _a, _b;
|
|
1091
1091
|
if (getMilestoneTimeline) {
|
|
1092
1092
|
try {
|
|
1093
1093
|
setLoading(true);
|
|
@@ -1103,7 +1103,7 @@ const MilestoneSection = ({ data, t, isDarkMode, getMilestoneTimeline }) => {
|
|
|
1103
1103
|
setLoading(false);
|
|
1104
1104
|
}
|
|
1105
1105
|
} else {
|
|
1106
|
-
const milestonesData = ((_a = data == null ? void 0 : data.sectionDataBindingItems) == null ? void 0 : _a.map((item) => item.data)) || [];
|
|
1106
|
+
const milestonesData = ((_b = (_a = data == null ? void 0 : data.sectionDataBindingItems) == null ? void 0 : _a.filter((item) => item == null ? void 0 : item.data)) == null ? void 0 : _b.map((item) => item.data)) || [];
|
|
1107
1107
|
setMilestones(milestonesData);
|
|
1108
1108
|
setLoading(false);
|
|
1109
1109
|
}
|
|
@@ -1244,12 +1244,15 @@ const MilestoneSection = ({ data, t, isDarkMode, getMilestoneTimeline }) => {
|
|
|
1244
1244
|
const TestimonialsSection = ({ data, t, isDarkMode }) => {
|
|
1245
1245
|
var _a;
|
|
1246
1246
|
const [currentIndex, setCurrentIndex] = useState(0);
|
|
1247
|
-
const testimonials = ((_a = data == null ? void 0 : data.sectionDataBindingItems) == null ? void 0 : _a.map((item) =>
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1247
|
+
const testimonials = ((_a = data == null ? void 0 : data.sectionDataBindingItems) == null ? void 0 : _a.filter((item) => item == null ? void 0 : item.data).map((item) => {
|
|
1248
|
+
var _a2, _b, _c, _d;
|
|
1249
|
+
return {
|
|
1250
|
+
quote: ((_a2 = item.data) == null ? void 0 : _a2.content) || "",
|
|
1251
|
+
author: ((_b = item.data) == null ? void 0 : _b.customerName) || "",
|
|
1252
|
+
position: ((_c = item.data) == null ? void 0 : _c.customerTitle) || t("testimonials.fallback.defaultPosition"),
|
|
1253
|
+
company: ((_d = item.data) == null ? void 0 : _d.customerCompany) || ""
|
|
1254
|
+
};
|
|
1255
|
+
})) || [];
|
|
1253
1256
|
const fallbackTestimonials = [
|
|
1254
1257
|
{
|
|
1255
1258
|
quote: t("testimonials.fallback.testimonial1.quote"),
|
|
@@ -2313,7 +2316,8 @@ const ContactFormSection = ({ data, t, isDarkMode, consultationRequestService })
|
|
|
2313
2316
|
] }) }) });
|
|
2314
2317
|
};
|
|
2315
2318
|
const ContactInfoSection = ({ data, t, isDarkMode }) => {
|
|
2316
|
-
|
|
2319
|
+
var _a;
|
|
2320
|
+
const contactData = ((_a = data == null ? void 0 : data.sectionDataBindingItems) == null ? void 0 : _a.filter((item) => item)) || [];
|
|
2317
2321
|
if (!contactData.length) return null;
|
|
2318
2322
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("section", { className: `py-16 ${isDarkMode ? "bg-gray-900" : "bg-slate-50"}`, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: [
|
|
2319
2323
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center mb-12", children: [
|
|
@@ -2328,14 +2332,14 @@ const ContactInfoSection = ({ data, t, isDarkMode }) => {
|
|
|
2328
2332
|
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: `text-lg max-w-3xl mx-auto ${isDarkMode ? "text-gray-300" : "text-slate-600"}`, children: t("contact.team.description") || "Chúng tôi luôn sẵn sàng hỗ trợ bạn với đội ngũ chuyên nghiệp và giàu kinh nghiệm" })
|
|
2329
2333
|
] }),
|
|
2330
2334
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid md:grid-cols-2 lg:grid-cols-3 gap-8", children: contactData.map((contact, index2) => {
|
|
2331
|
-
var
|
|
2335
|
+
var _a2;
|
|
2332
2336
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2333
2337
|
"div",
|
|
2334
2338
|
{
|
|
2335
2339
|
className: `rounded-2xl p-8 shadow-lg transition-all duration-300 hover:shadow-xl hover:-translate-y-1 border ${isDarkMode ? "bg-gray-800 border-gray-700" : "bg-white border-slate-200"}`,
|
|
2336
2340
|
children: [
|
|
2337
2341
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center mb-6", children: [
|
|
2338
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-20 h-20 rounded-full mx-auto mb-4 flex items-center justify-center text-2xl font-bold text-white bg-gradient-to-br from-indigo-500 to-purple-600", children: ((
|
|
2342
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-20 h-20 rounded-full mx-auto mb-4 flex items-center justify-center text-2xl font-bold text-white bg-gradient-to-br from-indigo-500 to-purple-600", children: ((_a2 = contact.data.name) == null ? void 0 : _a2.charAt(0)) || "A" }),
|
|
2339
2343
|
/* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: `text-xl font-bold mb-2 ${isDarkMode ? "text-white" : "text-slate-800"}`, children: contact.data.name }),
|
|
2340
2344
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `inline-block px-3 py-1 rounded-full text-sm font-semibold ${isDarkMode ? "bg-indigo-900/50 text-indigo-300" : "bg-indigo-50 text-indigo-700"}`, children: contact.data.position })
|
|
2341
2345
|
] }),
|