Skip to content

Commit 5a186c8

Browse files
committed
Cycling mileage log
1 parent a202132 commit 5a186c8

File tree

4 files changed

+201
-18
lines changed

4 files changed

+201
-18
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"data": {
3+
"type": "card",
4+
"attributes": {
5+
"target": 1500,
6+
"entries": [
7+
{
8+
"date": "2024-05-03",
9+
"origin": "Bandung Indah Plaza",
10+
"destination": "Kamojang Mountain",
11+
"distance": 65
12+
},
13+
{
14+
"date": "2024-04-27",
15+
"origin": "Cibiru Hilir",
16+
"destination": "Lingkar Nagreg",
17+
"distance": 60
18+
}
19+
],
20+
"title": "Cycling Mileage Log",
21+
"description": null,
22+
"thumbnailURL": null
23+
},
24+
"meta": {
25+
"adoptsFrom": {
26+
"module": "../cycling-mileage-log",
27+
"name": "CyclingMileageLog"
28+
}
29+
}
30+
}
31+
}

Diff for: packages/drafts-realm/cycling-mileage-log.gts

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import {
2+
contains,
3+
containsMany,
4+
CardDef,
5+
FieldDef,
6+
StringField,
7+
field,
8+
} from 'https://cardstack.com/base/card-api';
9+
import NumberField from 'https://cardstack.com/base/number';
10+
import { Component } from 'https://cardstack.com/base/card-api';
11+
import Date from 'https://cardstack.com/base/date';
12+
// @ts-ignore
13+
import {
14+
Chart,
15+
registerables,
16+
// @ts-ignore
17+
} from 'https://cdn.jsdelivr.net/npm/chart.js@4.4.2/+esm';
18+
import Modifier from 'ember-modifier';
19+
20+
type ProgressChartModifierSignature = {
21+
Args: {
22+
Named: {
23+
target?: number;
24+
total?: number;
25+
};
26+
};
27+
Element: HTMLElement;
28+
};
29+
30+
class ProgressChartModifier extends Modifier<ProgressChartModifierSignature> {
31+
modify(
32+
element: HTMLElement,
33+
_positional: [],
34+
{ target, total }: ProgressChartModifierSignature['Args']['Named'],
35+
) {
36+
total = total ?? 0;
37+
target = target ?? 0;
38+
Chart.register(...registerables);
39+
new Chart(element, {
40+
type: 'doughnut',
41+
data: {
42+
labels: ['progress (KM)', 'remaining (KM)'],
43+
datasets: [
44+
{
45+
label: ['Cycling Mileage Log'],
46+
data: [total, target - total],
47+
backgroundColor: ['rgb(255, 99, 132)', 'rgb(54, 162, 235)'],
48+
hoverOffset: 4,
49+
},
50+
],
51+
},
52+
});
53+
}
54+
}
55+
56+
class CyclingLogEntry extends FieldDef {
57+
static displayName = 'Cycling Log Entry';
58+
59+
@field date = contains(Date);
60+
@field origin = contains(StringField);
61+
@field destination = contains(StringField);
62+
@field distance = contains(NumberField);
63+
64+
static embedded = class Embedded extends Component<typeof this> {
65+
<template>
66+
<div class='entry'>
67+
<span>Date: <@fields.date /></span>
68+
<span>Routes: <@fields.origin /> - <@fields.destination /></span>
69+
<span>Distance: <@fields.distance /> KM</span>
70+
</div>
71+
<style>
72+
.entry {
73+
display: flex;
74+
flex-direction: column;
75+
padding: var(--boxel-sp);
76+
border: var(--boxel-border);
77+
}
78+
</style>
79+
</template>
80+
};
81+
}
82+
83+
export class CyclingMileageLog extends CardDef {
84+
static displayName = 'Cycling Mileage Log';
85+
86+
@field target = contains(NumberField);
87+
@field total = contains(NumberField, {
88+
computeVia: function (this: CyclingMileageLog) {
89+
let sum = 0;
90+
for (let entry of this.entries) {
91+
sum += entry.distance;
92+
}
93+
return sum;
94+
},
95+
});
96+
@field successPercentage = contains(NumberField, {
97+
computeVia: function (this: CyclingMileageLog) {
98+
return this.total / this.target;
99+
},
100+
});
101+
@field entries = containsMany(CyclingLogEntry);
102+
103+
static isolated = class Isolated extends Component<typeof this> {
104+
<template>
105+
<div class='cycling-mileage-log'>
106+
<div class='summary'>
107+
<span>Target: <@fields.target /> KM</span>
108+
<span>Progress: <@fields.total /> KM</span>
109+
<span>Success Percentage: <@fields.successPercentage /></span>
110+
</div>
111+
<div class='chart'>
112+
<canvas
113+
id='progress-chart'
114+
{{ProgressChartModifier
115+
total=this.args.model.total
116+
target=this.args.model.target
117+
}}
118+
/>
119+
</div>
120+
<div class='entries'>
121+
<@fields.entries />
122+
</div>
123+
</div>
124+
<style>
125+
.summary {
126+
display: flex;
127+
justify-content: center;
128+
align-items: center;
129+
gap: var(--boxel-sp-xs);
130+
margin-bottom: var(--boxel-sp-lg);
131+
}
132+
.chart {
133+
display: flex;
134+
justify-content: center;
135+
max-height: 400px;
136+
}
137+
.cycling-mileage-log {
138+
display: flex;
139+
flex-direction: column;
140+
padding: var(--boxel-sp);
141+
}
142+
.entries {
143+
padding: var(--boxel-sp);
144+
}
145+
</style>
146+
</template>
147+
};
148+
}

Diff for: packages/drafts-realm/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"devDependencies": {
77
"@cardstack/boxel-ui": "workspace:*",
88
"@cardstack/runtime-common": "workspace:*",
9-
"@types/lodash": "^4.14.182"
9+
"@types/lodash": "^4.14.182",
10+
"ember-modifier": "^4.1.0"
1011
},
1112
"scripts": {}
1213
}

Diff for: pnpm-lock.yaml

+20-17
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)