nuxt-layer-core 2.0.1 → 2.0.2

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.
@@ -61,9 +61,19 @@
61
61
 
62
62
  <!-- Searchable dropdown -->
63
63
  <div class="space-y-1.5" ref="dropdownRef">
64
- <label class="text-xs font-bold uppercase text-slate-500">
65
- Khóa học quan tâm <span class="text-red-500">*</span>
66
- </label>
64
+ <div class="flex items-center justify-between gap-2">
65
+ <label class="text-xs font-bold uppercase text-slate-500">
66
+ Khóa học quan tâm <span class="text-red-500">*</span>
67
+ </label>
68
+ <button v-if="consultationRequest" type="button" @click="clearContext"
69
+ class="flex items-center gap-1 text-[11px] font-medium text-[#004AC6] hover:underline cursor-pointer">
70
+ <Icon name="lucide:x" class="w-3 h-3" />
71
+ Xem tất cả
72
+ </button>
73
+ </div>
74
+ <p v-if="consultationRequest" class="text-[11px] text-slate-400 -mt-1">
75
+ Đã lọc theo mục bạn vừa chọn: {{ categoryLabels[consultationRequest.category] }}
76
+ </p>
67
77
  <div class="relative">
68
78
  <div @click="toggleDropdown"
69
79
  class="w-full px-4 py-3 border rounded-[10px] text-sm text-slate-900 cursor-pointer flex items-center justify-between transition-all"
@@ -145,6 +155,8 @@
145
155
  </template>
146
156
 
147
157
  <script setup>
158
+ import { hciPlans, workshopPlans, trainingPlans } from '../data/pricingPlans'
159
+
148
160
  useScrollAnimate('.cta-card', 'scale-up')
149
161
 
