Skip to content
This repository was archived by the owner on Jul 25, 2025. It is now read-only.

Commit 87f6a82

Browse files
Add tailwindcss + previous period visualization
1 parent f3b82e3 commit 87f6a82

File tree

17 files changed

+3173
-196
lines changed

17 files changed

+3173
-196
lines changed

src/controllers/OrderController.php

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use craft\commerce\Plugin as CommercePlugin;
1414
use craft\db\Query;
1515
use craft\helpers\Db;
16+
use DateTime;
1617

1718
class OrderController extends \craft\web\Controller
1819
{
@@ -40,13 +41,8 @@ private function asCurrency($value)
4041
: Craft::$app->formatter->asCurrency(0);
4142
}
4243

43-
public function actionOrderIndex($format='html')
44+
public function getOrderQuery($min, $max)
4445
{
45-
$statuses = CommercePlugin::getInstance()->orderStatuses;
46-
47-
$min = $this->params->start();
48-
$max = $this->params->end();
49-
5046
$taxQuery = (new Query())
5147
->select(['orderId', 'type', 'SUM(amount) as total'])
5248
->from('{{%commerce_orderadjustments}}')
@@ -97,11 +93,25 @@ public function actionOrderIndex($format='html')
9793
$query->andWhere([$key => $value]);
9894
}
9995

