1<#assign entries = entries?reverse />
2<#assign lastEntry = entries[entries?size - 1] />
3<#assign
4 assetRenderer = lastEntry.getAssetRenderer()
5 ddmFormFieldValuesMap = assetRenderer.getDDMFormValuesReader().getDDMFormValues().getDDMFormFieldValuesMap()
6 TyGiaSo = ""
7/>
8<#list ddmFormFieldValuesMap["TyGiaSo"] as field>
9 <#assign TyGiaSo = field.getValue().getString(locale) />
10</#list>
11<#assign secondLastEntry = entries[entries?size - 2] />
12
13<#-- Giá trị mới nhất -->
14<#assign
15 lastRenderer = lastEntry.getAssetRenderer()
16 lastFields = lastRenderer.getDDMFormValuesReader().getDDMFormValues().getDDMFormFieldValuesMap()
17 lastValue = 0
18/>
19<#list lastFields["TyGiaSo"] as field>
20 <#assign lastValue = field.getValue().getString(locale)?number />
21</#list>
22
23<#-- Giá trị trước đó -->
24<#assign
25 secondRenderer = secondLastEntry.getAssetRenderer()
26 secondFields = secondRenderer.getDDMFormValuesReader().getDDMFormValues().getDDMFormFieldValuesMap()
27 prevValue = 0
28/>
29<#list secondFields["TyGiaSo"] as field>
30 <#assign prevValue = field.getValue().getString(locale)?number />
31</#list>
32 <#assign delta = lastValue - prevValue />
33<#assign percent = (delta / prevValue) * 100 />
34
35<#if delta gte 0>
36 <#assign sign = "+" />
37<#else>
38 <#assign sign = "" />
39</#if>
40
41<div class="sbv-chart custom-chart">
42 <div class="chart-header">
43 <a
44 href="https://www.dttktt.sbv.gov.vn/TyGia/faces/TyGiaTrungTam.jspx"
45 class="chart-title"
46 >
47 tỷ giá
48 </a>
49 <span class="chart-value">
50 ${lastValue?string["#,##0.00"]} VND
51 <span class="chart-change">
52 ${sign}${delta?string["#,##0.00"]} (${sign}${percent?string["0.00"]}%)
53 </span>
54 </span>
55 </div>
56 <div class="chart-canvas-wrapper">
57 <!-- Bar chart would be rendered here -->
58 <canvas id="TyGiaChart" width="400" height="300"></canvas>
59 </div>
60 <div class="chart-footer">
61 <span class="chart-footer-text">
62 <a
63 href="https://www.dttktt.sbv.gov.vn/TyGia/faces/TyGiaTrungTam.jspx"
64 class="chart-link"
65 >
66 Xem chi tiết <i class="fa-solid fa-caret-right"></i>
67 </a>
68 </span>
69 </div>
70</div>
71
72 <style>
73 .sbv-chart .text-end a{color: #9D1B43;}
74 .sbv-chart .text-end a:hover{color: var(--btn-link-hover-color);}
75
76 /* Container Responsive Widths */
77.custom-chart {
78 /* Mặc định full */
79 width: 100%;
80 padding-left: 0;
81 padding-right: 0;
82}
83
84@media (max-width: 1170px) {
85 .custom-chart {
86 width: calc(50% - 12px);
87 }
88}
89
90@media (max-width: 768px) {
91 .custom-chart {
92 width: calc(50% - 4px);
93 padding: 0.5rem;
94 }
95}
96
97@media (max-width: 650px) {
98 .custom-chart {
99 width: 100%;
100 padding-left: 0;
101 padding-right: 0;
102 }
103}
104
105/* Header Section */
106.chart-header {
107 display: flex;
108 flex-direction: column;
109 gap: 1px;
110 margin-bottom: 0.75rem; /* mb-3 */
111}
112
113.chart-title {
114 color: #000000;
115 font-size: 20px;
116 line-height: 1.15;
117 font-weight: bold;
118 text-transform: uppercase;
119 text-decoration: none;
120}
121
122.chart-value {
123 color: #009953;
124 font-size: 18px;
125 line-height: 21px;
126}
127
128.chart-change {
129 display: inline-block;
130 font-size: 14px;
131 line-height: 1rem; /* leading-4 */
132 color: #000000;
133}
134
135/* Chart Canvas Box */
136.chart-canvas-wrapper {
137 width: 370px;
138 height: 270px;
139 background-color: white;
140 padding: 0.25rem;
141}
142
143@media (max-width: 1170px) {
144 .chart-canvas-wrapper {
145 width: 100%;
146 height: 370px;
147 }
148}
149
150/* Footer Link */
151.chart-footer {
152 text-align: end;
153 margin-top: 0.5rem;
154}
155
156.chart-footer-text {
157 color: #9D1B43;
158 border-radius: 9999px;
159 display: flex;
160 justify-content: flex-start;
161 align-items: center;
162 gap: 6px;
163}
164
165.chart-link {
166 color: #9D1B43;
167 display: inline-block;
168 font-size: 14px;
169 line-height: 1.5;
170 text-decoration: none;
171}
172
173</style>
174<script>
175const showFirstAndLastLabelPlugin = {
176 id: 'showFirstAndLastLabel',
177 afterBuildTicks: function(chart) {
178 const xAxis = chart.scales.x;
179 if (!xAxis) return;
180
181 const ticks = xAxis.ticks;
182 const labels = xAxis.getLabels();
183
184 if (ticks.length === 0 || labels.length === 0) return;
185
186 // Xác định index đang hiển thị trong viewport hiện tại
187 const minIndex = Math.ceil(xAxis.min); // chỉ số nhỏ nhất hiển thị
188 const maxIndex = Math.floor(xAxis.max); // chỉ số lớn nhất hiển thị
189
190 // Nếu mốc đầu đang nằm trong viewport, ép lại label
191 if (ticks[0] && minIndex === 0) {
192 ticks[0].label = labels[0];
193 }
194
195 // Nếu mốc cuối đang nằm trong viewport, ép lại label
196 if (ticks[ticks.length - 1] && maxIndex === labels.length - 1) {
197 ticks[ticks.length - 1].label = labels[labels.length - 1];
198 }
199 }
200};
201
202document.addEventListener("DOMContentLoaded", function() {
203var dates = [
204 <#list entries as curEntry>
205 <#assign
206 assetRenderer = curEntry.getAssetRenderer()
207 ddmFormFieldValuesMap = assetRenderer.getDDMFormValuesReader().getDDMFormValues().getDDMFormFieldValuesMap()
208 NgayBatDau = ""
209 />
210<#assign zoomStart = (entries?size > 10)?then(entries?size - 10, 0) />
211<#assign zoomEnd = entries?size - 1 />
212 <#list ddmFormFieldValuesMap["NgayBatDau"] as field>
213 <#assign NgayBatDau = field.getValue().getString(locale)?date("yyyy-MM-dd") />
214 </#list>
215 "${NgayBatDau?string("dd-MM-yyyy")}"<#if curEntry_has_next>,</#if>
216 </#list>
217];
218
219 var tyGiaValues = [
220 <#list entries as curEntry>
221 <#assign
222 assetRenderer = curEntry.getAssetRenderer()
223 ddmFormFieldValuesMap = assetRenderer.getDDMFormValuesReader().getDDMFormValues().getDDMFormFieldValuesMap()
224 TyGiaSo = ""
225 />
226 <#list ddmFormFieldValuesMap["TyGiaSo"] as field>
227 <#assign TyGiaSo = field.getValue().getString(locale) />
228 </#list>
229 ${TyGiaSo?number}<#if curEntry_has_next>,</#if>
230 </#list>
231 ];
232 const ctx = document.getElementById('TyGiaChart').getContext('2d');
233new Chart(ctx, {
234 type: 'line',
235 data: {
236 labels: dates,
237 datasets: [{
238 label: 'Tỷ giá (VND)',
239 data: tyGiaValues,
240 borderColor: '#A37B40',
241 backgroundColor: '#A37B40',
242 tension: 0.3,
243 fill: false,
244 pointBackgroundColor: '#A37B40'
245 }]
246 },
247 options: {
248 scales: {
249 y: {
250 ticks: {
251 color: '#000000'
252 },
253 title: {
254 display: false,
255 text: 'Tỷ giá',
256 font: { weight: 'bold' },
257 color: "#000000",
258 align: 'end'
259 },
260 beginAtZero: false
261 },
262 x: {
263 ticks: {
264 color: '#000000'
265 },
266 title: {
267 display: false,
268 text: 'Năm',
269 font: { weight: 'bold' },
270 align: 'end',
271 color: "#000000",
272 min: ${zoomStart},
273 max: ${zoomEnd},
274 }
275 }
276 },
277 plugins: {
278 legend: { display: false },
279 zoom: {
280 zoom: {
281 wheel: { enabled: true }, // Zoom bằng cuộn chuột
282 pinch: { enabled: true }, // Zoom bằng cảm ứng (mobile)
283 mode: 'x', // hoặc 'y' hoặc 'xy'
284 onZoomComplete: ({ chart }) => chart.update('none'),
285 // 👇 Zoom theo con trỏ chuột
286 center: {
287 x: 'mouse',
288 y: 'mouse'
289 }
290 },
291 pan: {
292 enabled: true,
293 mode: 'x' // cho phép kéo trục x
294 },
295 limits: {
296 x: { min: 'original', max: 'original' },
297 y: { min: 'original', max: 'original' }
298 }
299 }
300 }
301 },
302 plugins: [ChartZoom, showFirstAndLastLabelPlugin]
303});
304
305});
306</script>