150
162
  const props = defineProps({
@@ -152,6 +164,36 @@ const props = defineProps({
152
164
  selectedPlanId: { type: String, default: null },
153
165
  })
154
166
 
167
+ const consultationRequest = useConsultationRequest()
168
+
169
+ const categoryLabels = {
170
+ hci: 'Proxmox HCI',
171
+ workshop: 'Workshop 1 ngày',
172
+ training: 'Đào tạo Proxmox',
173
+ }
174
+
175
+ // Items relevant to whichever plan the visitor just picked in PricingPlansSection
176
+ const contextPlans = computed(() => {
177
+ const category = consultationRequest.value?.category
178
+ if (category === 'hci') {
179
+ return hciPlans.map((p) => ({ id: p.id, name: p.name, level: 'Gói thuê hạ tầng', duration: p.unit, icon: 'lucide:server-cog' }))
180
+ }
181
+ if (category === 'workshop') {
182
+ return workshopPlans.map((p) => ({ id: p.id, name: p.name, level: 'Workshop', duration: p.tags?.[0] || '', icon: 'lucide:zap' }))
183
+ }
184
+ if (category === 'training') {
185
+ return trainingPlans.map((p) => ({ id: p.id, name: p.name, level: 'Khóa đào tạo', duration: p.tags?.[0] || '', icon: 'lucide:graduation-cap' }))
186
+ }
187
+ return null
188
+ })
189
+
190
+ function clearContext() {
191
+ consultationRequest.value = null
192
+ selectedCourse.value = null
193
+ touched.course = false
194
+ errors.course = ''
195
+ }
196
+
155
197
  const { addOpportunity } = useCrmApi()
156
198
 
157
199
  // Format plans from resumeData (same logic as CoursesSection)
@@ -194,6 +236,8 @@ const formattedPlans = computed(() => {
194
236
  return result.length > 0 ? result : samplePlans
195
237
  })
196
238
 
239
+ const dropdownPlans = computed(() => contextPlans.value || formattedPlans.value)
240
+
197
241
  // Form state
198
242
  const form = reactive({ name: '', phone: '', email: '' })
199
243
  const errors = reactive({ name: '', phone: '', email: '', course: '' })
@@ -219,11 +263,23 @@ watch(() => props.selectedPlanId, (id) => {
219
263
  }
220
264
  }, { immediate: true })
221
265
 
266
+ // Auto-select + activate the item the visitor just picked in PricingPlansSection
267
+ watch(consultationRequest, (request) => {
268
+ if (!request) return
269
+ const found = dropdownPlans.value.find(p => p.id === request.planId)
270
+ || dropdownPlans.value.find(p => p.name === request.planName)
271
+ if (found) {
272
+ selectedCourse.value = found
273
+ touched.course = true
274
+ errors.course = ''
275
+ }
276
+ }, { immediate: true })
277
+
222
278
  // Search filter
223
279
  const filteredCourses = computed(() => {
224
- if (!searchQuery.value) return formattedPlans.value
280
+ if (!searchQuery.value) return dropdownPlans.value
225
281
  const q = searchQuery.value.toLowerCase()
226
- return formattedPlans.value.filter(p =>
282
+ return dropdownPlans.value.filter(p =>
227
283
  p.name.toLowerCase().includes(q) || p.level.toLowerCase().includes(q)
228
284
  )
229
285
  })
@@ -146,8 +146,24 @@
146
146
  </div>
147
147
 
148
148
  <div class="p-7 pt-2 mt-auto">
149
+ <a
150
+ v-if="plan.url"
151
+ :href="plan.url"
152
+ target="_blank"
153
+ rel="noopener noreferrer"
154
+ class="w-full py-3.5 rounded-xl font-bold text-sm transition-all duration-300 cursor-pointer inline-flex items-center justify-center gap-2"
155
+ :class="plan.highlight
156
+ ? 'bg-[#004AC6] text-white shadow-lg shadow-blue-500/25 hover:bg-[#1D4ED8] hover:shadow-blue-500/40'
157
+ : 'border border-slate-200 text-slate-600 hover:border-[#004AC6] hover:text-[#004AC6] hover:bg-blue-50/50'
158
+ "
159
+ >
160
+ Đăng ký ngay
161
+ <Icon name="lucide:arrow-up-right" class="w-4 h-4" />
162
+ </a>
149
163
  <button
164
+ v-else
150
165
  type="button"
166
+ @click="handleContactClick(plan)"
151
167
  class="w-full py-3.5 rounded-xl font-bold text-sm transition-all duration-300 cursor-pointer"
152
168
  :class="plan.highlight
153
169
  ? 'bg-[#004AC6] text-white shadow-lg shadow-blue-500/25 hover:bg-[#1D4ED8] hover:shadow-blue-500/40'
@@ -168,8 +184,22 @@
168
184
  </template>
169
185
 
170
186
  <script setup>
187
+ import { hciPlans, workshopPlans, trainingPlans } from '../data/pricingPlans'
188
+
171
189
  useScrollAnimate('.pricing-header', 'fade-up')
172
190
 
191
+ const { scrollTo } = useScrollToSection()
192
+ const consultationRequest = useConsultationRequest()
193
+
194
+ function handleContactClick(plan) {
195
+ consultationRequest.value = {
196
+ category: activeTab.value,
197
+ planId: plan.id,
198
+ planName: plan.name,
199
+ }
200
+ scrollTo('lien-he')
201
+ }
202
+
173
203
  const sectionRoot = ref(null)
174
204
  const revealed = ref(false)
175
205
 
@@ -223,144 +253,6 @@ const hciInfoCards = [
223
253
  },
224
254
  ]
225
255
 
226
- const hciPlans = [
227
- {
228
- name: 'HCI Starter',
229
- desc: 'Cụm 3 node cho doanh nghiệp vừa và nhỏ',
230
- unit: '/tháng',
231
- priceLabel: '',
232
- prices: { 12: 14900000, 24: 11900000, 36: 9900000 },
233
- tags: ['3 node x86', 'Ceph 3 bản sao', '10 GbE nội cụm', '~20 TB khả dụng'],
234
- features: [
235
- 'Không chi phí đầu tư thiết bị ban đầu',
236
- 'Triển khai, tinh chỉnh và bật High Availability',
237
- 'Bảo hành, thay thế phần cứng trong suốt thời gian thuê',
238
- 'Đào tạo vận hành 1 buổi cho đội IT',
239
- ],
240
- highlight: false,
241
- },
242
- {
243
- name: 'HCI Pro',
244
- desc: 'Cụm mở rộng cho hệ thống production',
245
- unit: '/tháng',
246
- priceLabel: '',
247
- prices: { 12: 27900000, 24: 23900000, 36: 20900000 },
248
- tags: ['5 node NVMe', 'Ceph RBD + CephFS', '25 GbE dự phòng', '~60 TB khả dụng'],
249
- features: [
250
- 'Proxmox Backup Server riêng đi kèm',
251
- 'SDN, firewall phân tầng cho từng VM',
252
- 'Giám sát, cảnh báo toàn cụm 24/7',
253
- 'Nâng cấp cấu hình giữa kỳ không phát sinh phí ký lại',
254
- ],
255
- highlight: true,
256
- },
257
- {
258
- name: 'HCI Datacenter',
259
- desc: 'Đa site, khôi phục thảm họa',
260
- unit: '',
261
- contact: true,
262
- tags: ['Từ 8 node', 'Ceph đa pool', 'Mạng lõi 100 GbE', 'Dung lượng theo yêu cầu'],
263
- features: [
264
- 'Nhân bản chéo hai trung tâm dữ liệu',
265
- 'Kịch bản DR và diễn tập định kỳ',
266
- 'Tự động hóa qua API/Terraform',
267
- 'Kỹ sư trực onsite theo SLA cam kết',
268
- ],
269
- highlight: false,
270
- },
271
- ]
272
-
273
- const workshopPlans = [
274
- {
275
- name: 'Workshop Trực tuyến',
276
- desc: 'Tham gia từ xa · lab riêng trên cloud',
277
- unit: '/học viên',
278
- priceLabel: '',
279
- price: 2900000,
280
- priceNote: '',
281
- tags: ['1 ngày · 8 giờ', 'Trực tuyến', 'Lab cloud riêng'],
282
- features: [
283
- 'Cài đặt Proxmox VE từ số 0',
284
- 'Dựng cụm HCI với Ceph, bật High Availability',
285
- 'Ghi hình buổi học để xem lại 30 ngày',
286
- 'Checklist triển khai dùng ngay',
287
- ],
288
- highlight: false,
289
- },
290
- {
291
- name: 'Workshop Tại lớp',
292
- desc: 'Học trực tiếp cùng giảng viên',
293
- unit: '/học viên',
294
- priceLabel: '',
295
- price: 3900000,
296
- priceNote: '',
297
- tags: ['1 ngày · 8 giờ', 'Tại lớp', 'Thiết bị thật tại lab'],
298
- features: [
299
- 'Thao tác trực tiếp trên cụm nhiều node',
300
- 'Mô phỏng sự cố node và tự phục hồi',
301
- 'Hỏi đáp 1-1 với giảng viên',
302
- 'Suất ăn trưa và tài liệu in kèm',
303
- ],
304
- highlight: true,
305
- },
306
- {
307
- name: 'Workshop Nội bộ doanh nghiệp',
308
- desc: 'Tổ chức riêng cho đội IT của bạn',
309
- unit: '/lớp tối đa 15 người',
310
- priceLabel: 'Từ ',
311
- price: 39000000,
312
- priceNote: '',
313
- tags: ['1-2 ngày', 'Tại doanh nghiệp', 'Nội dung tùy chỉnh'],
314
- features: [
315
- 'Nội dung thiết kế theo hạ tầng hiện có',
316
- 'Giảng viên và thiết bị lab tới tận nơi',
317
- 'Đánh giá năng lực đội IT sau workshop',
318
- 'Ưu đãi khi kết hợp gói thuê HCI',
319
- ],
320
- highlight: false,
321
- },
322
- ]
323
-
324
- const trainingPlans = [
325
- {
326
- name: 'Deployment & Management',
327
- desc: 'Nền tảng · máy chủ đơn',
328
- unit: '/học viên',
329
- priceLabel: 'Từ ',
330
- price: 9900000,
331
- priceNote: '',
332
- tags: ['14 giờ · 2 buổi', 'Trực tuyến hoặc tại lớp', 'Lab thực hành riêng'],
333
- features: ['Cài đặt, mạng, lưu trữ, VM/LXC', 'Backup, phân quyền, tường lửa', 'Chứng nhận hoàn thành Proxmox'],
334
- highlight: false,
335
- },
336
- {
337
- name: 'Clustering & Shared Storage',
338
- desc: 'Chuyên sâu · cụm và Ceph',
339
- unit: '/học viên',
340
- priceLabel: 'Từ ',
341
- price: 11900000,
342
- priceNote: '',
343
- tags: ['14 giờ · 2 buổi', 'Trực tuyến hoặc tại lớp', 'Lab cụm nhiều node'],
344
- features: ['Cluster, pmxcfs, Corosync, SDN', 'Ceph HCI, ZFS replication, HA', 'Kịch bản sự cố thực chiến'],
345
- highlight: true,
346
- },
347
- {
348
- name: 'Proxmox VE Bundle',
349
- desc: 'Trọn gói cả hai khóa · tiết kiệm hơn',
350
- unit: '/học viên',
351
- priceLabel: 'Từ ',
352
- price: 19900000,
353
- priceNote: '',
354
- tags: ['28 giờ · 4-5 buổi', 'Trực tuyến hoặc tại lớp', 'Lộ trình liền mạch'],
355
- features: [
356
- 'Gồm đủ Module 1 và Module 2',
357
- 'Ưu đãi khi đăng ký theo nhóm',
358
- 'Hỗ trợ hỏi đáp sau khóa học',
359
- ],
360
- highlight: false,
361
- },
362
- ]
363
-
364
256
  const tabContent = {
365
257
  hci: {
366
258
  intro: 'Thuê cụm siêu hội tụ theo tháng: không đầu tư ban đầu, đã bao gồm phần cứng, triển khai Ceph, HA và đội ngũ vận hành.',
@@ -0,0 +1,8 @@
1
+ export type ConsultationRequest = {
2
+ category: 'hci' | 'workshop' | 'training'
3
+ planId: string
4
+ planName: string
5
+ } | null
6
+
7
+ export const useConsultationRequest = () =>
8
+ useState<ConsultationRequest>('consultation-request', () => null)
@@ -0,0 +1,150 @@
1
+ export const hciPlans = [
2
+ {
3
+ id: 'hci-2',
4
+ name: 'HCI2 - 3 NODE, 120 CORE, 384GB RAM, 2.88TB NVME',
5
+ desc: 'Cụm 3 node cho doanh nghiệp vừa và nhỏ',
6
+ unit: '/tháng',
7
+ priceLabel: '',
8
+ prices: { 12: 8540000, 24: 8038000, 36: 7535000 },
9
+ tags: ['3 node x86', 'Ceph 3 bản sao', '10 GbE nội cụm', '~20 TB khả dụng'],
10
+ features: [
11
+ 'Không chi phí đầu tư thiết bị ban đầu',
12
+ 'Triển khai, tinh chỉnh và bật High Availability',
13
+ 'Bảo hành, thay thế phần cứng trong suốt thời gian thuê',
14
+ 'Đào tạo vận hành 1 buổi cho đội IT',
15
+ ],
16
+ highlight: false,
17
+ url: 'https://dev.longvan.net/san-pham/hci2',
18
+ },
19
+ {
20
+ id: 'hci-3',
21
+ name: 'HCI3 - 8 NODE, 160 CORE, 1024GB RAM, 7.68TB NVME',
22
+ desc: 'Cụm mở rộng cho hệ thống production',
23
+ unit: '/tháng',
24
+ priceLabel: '',
25
+ prices: { 12: 17847000, 24: 16844000, 36: 15791000 },
26
+ tags: ['5 node NVMe', 'Ceph RBD + CephFS', '25 GbE dự phòng', '~60 TB khả dụng'],
27
+ features: [
28
+ 'Proxmox Backup Server riêng đi kèm',
29
+ 'SDN, firewall phân tầng cho từng VM',
30
+ 'Giám sát, cảnh báo toàn cụm 24/7',
31
+ 'Nâng cấp cấu hình giữa kỳ không phát sinh phí ký lại',
32
+ ],
33
+ highlight: true,
34
+ url: 'https://dev.longvan.net/san-pham/hci3',
35
+ },
36
+ {
37
+ id: 'hci-4',
38
+ name: 'HCI4 - 8 NODE, 320 CORE, 1024GB RAM, 7.68TB NVME',
39
+ desc: 'Cụm mở rộng cho hệ thống production',
40
+ unit: '/tháng',
41
+ priceLabel: '',
42
+ prices: { 12: 22773000, 24: 21433000, 36: 20094000 },
43
+ tags: ['Từ 8 node', 'Ceph đa pool', 'Mạng lõi 100 GbE', 'Dung lượng theo yêu cầu'],
44
+ features: [
45
+ 'Nhân bản chéo hai trung tâm dữ liệu',
46
+ 'Kịch bản DR và diễn tập định kỳ',
47
+ 'Tự động hóa qua API/Terraform',
48
+ 'Kỹ sư trực onsite theo SLA cam kết',
49
+ ],
50
+ highlight: false,
51
+ url: 'https://dev.longvan.net/san-pham/hci4',
52
+ },
53
+ ]
54
+
55
+ export const workshopPlans = [
56
+ {
57
+ id: 'workshop-online',
58
+ name: 'Workshop Trực tuyến',
59
+ desc: 'Tham gia từ xa · lab riêng trên cloud',
60
+ unit: '/học viên',
61
+ priceLabel: '',
62
+ price: 2900000,
63
+ priceNote: '',
64
+ tags: ['1 ngày · 8 giờ', 'Trực tuyến', 'Lab cloud riêng'],
65
+ features: [
66
+ 'Cài đặt Proxmox VE từ số 0',
67
+ 'Dựng cụm HCI với Ceph, bật High Availability',
68
+ 'Ghi hình buổi học để xem lại 30 ngày',
69
+ 'Checklist triển khai dùng ngay',
70
+ ],
71
+ highlight: false,
72
+ },
73
+ {
74
+ id: 'workshop-onsite',
75
+ name: 'Workshop Tại lớp',
76
+ desc: 'Học trực tiếp cùng giảng viên',
77
+ unit: '/học viên',
78
+ priceLabel: '',
79
+ price: 3900000,
80
+ priceNote: '',
81
+ tags: ['1 ngày · 8 giờ', 'Tại lớp', 'Thiết bị thật tại lab'],
82
+ features: [
83
+ 'Thao tác trực tiếp trên cụm nhiều node',
84
+ 'Mô phỏng sự cố node và tự phục hồi',
85
+ 'Hỏi đáp 1-1 với giảng viên',
86
+ 'Suất ăn trưa và tài liệu in kèm',
87
+ ],
88
+ highlight: true,
89
+ },
90
+ {
91
+ id: 'workshop-enterprise',
92
+ name: 'Workshop Nội bộ doanh nghiệp',
93
+ desc: 'Tổ chức riêng cho đội IT của bạn',
94
+ unit: '/lớp tối đa 15 người',
95
+ priceLabel: 'Từ ',
96
+ price: 39000000,
97
+ priceNote: '',
98
+ tags: ['1-2 ngày', 'Tại doanh nghiệp', 'Nội dung tùy chỉnh'],
99
+ features: [
100
+ 'Nội dung thiết kế theo hạ tầng hiện có',
101
+ 'Giảng viên và thiết bị lab tới tận nơi',
102
+ 'Đánh giá năng lực đội IT sau workshop',
103
+ 'Ưu đãi khi kết hợp gói thuê HCI',
104
+ ],
105
+ highlight: false,
106
+ },
107
+ ]
108
+
109
+ export const trainingPlans = [
110
+ {
111
+ id: 'training-deploy',
112
+ name: 'Deployment & Management',
113
+ desc: 'Nền tảng · máy chủ đơn',
114
+ unit: '/học viên',
115
+ priceLabel: 'Từ ',
116
+ price: 9900000,
117
+ priceNote: '',
118
+ tags: ['14 giờ · 2 buổi', 'Trực tuyến hoặc tại lớp', 'Lab thực hành riêng'],
119
+ features: ['Cài đặt, mạng, lưu trữ, VM/LXC', 'Backup, phân quyền, tường lửa', 'Chứng nhận hoàn thành Proxmox'],
120
+ highlight: false,
121
+ },
122
+ {
123
+ id: 'training-cluster',
124
+ name: 'Clustering & Shared Storage',
125
+ desc: 'Chuyên sâu · cụm và Ceph',
126
+ unit: '/học viên',
127
+ priceLabel: 'Từ ',
128
+ price: 11900000,
129
+ priceNote: '',
130
+ tags: ['14 giờ · 2 buổi', 'Trực tuyến hoặc tại lớp', 'Lab cụm nhiều node'],
131
+ features: ['Cluster, pmxcfs, Corosync, SDN', 'Ceph HCI, ZFS replication, HA', 'Kịch bản sự cố thực chiến'],
132
+ highlight: true,
133
+ },
134
+ {
135
+ id: 'training-bundle',
136
+ name: 'Proxmox VE Bundle',
137
+ desc: 'Trọn gói cả hai khóa · tiết kiệm hơn',
138
+ unit: '/học viên',
139
+ priceLabel: 'Từ ',
140
+ price: 19900000,
141
+ priceNote: '',
142
+ tags: ['28 giờ · 4-5 buổi', 'Trực tuyến hoặc tại lớp', 'Lộ trình liền mạch'],
143
+ features: [
144
+ 'Gồm đủ Module 1 và Module 2',
145
+ 'Ưu đãi khi đăng ký theo nhóm',
146
+ 'Hỗ trợ hỏi đáp sau khóa học',
147
+ ],
148
+ highlight: false,
149
+ },
150
+ ]
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "nuxt-layer-core",
3
3
  "type": "module",
4
4
  "private": false,
5
- "version": "2.0.1",
5
+ "version": "2.0.2",
6
6
  "scripts": {
7
7
  "build": "env-cmd -f .env nuxt build",
8
8
  "dev": "env-cmd -f .env nuxt dev"