nuxt-layer-core 1.1.2 → 1.1.4

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.
@@ -1,67 +1,67 @@
1
- <template>
2
- <section id="giai-phap" class="py-24 bg-white">
3
- <div class="max-w-7xl mx-auto px-6 lg:px-8">
4
- <div class="text-center mb-16">
5
- <h2 class="text-[#004AC6] font-bold uppercase tracking-widest text-sm mb-2">Lợi ích khóa học</h2>
6
- <h3 class="font-['Montserrat'] font-extrabold text-2xl sm:text-3xl text-slate-900 mb-4">
7
- Vì sao nên tham gia đào tạo Proxmox VE?
8
- </h3>
9
- <p class="text-slate-500 max-w-2xl mx-auto">
10
- Đầu tư một lần vào đào tạo bài bản không chỉ giúp bạn tối ưu năng lực vận hành, cắt giảm chi phí hạ tầng mà còn tạo bước đệm bứt phá trên con đường sự nghiệp.
11
- </p>
12
- </div>
13
-
14
- <div class="grid grid-cols-1 md:grid-cols-3 gap-8">
15
- <div
16
- v-for="(item, i) in features"
17
- :key="item.title"
18
- :data-delay="i * 100"
19
- class="feature-card bg-white p-8 rounded-xl border border-slate-200 hover:shadow-xl hover:border-blue-200 transition-all"
20
- >
21
- <div class="w-11 h-11 rounded-[10px] bg-blue-50 flex items-center justify-center mb-6 text-[#004AC6]">
22
- <Icon :name="item.icon" />
23
- </div>
24
- <h4 class="font-bold text-lg text-slate-900 mb-3">{{ item.title }}</h4>
25
- <p class="text-sm text-slate-500 leading-relaxed">{{ item.desc }}</p>
26
- </div>
27
- </div>
28
- </div>
29
- </section>
30
- </template>
31
-
32
- <script setup>
33
- useScrollAnimate('.feature-card', 'scale-up')
34
-
35
- const features = [
36
- {
37
- icon: 'lucide:server',
38
- title: 'Làm chủ toàn diện Proxmox VE',
39
- desc: 'Nắm vững trọn vẹn các khái niệm cốt lõi, từ triển khai ảo hóa cơ bản đến quản trị các tính năng cao cấp của nền tảng.'
40
- },
41
- {
42
- icon: 'lucide:award',
43
- title: 'Học từ chuyên gia thực chiến',
44
- desc: 'Tiếp thu kinh nghiệm vận hành ảo hóa trực tiếp từ đội ngũ giảng viên được chứng nhận, đã xử lý hàng ngàn bài toán hệ thống.'
45
- },
46
- {
47
- icon: 'lucide:monitor-play',
48
- title: 'Thực hành Lab môi trường thật',
49
- desc: 'Không chỉ học lý thuyết, bạn sẽ trực tiếp thao tác và giải quyết các tình huống giả lập sát với thực tế doanh nghiệp.'
50
- },
51
- {
52
- icon: 'lucide:activity',
53
- title: 'Troubleshooting thần tốc',
54
- desc: 'Trang bị kỹ năng nhận diện "bệnh" hệ thống nhạy bén, giúp bạn xử lý sự cố chính xác và dứt điểm hơn bao giờ hết.'
55
- },
56
- {
57
- icon: 'lucide:trending-up',
58
- title: 'Thăng tiến & Khẳng định vị thế',
59
- desc: 'Nâng tầm giá trị bản thân, tạo dựng sự tín nhiệm tuyệt đối với cấp trên, đồng nghiệp và đối tác nhờ năng lực chuyên môn vượt trội.'
60
- },
61
- {
62
- icon: 'lucide:file-badge',
63
- title: 'Chứng nhận chính quy',
64
- desc: 'Nhận chứng chỉ hoàn thành khóa học danh giá, minh chứng rõ nét cho năng lực thực sự và sự đầu tư nghiêm túc của bạn.'
65
- }
66
- ]
1
+ <template>
2
+ <section id="giai-phap" class="py-24 bg-white">
3
+ <div class="max-w-7xl mx-auto px-6 lg:px-8">
4
+ <div class="text-center mb-16">
5
+ <h2 class="text-[#004AC6] font-bold uppercase tracking-widest text-sm mb-2">Lợi ích khóa học</h2>
6
+ <h3 class="font-['Montserrat'] font-extrabold text-2xl sm:text-3xl text-slate-900 mb-4">
7
+ Vì sao nên tham gia đào tạo Proxmox VE?
8
+ </h3>
9
+ <p class="text-slate-500 max-w-2xl mx-auto">
10
+ Đầu tư một lần vào đào tạo bài bản không chỉ giúp bạn tối ưu năng lực vận hành, cắt giảm chi phí hạ tầng mà còn tạo bước đệm bứt phá trên con đường sự nghiệp.
11
+ </p>
12
+ </div>
13
+
14
+ <div class="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
15
+ <div
16
+ v-for="(item, i) in features"
17
+ :key="item.title"
18
+ :data-delay="i * 100"
19
+ class="feature-card bg-white p-8 rounded-xl border border-slate-200 hover:shadow-xl hover:border-blue-200 transition-all"
20
+ >
21
+ <div class="w-11 h-11 rounded-[10px] bg-blue-50 flex items-center justify-center mb-6 text-[#004AC6]">
22
+ <Icon :name="item.icon" />
23
+ </div>
24
+ <h4 class="font-bold text-lg text-slate-900 mb-3">{{ item.title }}</h4>
25
+ <p class="text-sm text-slate-500 leading-relaxed">{{ item.desc }}</p>
26
+ </div>
27
+ </div>
28
+ </div>
29
+ </section>
30
+ </template>
31
+
32
+ <script setup>
33
+ useScrollAnimate('.feature-card', 'scale-up')
34
+
35
+ const features = [
36
+ {
37
+ icon: 'lucide:server',
38
+ title: 'Làm chủ toàn diện Proxmox VE',
39
+ desc: 'Nắm vững trọn vẹn các khái niệm cốt lõi, từ triển khai ảo hóa cơ bản đến quản trị các tính năng cao cấp của nền tảng.'
40
+ },
41
+ {
42
+ icon: 'lucide:award',
43
+ title: 'Học từ chuyên gia thực chiến',
44
+ desc: 'Tiếp thu kinh nghiệm vận hành ảo hóa trực tiếp từ đội ngũ giảng viên được chứng nhận, đã xử lý hàng ngàn bài toán hệ thống.'
45
+ },
46
+ {
47
+ icon: 'lucide:monitor-play',
48
+ title: 'Thực hành Lab môi trường thật',
49
+ desc: 'Không chỉ học lý thuyết, bạn sẽ trực tiếp thao tác và giải quyết các tình huống giả lập sát với thực tế doanh nghiệp.'
50
+ },
51
+ {
52
+ icon: 'lucide:activity',
53
+ title: 'Troubleshooting thần tốc',
54
+ desc: 'Trang bị kỹ năng nhận diện "bệnh" hệ thống nhạy bén, giúp bạn xử lý sự cố chính xác và dứt điểm hơn bao giờ hết.'
55
+ },
56
+ {
57
+ icon: 'lucide:trending-up',
58
+ title: 'Thăng tiến & Khẳng định vị thế',
59
+ desc: 'Nâng tầm giá trị bản thân, tạo dựng sự tín nhiệm tuyệt đối với cấp trên, đồng nghiệp và đối tác nhờ năng lực chuyên môn vượt trội.'
60
+ },
61
+ {
62
+ icon: 'lucide:file-badge',
63
+ title: 'Chứng nhận chính quy',
64
+ desc: 'Nhận chứng chỉ hoàn thành khóa học danh giá, minh chứng rõ nét cho năng lực thực sự và sự đầu tư nghiêm túc của bạn.'
65
+ }
66
+ ]
67
67
  </script>
