Skip to content

Commit 68e2445

Browse files
committed
feat(ec-buyers-profile-chart): setup new buyers profile graphs card
1 parent 783be84 commit 68e2445

File tree

3 files changed

+216
-0
lines changed

3 files changed

+216
-0
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<template>
2+
<div
3+
class="ec-buyers-profile-chart mx-auto"
4+
style="max-width: 500px"
5+
>
6+
<div class="mb-5">
7+
<canvas
8+
ref="canva-radar"
9+
width="400"
10+
height="300"
11+
></canvas>
12+
</div>
13+
14+
<div>
15+
<canvas
16+
ref="canva-doughnut"
17+
width="400"
18+
height="200"
19+
></canvas>
20+
</div>
21+
</div>
22+
</template>
23+
24+
<script src="./js/EcBuyersProfileChart.js"></script>
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import {
2+
// i19aboveOf
3+
i19Gender,
4+
i19upTo
5+
// i19years
6+
} from '@ecomplus/i18n'
7+
8+
import { i18n } from '@ecomplus/utils'
9+
import ecomAuth from '@ecomplus/auth'
10+
import Chart from 'chart.js'
11+
12+
export default {
13+
name: 'EcBuyersProfileChart',
14+
15+
props: {
16+
ecomAuth: {
17+
type: Object,
18+
default () {
19+
return ecomAuth
20+
}
21+
}
22+
},
23+
24+
computed: {
25+
i19aboveOf: () => 'Acima de',
26+
i19upTo: () => i18n(i19upTo),
27+
i19years: () => 'anos'
28+
},
29+
30+
methods: {
31+
i19Gender: prop => i18n(i19Gender)[prop],
32+
33+
setupCharts (aggregation) {
34+
const year = new Date().getFullYear()
35+
const ageRanges = [{
36+
label: `${this.i19upTo} 25`,
37+
minYear: year - 25
38+
}, {
39+
label: '26 - 40',
40+
minYear: year - 40
41+
}, {
42+
label: '41 - 60',
43+
minYear: year - 60
44+
}, {
45+
label: `${this.i19aboveOf} 60`,
46+
minYear: null
47+
}]
48+
const genders = ['f', 'm', 'x']
49+
const rgbByGender = {
50+
f: '223, 242, 0',
51+
m: '3, 169, 179',
52+
x: '255, 86, 0'
53+
}
54+
const ordersByGender = {
55+
f: 0,
56+
m: 0,
57+
x: 0
58+
}
59+
const aggByAge = ageRanges.map(({ label, minYear }) => {
60+
return {
61+
label: `${label} ${this.i19years}`,
62+
minYear,
63+
orders: 0
64+
}
65+
})
66+
const aggAgeGender = ageRanges.reduce((aggAgeGender, { minYear }) => {
67+
genders.forEach(gender => {
68+
aggAgeGender.push({
69+
minYear,
70+
gender,
71+
orders: 0
72+
})
73+
})
74+
return aggAgeGender
75+
}, [])
76+
let totalOrders = 0
77+
aggregation.forEach(({ _id, orders }) => {
78+
const { birth, gender } = _id
79+
;[aggByAge, aggAgeGender].forEach(groups => {
80+
for (let i = 0; i < groups.length; i++) {
81+
const group = groups[i]
82+
if (!(group.minYear > birth)) {
83+
if (gender && gender !== group.gender) {
84+
continue
85+
}
86+
group.orders += orders
87+
break
88+
}
89+
}
90+
})
91+
if (gender) {
92+
ordersByGender[gender] += orders
93+
}
94+
totalOrders += orders
95+
})
96+
const radar = new Chart(this.$refs['canva-radar'], {
97+
type: 'radar',
98+
data: {
99+
labels: aggByAge.map(({ label }) => label),
100+
datasets: genders.map(gender => {
101+
return {
102+
data: aggByAge.map(({ minYear }) => {
103+
const data = aggAgeGender.find(group => {
104+
return group.gender === gender && minYear === group.minYear
105+
})
106+
if (data) {
107+
return parseInt(data.orders * 10000 / totalOrders, 10) / 100
108+
}
109+
return 0
110+
}),
111+
label: `${this.i19Gender(gender)}: ` +
112+
`${(ordersByGender[gender] * 100 / totalOrders).toFixed(2)}%`,
113+
backgroundColor: `rgba(${rgbByGender[gender]}, 0.5)`,
114+
borderColor: `rgba(${rgbByGender[gender]}, 0.85)`,
115+
borderWidth: 1
116+
}
117+
})
118+
},
119+
options: {
120+
tooltips: {
121+
callbacks: {
122+
title: ([{ value }]) => {
123+
return `${value}%`
124+
},
125+
label: ({ index, datasetIndex }, { labels }) => {
126+
return `${this.i19Gender(genders[datasetIndex])} & ${labels[index]}`
127+
}
128+
}
129+
}
130+
}
131+
})
132+
if (radar) {
133+
return new Chart(this.$refs['canva-doughnut'], {
134+
type: 'doughnut',
135+
data: {
136+
labels: aggByAge.map(({ label }) => label),
137+
datasets: [{
138+
data: aggByAge.map(({ orders }) => orders),
139+
backgroundColor: aggByAge.map((group, index) => `hsl(295, 100%, ${(32 - index * 7)}%)`)
140+
}]
141+
},
142+
options: {
143+
tooltips: {
144+
callbacks: {
145+
label: ({ index }) => {
146+
const { orders } = aggByAge[index]
147+
return `${(orders * 100 / totalOrders).toFixed(2)}%`
148+
},
149+
beforeLabel: ({ index }) => {
150+
return aggByAge[index].label
151+
}
152+
}
153+
}
154+
}
155+
})
156+
}
157+
}
158+
},
159+
160+
created () {
161+
return this.ecomAuth.requestApi('$aggregate', 'post', {
162+
resource: 'customers',
163+
pipeline: [
164+
{ $match: { orders_count: { $gt: 0 } } },
165+
{
166+
$group: {
167+
_id: {
168+
birth: '$birth_date.year',
169+
gender: '$gender'
170+
},
171+
orders: { $sum: '$orders_count' }
172+
}
173+
}
174+
]
175+
})
176+
.then(({ data }) => {
177+
this.$nextTick(() => {
178+
this.setupCharts(data.result)
179+
})
180+
})
181+
.catch(console.error)
182+
.finally(() => {
183+
this.$emit('load')
184+
})
185+
}
186+
}

src/components/js/EcHomeCards.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
// i19buyersProfile,
23
// i19disfavor,
34
// i19favor,
45
// i19latestOrders,
@@ -16,6 +17,7 @@ import ecomAuth from '@ecomplus/auth'
1617
import Vue from 'vue'
1718
import { SlideYUpTransition } from 'vue2-transitions'
1819

20+
const i19buyersProfile = 'Perfil dos compradores'
1921
const i19latestOrders = 'Últimos pedidos'
2022
const i19topSellingProducts = 'Produtos mais vendidos'
2123

@@ -50,6 +52,10 @@ export default {
5052
id: 'payment_methods_chart',
5153
title: i18n(i19paymentMethods),
5254
load: id => this.renderCard(import('../cards/EcPaymentMethodsChart.vue'), id)
55+
}, {
56+
id: 'buyers_profile_chart',
57+
title: i18n(i19buyersProfile),
58+
load: id => this.renderCard(import('../cards/EcBuyersProfileChart.vue'), id)
5359
}],
5460
loadingCards: [],
5561
loadedCards: [],

0 commit comments

Comments
 (0)