Skip to content

Commit dea32ad

Browse files
committed
Merge branch 'next' of github.com:devforth/adminforth into next
2 parents 2713c77 + 89d4504 commit dea32ad

File tree

5 files changed

+203
-1
lines changed

5 files changed

+203
-1
lines changed

adminforth/documentation/blog/2025-02-19-compose-ec2-deployment-ci-registry/index.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -803,4 +803,30 @@ sudo growpart /dev/nvme0n1 1
803803
sudo resize2fs /dev/nvme0n1p1
804804
```
805805

806-
This will extend partition to the full disk size. No reboot is needed.
806+
This will extend partition to the full disk size. No reboot is needed.
807+
808+
809+
### Want slack notifications about build?
810+
811+
Create Slack channel and add [Slack app](https://slack.com/apps/A0F7YS25R-incoming-webhooks) to it.
812+
813+
Then create webhook URL and add it to GitHub secrets as `SLACK_WEBHOOK_URL`.
814+
815+
Add this steps to the end of your GitHub actions file:
816+
817+
```yml title=".github/workflows/deploy.yml"
818+
- name: Notify Slack on success
819+
if: success()
820+
run: |
821+
curl -X POST -H 'Content-type: application/json' --data \
822+
"{\"text\": \"✅ *${{ github.actor }}* successfully built *${{ github.ref_name }}* with commit \\\"${{ github.event.head_commit.message }}\\\".\n:link: <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Build> | :link: <${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}|View Commit>\"}" \
823+
${{ secrets.SLACK_WEBHOOK_URL }}
824+
825+
- name: Notify Slack on failure
826+
if: failure()
827+
run: |
828+
curl -X POST -H 'Content-type: application/json' --data \
829+
"{\"text\": \"❌ *${{ github.actor }}* failed to build *${{ github.ref_name }}* with commit \\\"${{ github.event.head_commit.message }}\\\".\n:link: <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Build> | :link: <${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}|View Commit>\"}" \
830+
${{ secrets.SLACK_WEBHOOK_URL }}
831+
832+
```

adminforth/documentation/docs/tutorial/03-Customization/14-afcl.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1612,4 +1612,53 @@ import { PieChart } from '@/afcl'
16121612
</div>
16131613
</div>
16141614

1615+
## Mixed Chart
1616+
1617+
```ts
1618+
import { MixedChart } from '@/afcl'
1619+
```
1620+
1621+
### Basic
1622+
1623+
<div class="split-screen" >
1624+
1625+
<div>
1626+
```html
1627+
<MixedChart
1628+
:data="[
1629+
{ x: '02 Jun 2025', avgPrice: 100, listedPrice: 100, unlistedPrice: 100},
1630+
{ x: '03 Jun 2025', avgPrice: 280, listedPrice: 130, unlistedPrice: 200},
1631+
{ x: '04 Jun 2025', avgPrice: 150, listedPrice: 90, unlistedPrice: 60},
1632+
{ x: '05 Jun 2025', avgPrice: 100, listedPrice: 100, unlistedPrice: 100},
1633+
{ x: '06 Jun 2025', avgPrice: 200, listedPrice: 290, unlistedPrice: 180},
1634+
{ x: '07 Jun 2025', avgPrice: 100, listedPrice: 100, unlistedPrice: 100},
1635+
]"
1636+
:series="[
1637+
{ name: $t('Avg Price'), fieldName: 'avgPrice', type: 'line', color: COLORS[0] },
1638+
{ name: $t('Listed Price'), fieldName: 'listedPrice', type: 'column', color: COLORS[1] },
1639+
{ name: $t('Unlisted Price'), fieldName: 'unlistedPrice', type: 'area', color: COLORS[2] },
1640+
1641+
]"
1642+
:options="{
1643+
chart: {
1644+
height: 350,
1645+
},
1646+
stroke: {
1647+
width: [6, 0, 6], // Line: 6px, Column: 0px (no border), Area: 6px
1648+
},
1649+
fill: {
1650+
opacity: [1, 0.85, 0.25], // Line: 1, Column: 0.85, Area: 0.25
1651+
},
1652+
}"
1653+
/>
1654+
```
1655+
</div>
1656+
<div>
1657+
![Mixed Chart](image-91.png)
1658+
</div>
1659+
</div>
1660+
1661+
1662+
1663+
16151664

47.8 KB
Loading
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<template>
2+
<div class="-mb-2" ref="chart"></div>
3+
</template>
4+
5+
<script setup lang="ts">
6+
import ApexCharts, { type ApexOptions } from 'apexcharts';
7+
import { ref, onUnmounted, watch, computed } from 'vue';
8+
9+
const props = defineProps<{
10+
data: {
11+
x: string,
12+
[key: string]: any,
13+
}[],
14+
series: {
15+
name: string,
16+
fieldName: string,
17+
color?: string,
18+
type: 'column' | 'area' | 'line',
19+
}[],
20+
options?: ApexOptions,
21+
}>();
22+
23+
const chart = ref<HTMLElement | null>(null);
24+
25+
const optionsBase = {
26+
chart: {
27+
height: 250,
28+
type: 'line',
29+
stacked: false,
30+
fontFamily: "Inter, sans-serif",
31+
toolbar: {
32+
show: false,
33+
}
34+
},
35+
stroke: {
36+
curve: 'smooth',
37+
},
38+
grid: {
39+
show: false,
40+
strokeDashArray: 4,
41+
padding: {
42+
left: 3,
43+
right: 3,
44+
top: 3,
45+
bottom: 3
46+
},
47+
},
48+
xaxis: {
49+
categories: [],
50+
labels: {
51+
show: false,
52+
},
53+
axisBorder: {
54+
show: false,
55+
},
56+
axisTicks: {
57+
show: false,
58+
},
59+
},
60+
yaxis: {
61+
show: false,
62+
},
63+
};
64+
65+
const chartOptions = computed(() => {
66+
if (props.data?.length > 0) {
67+
props.series.forEach((s) => {
68+
if (props.data[0][s.fieldName] === undefined) {
69+
throw new Error(`Field ${s.fieldName} not found even in first data point ${JSON.stringify(props.data[0])}, something is wrong`);
70+
}
71+
});
72+
}
73+
74+
const options = {
75+
...optionsBase,
76+
series: props.series.map((s) => ({
77+
name: s.name,
78+
type: s.type,
79+
color: s.color,
80+
data: props.data?.map((item) => item[s.fieldName]) ?? [],
81+
})),
82+
labels: props.data?.map((item) => item.x) ?? [],
83+
};
84+
85+
function mergeOptions(options: any, newOptions: any) {
86+
if (!newOptions) {
87+
return;
88+
}
89+
for (const key in newOptions) {
90+
if (typeof newOptions[key] === 'object' && !Array.isArray(newOptions[key])) {
91+
if (!options[key]) {
92+
options[key] = {};
93+
}
94+
mergeOptions(options[key], newOptions[key]);
95+
} else {
96+
options[key] = newOptions[key];
97+
}
98+
}
99+
}
100+
mergeOptions(options, props.options);
101+
102+
return options;
103+
});
104+
105+
let apexChart: ApexCharts | null = null;
106+
107+
108+
watch(() => [chartOptions.value, chart.value], ([newOptions, newRef]) => {
109+
if (!newOptions || !newRef) {
110+
return;
111+
}
112+
113+
if (apexChart) {
114+
apexChart.updateOptions(newOptions);
115+
} else if (chart.value) {
116+
apexChart = new ApexCharts(chart.value, newOptions);
117+
apexChart.render();
118+
}
119+
}, { deep: true });
120+
121+
onUnmounted(() => {
122+
if (apexChart) {
123+
apexChart.destroy();
124+
}
125+
});
126+
</script>

adminforth/spa/src/afcl/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ export { default as ProgressBar } from './ProgressBar.vue';
1717
export { default as Spinner } from './Spinner.vue';
1818
export { default as Skeleton } from './Skeleton.vue';
1919
export { default as Dialog } from './Dialog.vue';
20+
export { default as MixedChart } from './MixedChart.vue';
2021

2122

0 commit comments

Comments
 (0)