@@ -51,7 +51,7 @@
51
51
  DevOps phù hợp nhất — thay vì để bạn tự đoán.
52
52
  </p>
53
53
 
54
- <div class="flex flex-wrap gap-4">
54
+ <div class="flex flex-wrap justify-center gap-4 lg:justify-start">
55
55
  <NuxtLink to="#lien-he" @click.prevent="scrollTo('lien-he')"
56
56
  class="bg-white text-[#004AC6] px-7 py-3.5 rounded-[10px] font-bold flex items-center gap-2 hover:bg-blue-50 transition-colors shadow-lg active:scale-95">
57
57
  Đặt lịch tư vấn miễn phí
@@ -65,8 +65,8 @@
65
65
  </div>
66
66
  </div>
67
67
 
68
- <!-- Right -->
69
- <div class="relative flex justify-center">
68
+ <!-- Right: ẩn trên mobile/tablet để tiết kiệm chiều cao, chỉ hiện khi có đủ chỗ 2 cột -->
69
+ <div class="relative hidden justify-center lg:flex">
70
70
  <div class="relative z-10 rounded-2xl overflow-hidden">
71
71
  <!-- <NuxtImg src="/images/image_from_https_hercules_cdn.com_file_6dztkvkfnip0ud23tzzvu57c.png"
72
72
  alt="Long Vân Proxmox Infrastructure" class="w-full h-full object-contain p-4" /> -->