100-
$data = $query->all();
101-
$rows = collect($query->all());
96+
return collect($query->all());
97+
}
98+
99+
public function actionOrderIndex($format='html')
100+
{
101+
$statuses = CommercePlugin::getInstance()->orderStatuses;
102+
103+
$min = $this->params->start();
104+
$max = $this->params->end();
105+
106+
$currMin = new DateTime($min);
107+
$prevMin = (clone $currMin)->sub($currMin->diff(new DateTime($max)))->format('Y-m-d');
108+
109+
$rows = $this->getOrderQuery($min, $max);
110+
$prevPeriodRows = $this->getOrderQuery($prevMin, $min);
102111

103112
$formatterClass = BaseFormatter::getFormatter(Orders::class);
104113
$formatter = new $formatterClass($rows);
114+
$prevPeriodFormatter = new $formatterClass($prevPeriodRows, $prevMin, $min);
105115

106116
if ($format == 'csv') {
107117
return Plugin::getInstance()->csv->generate('orders', $formatter->csv());
@@ -133,6 +143,7 @@ public function actionOrderIndex($format='html')
133143
'formatter' => $formatter::$key,
134144
'chartShowsCurrency' => $formatter->showsCurrency(),
135145
'chartData' => $formatter->format(),
146+
'secondaryChartData' => $prevPeriodFormatter->format(),
136147
'min' => $min,
137148
'max' => $max,
138149
'range' => $this->params->range(),

src/controllers/RevenueController.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,15 @@ public function actionRevenueIndex($format='html')
8484
$max = $this->params->end();
8585

8686
$currMin = new DateTime($min);
87-
$prevMin = (clone $currMin)->sub($currMin->diff(new DateTime($max)));
87+
$prevMin = (clone $currMin)->sub($currMin->diff(new DateTime($max)))->format('Y-m-d');
8888

8989
$formatterClass = BaseFormatter::getFormatter(Revenue::class);
9090

9191
$rows = $this->getRevenueQuery($min, $max, $request);
92-
$prevPeriodRows = $this->getRevenueQuery($prevMin->format('Y-m-d'), $min, $request);
92+
$prevPeriodRows = $this->getRevenueQuery($prevMin, $min, $request);
9393

9494
$formatter = new $formatterClass($rows);
95-
$prevPeriodFormatter = new $formatterClass($prevPeriodRows);
95+
$prevPeriodFormatter = new $formatterClass($prevPeriodRows, $prevMin, $min);
9696

9797
$prevTotal = $prevPeriodFormatter->totalPaid();
9898
$diff = $formatter->totalPaid() - $prevTotal;
@@ -128,6 +128,7 @@ public function actionRevenueIndex($format='html')
128128
'totals' => $totals, // $totalsHtml,
129129
'chartShowsCurrency' => $formatter->showsCurrency(),
130130
'chartData' => $formatter->format(),
131+
'secondaryChartData' => $prevPeriodFormatter->format(),
131132
'min' => $min,
132133
'max' => $max,
133134
'range' => $this->params->range(),

src/formatters/BaseFormatter.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ abstract class BaseFormatter
3333
/** @var Collection */
3434
protected $groupedData;
3535

36-
public function __construct($data)
36+
public function __construct($data, $min = null, $max = null)
3737
{
3838
$this->params = Plugin::getInstance()->paramParser;
39-
$this->min = $this->params->start();
40-
$this->max = $this->params->end();
39+
$this->min = $min ?: $this->params->start();
40+
$this->max = $max ?: $this->params->end();
4141
$this->step = $this->params->step();
4242
$this->stepFormat = $this->params->stepFormat();
4343
$this->empty = $this->getEmptyCollection();

src/front/dist/main.js

Lines changed: 152 additions & 130 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/front/package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,29 @@
1212
"chart.js": "^2.7.3",
1313
"date-fns": "^1.29.0",
1414
"lodash": "^4.17.11",
15+
"tailwindcss": "^0.7.3",
1516
"vue": "^2.5.17",
1617
"vue-router": "^3.0.1"
1718
},
1819
"devDependencies": {
1920
"@babel/core": "^7.1.5",
2021
"@babel/preset-env": "^7.1.5",
22+
"autoprefixer": "^9.4.3",
2123
"babel-helper-vue-jsx-merge-props": "^2.0.3",
2224
"babel-loader": "^8.0.4",
2325
"babel-plugin-syntax-jsx": "^6.18.0",
2426
"babel-plugin-transform-vue-jsx": "^3.7.0",
2527
"css-loader": "^1.0.1",
28+
"cssnano": "^4.1.8",
2629
"eslint": "^5.9.0",
2730
"eslint-config-prettier": "^3.3.0",
2831
"eslint-plugin-prettier": "^3.0.0",
2932
"eslint-plugin-vue": "^4.7.1",
33+
"postcss-cssnext": "^3.1.0",
34+
"postcss-import": "^12.0.1",
35+
"postcss-loader": "^3.0.0",
36+
"postcss-preset-env": "^6.5.0",
37+
"postcss-safe-parser": "^4.0.1",
3038
"prettier": "^1.15.2",
3139
"vue-loader": "^15.4.2",
3240
"vue-style-loader": "^4.1.2",

src/front/postcss.config.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const tailwindcss = require('tailwindcss')
2+
3+
module.exports = {
4+
// parser: 'postcss-safe-parser',
5+
plugins: [
6+
require('tailwindcss')('./tailwind.js'),
7+
require('autoprefixer'),
8+
require('postcss-preset-env'),
9+
require('postcss-import')
10+
]
11+
}

src/front/src/App.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
</template>
44

55
<script>
6+
import './tailwind.css'
7+
68
export default {
79
name: 'App',
810
}

src/front/src/components/DateLineChart.vue

Lines changed: 81 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,13 @@ const startOf = (interval, value) => {
4545
}
4646
}
4747
48-
const groupData = (min, max, data) => {
48+
const formatDay = (min, max, day) => {
4949
const interval = calculateInterval(min, max)
50+
return format(startOf(interval, parseDate(day)), 'YYYY-MM-DDTHH:mm:ss.SSSZ')
51+
}
5052
51-
const formatDay = day =>
52-
format(startOf(interval, parseDate(day.x)), 'YYYY-MM-DDTHH:mm:ss.SSSZ')
53-
const groupedResults = groupBy(data, formatDay)
53+
const groupData = (min, max, data) => {
54+
const groupedResults = groupBy(data, day => formatDay(min, max, day.x))
5455
5556
const result = Object.keys(groupedResults).map(key => ({
5657
x: key,
@@ -60,18 +61,55 @@ const groupData = (min, max, data) => {
6061
return result
6162
}
6263
64+
const normalizeData = (primary, secondary) =>
65+
primary
66+
.filter((o, index) => secondary[index])
67+
.map((o, index) => {
68+
const hasSecondary = secondary[index]
69+
return {
70+
x: o.x,
71+
y: secondary[index].y,
72+
secondary: secondary[index].x,
73+
}
74+
})
75+
6376
const options = vm => ({
6477
legend: {
6578
display: false,
6679
},
80+
tooltips: {
81+
mode: 'index',
82+
position: 'nearest',
83+
intersect: false,
84+
callbacks: {
85+
label: (item, data) => {
86+
const {datasets} = data
87+
88+
const originalItem = datasets[item.datasetIndex].data[item.index]
89+
90+
let xLabel = item.xLabel
91+
if (originalItem.secondary) {
92+
xLabel = originalItem.secondary
93+
}
94+
95+
xLabel = format(xLabel, 'MM/YYYY')
96+
return `${xLabel}: ${item.yLabel}`;
97+
},
98+
title: () => '',
99+
},
100+
},
101+
hover: {
102+
mode: 'index',
103+
intersect: true
104+
},
67105
scales: {
68106
yAxes: [
69107
{
108+
type: 'linear',
109+
position: 'left',
110+
id: 'primary',
70111
ticks: {
71112
beginAtZero: true,
72-
callback: value => {
73-
return value
74-
}, //this.yAxesCallback.bind(this),
75113
},
76114
},
77115
],
@@ -94,36 +132,50 @@ export default {
94132
props: [
95133
'start',
96134
'end',
97-
'chartData'
135+
'chartData',
136+
'secondaryChartData',
98137
],
99-
// render() {
100-
// return (
101-
// <Chart
102-
// chartData={this.transformedData}
103-
// options={this.options}
104-
// min={this.min}
105-
// max={this.max}
106-
// width={900}
107-
// />
108-
// )
109-
// },
110138
computed: {
111-
transformedData: ({ chartData, min, max }) => ({
112-
datasets: [
113-
{
114-
label: '',
115-
lineTension: 0,
116-
data: groupData(min, max, chartData),
117-
backgroundColor: '#36a2eb',
118-
},
119-
],
120-
}),
139+
transformedData: ({ chartData, secondaryChartData, min, max }) => {
140+
141+
const groupedData = groupData(min, max, chartData)
142+
const datasets = {
143+
datasets: [
144+
{
145+
label: 'Current',
146+
// lineTension: 0,
147+
data: groupedData,
148+
backgroundColor: 'rgb(54, 162, 235)',
149+
borderColor: 'rgb(54, 162, 235)',
150+
fill: false,
151+
},
152+
],
153+
}
154+
155+
if (secondaryChartData) {
156+
datasets.datasets.push({
157+
label: 'Previous',
158+
display: true,
159+
data: normalizeData(groupedData, groupData(min, max, secondaryChartData)),
160+
backgroundColor: 'rgba(75, 192, 192, 0.5)',
161+
borderColor: 'rgba(75, 192, 192, 0.5)',
162+
fill: false,
163+
})
164+
}
165+
166+
return datasets
167+
},
121168
min: ({ start }) => parseDate(start),
122169
max: ({ end }) => parseDate(end),
123170
},
124171
watch: {
125172
transformedData () {
126173
this.chart.data.datasets[0].data = this.transformedData.datasets[0].data
174+
175+
if (this.secondaryChartData) {
176+
this.chart.data.datasets[1].data = this.transformedData.datasets[1].data
177+
}
178+
127179
this.chart.options = options(this)
128180
this.chart.update()
129181
},

src/front/src/components/craft/ActionIcon.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,7 @@ export default {
2020
</script>
2121

2222
<style scoped>
23-
.icon.user:before { content: 'user'; }
23+
.icon.user:before {
24+
content: 'user';
25+
}
2426
</style>

src/front/src/pages/Orders/index.vue

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
<Chart
3434
:chart-data="state.chartData"
35+
:secondary-chart-data="state.secondaryChartData"
3536
:start="state.startDate"
3637
:end="state.endDate"
3738
/>
@@ -103,6 +104,7 @@ export default {
103104
.then(({ data }) => {
104105
this.shouldUpdate = false
105106
this.state.chartData = data.chartData
107+
this.state.secondaryChartData = data.secondaryChartData
106108
this.state.startDate = data.min
107109
this.state.endDate = format(subDays(parse(data.max), 1), 'YYYY-MM-DD')
108110
this.state.results = data.tableData

0 commit comments

Comments
 (0)