@@ -15,7 +15,7 @@
15
15
  </div>
16
16
 
17
17
  <!-- Version cards -->
18
- <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-16">
18
+ <div class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 mb-16">
19
19
  <button
20
20
  v-for="version in resolvedVersions"
21
21
  :key="version.id"
@@ -79,6 +79,13 @@
79
79
 
80
80
  <Transition name="fade-swap" mode="out-in">
81
81
  <div :key="activeVersionId" class="flex flex-col gap-14">
82
+ <div
83
+ v-if="activeVersion.groups.length === 0"
84
+ class="text-center text-slate-400 text-sm py-14 border border-dashed border-slate-200 rounded-2xl"
85
+ >
86
+ Chưa có gói dịch vụ nào được cấu hình cho phiên bản này.
87
+ </div>
88
+
82
89
  <div
83
90
  v-for="(group, gi) in activeVersion.groups"
84
91
  :key="group.id"
@@ -95,15 +102,18 @@
95
102
  </div>
96
103
  </div>
97
104
 
98
- <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
105
+ <div class="grid gap-6"
106
+ :class="group.packages.length === 1 ? 'grid-cols-1 justify-items-center' : 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3'">
99
107
  <div
100
108
  v-for="pkg in group.packages"
101
109
  :key="pkg.id"
102
110
  class="relative bg-white rounded-2xl flex flex-col overflow-hidden transition-all duration-300 ease-out"
103
- :class="pkg.highlight
104
- ? 'ring-1 ring-[#004AC6]/15 shadow-[0_8px_30px_-6px_rgba(0,74,198,0.25)] hover:shadow-[0_20px_45px_-10px_rgba(0,74,198,0.35)] hover:-translate-y-1'
105
- : 'ring-1 ring-slate-200 shadow-[0_2px_10px_-2px_rgba(15,23,42,0.06)] hover:ring-blue-200 hover:shadow-[0_12px_30px_-8px_rgba(15,23,42,0.12)] hover:-translate-y-1'
106
- "
111
+ :class="[
112
+ group.packages.length === 1 ? 'w-full max-w-sm' : '',
113
+ pkg.highlight
114
+ ? 'ring-1 ring-[#004AC6]/15 shadow-[0_8px_30px_-6px_rgba(0,74,198,0.25)] hover:shadow-[0_20px_45px_-10px_rgba(0,74,198,0.35)] hover:-translate-y-1'
115
+ : 'ring-1 ring-slate-200 shadow-[0_2px_10px_-2px_rgba(15,23,42,0.06)] hover:ring-blue-200 hover:shadow-[0_12px_30px_-8px_rgba(15,23,42,0.12)] hover:-translate-y-1'
116
+ ]"
107
117
  >
108
118
  <div
109
119
  v-if="pkg.highlight"
@@ -121,7 +131,7 @@
121
131
 
122
132
  <div class="pb-1">
123
133
  <span class="font-['Montserrat'] font-extrabold text-xl text-slate-900">
124
- {{ formatPrice(pkg.price) }}đ
134
+ {{ pkg.price }}
125
135
  </span>
126
136
  <span class="text-slate-400 text-sm">{{ pkg.unit }}</span>
127
137
  </div>
@@ -158,7 +168,7 @@
158
168
  : 'border border-slate-200 text-slate-600 hover:border-[#004AC6] hover:text-[#004AC6] hover:bg-blue-50/50'
159
169
  "
160
170
  >
161
- Đăng ký ngay
171
+ {{ pkg.titleButton || 'Đăng ký ngay' }}
162
172
  </button>
163
173
  </div>
164
174
  </div>
@@ -207,7 +217,12 @@ const normalizeGroup = (raw) => {
207
217
  name: raw.name,
208
218
  icon: raw.icon || 'lucide:server-cog',
209
219
  desc: raw.desc || '',
210
- packages: Array.isArray(raw.packages) ? raw.packages : [],
220
+ // Chỉ 1 gói -> tự động highlight (không gói khác để so sánh nên luôn là gói nổi bật)
221
+ packages: Array.isArray(raw.packages)
222
+ ? raw.packages.length === 1
223
+ ? [{ ...raw.packages[0], highlight: true }]
224
+ : raw.packages
225
+ : [],
211
226
  }
212
227
  }
213
228
 
@@ -283,10 +298,6 @@ function handleSelectPackage(pkg, group) {
283
298
  })
284
299
  }
285
300
 
286
- function formatPrice(value) {
287
- if (!value && value !== 0) return ''
288
- return new Intl.NumberFormat('vi-VN').format(value)
289
- }
290
301
 
291
302
  const sectionRoot = ref(null)
292
303
  const revealed = ref(false)