Skip to content

Commit

Permalink
Site: add config UI (BC) (evcc-io#9812)
Browse files Browse the repository at this point in the history
  • Loading branch information
andig authored Jan 27, 2024
1 parent 09d7e76 commit 186ba62
Show file tree
Hide file tree
Showing 47 changed files with 3,844 additions and 1,587 deletions.
10 changes: 6 additions & 4 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ module.exports = {
es6: true,
},
extends: [
"plugin:vue/vue3-essential",
"eslint:recommended",
"plugin:vue/vue3-recommended",
"plugin:prettier/recommended",
"prettier",
"@vue/eslint-config-typescript",
"@vue/eslint-config-prettier/skip-formatting",
],
parser: "vue-eslint-parser",
parserOptions: {
ecmaVersion: "latest",
},
rules: {
"vue/require-default-prop": "off",
"vue/attribute-hyphenation": "off",
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ __debug_bin
!package*.json
!evcc.dist.yaml
!tests/**/*.evcc.yaml
!tsconfig.json
/templates/docs
evcc
evcc.exe
Expand Down
22 changes: 20 additions & 2 deletions assets/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
--evcc-box: var(--bs-white);
--evcc-default-text: var(--bs-gray-dark);
--evcc-gray: var(--bs-gray-medium);
--evcc-gray-50: #93949e80;

--evcc-accent1: var(--evcc-dark-yellow);
--evcc-accent2: var(--evcc-darker-green);
Expand Down Expand Up @@ -193,19 +194,27 @@ a:hover {
opacity: 0.2;
}

.btn-outline-primary {
--bs-btn-active-bg: var(--bs-primary);
--bs-btn-active-border-color: var(--bs-primary);
}

.btn-outline-primary,
.btn-outline-primary:focus {
color: var(--bs-primary);
background-color: transparent;
border-color: var(--bs-primary);
}

.btn-outline-primary:hover {
color: var(--evcc-accent3) !important;
background-color: transparent !important;
border-color: var(--evcc-accent3) !important;
}

.btn-group > .btn-check + .btn-outline-primary:hover,
.btn-outline-primary:active {
color: var(--evcc-default-text) !important;
color: var(--bs-white) !important;
background-color: var(--evcc-accent3) !important;
border-color: var(--evcc-accent3) !important;
}
Expand Down Expand Up @@ -319,7 +328,6 @@ a:hover {

.modal-body {
padding: 1rem 0 0;
overflow-x: hidden;
}

.modal-footer {
Expand Down Expand Up @@ -427,6 +435,9 @@ input::-webkit-datetime-edit {
background-color: var(--evcc-box);
color: var(--evcc-default-text);
}
.form-control:disabled {
color: var(--bs-gray-dark);
}

input[type="time"]::-webkit-calendar-picker-indicator {
display: none;
Expand All @@ -436,6 +447,13 @@ input[type="time"]::-webkit-calendar-picker-indicator {
--bs-table-bg: transparent;
}

.badge {
--bs-badge-font-size: 0.8rem;
--bs-badge-padding-x: 0.75em;
--bs-badge-padding-y: 0.75em;
font-weight: normal;
}

/* larger check switch */
.form-switch .form-check-input {
height: 1.1rem;
Expand Down
4 changes: 2 additions & 2 deletions assets/js/components/ChargingPlanSettingsEntry.vue
Original file line number Diff line number Diff line change
Expand Up @@ -269,12 +269,12 @@ export default {
this.$t("main.targetCharge.tomorrow"),
];
for (let i = 0; i < 7; i++) {
const dayNumber = date.toLocaleDateString(this.$i18n.locale, {
const dayNumber = date.toLocaleDateString(this.$i18n?.locale, {
month: "short",
day: "numeric",
});
const dayName =
labels[i] || date.toLocaleDateString(this.$i18n.locale, { weekday: "long" });
labels[i] || date.toLocaleDateString(this.$i18n?.locale, { weekday: "long" });
options.push({
value: this.fmtDayString(date),
name: `${dayNumber} (${dayName})`,
Expand Down
45 changes: 45 additions & 0 deletions assets/js/components/Config/AddDeviceButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<template>
<li class="root">
<button class="d-flex align-items-center justify-content-center p-3" @click="$emit('add')">
<shopicon-regular-plus class="me-1"></shopicon-regular-plus>
{{ title }}
</button>
</li>
</template>

<script>
import "@h2d2/shopicons/es/regular/plus";
export default {
name: "AddDeviceButton",
props: {
title: String,
},
emits: ["add"],
};
</script>

<style scoped>
.root {
padding: 0;
display: block;
list-style-type: none;
min-height: 9rem;
border-radius: 1rem;
border: 1px solid var(--evcc-gray-50);
padding: 1rem 1rem 0.5rem;
transition: border-color var(--evcc-transition-fast) linear;
}
.root:hover {
border-color: var(--evcc-default-text);
color: var(--evcc-default-text);
}
button {
border-radius: 1rem;
background: none;
border: none;
height: 100%;
width: 100%;
color: inherit;
}
</style>
105 changes: 105 additions & 0 deletions assets/js/components/Config/DeviceCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<template>
<li class="root py-2 px-4">
<div class="d-flex align-items-center mb-2">
<div class="icon me-2">
<slot name="icon" />
</div>
<strong class="flex-grow-1 text-nowrap text-truncate">{{ name }}</strong>
<button
v-if="unconfigured"
type="button"
class="btn btn-sm btn-outline-secondary position-relative border-0 p-2"
:title="$t('config.main.new')"
@click="$emit('configure')"
>
<shopicon-regular-adjust size="s"></shopicon-regular-adjust>
</button>
<button
v-else-if="editable"
type="button"
class="btn btn-sm btn-outline-secondary position-relative border-0 p-2"
:title="$t('config.main.edit')"
@click="$emit('edit')"
>
<shopicon-regular-adjust size="s"></shopicon-regular-adjust>
</button>
<button
v-else
ref="tooltip"
type="button"
class="btn btn-sm btn-outline-secondary position-relative border-0 p-2 opacity-25"
data-bs-toggle="tooltip"
:title="$t('config.main.yaml')"
>
<shopicon-regular-adjust size="s"></shopicon-regular-adjust>
</button>
</div>
<div v-if="unconfigured" class="text-center py-3 evcc-gray">
{{ $t("config.main.unconfigured") }}
</div>
<slot v-else name="tags" />
</li>
</template>

<script>
import "@h2d2/shopicons/es/regular/adjust";
import "@h2d2/shopicons/es/regular/invoice";
import "@h2d2/shopicons/es/regular/edit";
import Tooltip from "bootstrap/js/dist/tooltip";
export default {
name: "DeviceCard",
props: {
name: String,
editable: Boolean,
unconfigured: Boolean,
},
data() {
return {
tooltip: null,
};
},
emits: ["edit", "configure"],
mounted() {
this.initTooltip();
},
watch: {
editable() {
this.initTooltip();
},
unconfigured() {
this.initTooltip();
},
},
methods: {
initTooltip() {
this.$nextTick(() => {
this.tooltip?.dispose();
if (this.$refs.tooltip) {
this.tooltip = new Tooltip(this.$refs.tooltip);
}
});
},
},
};
</script>
<style scoped>
.root {
display: block;
list-style-type: none;
min-height: 9rem;
color: var(--evcc-default-text);
border-radius: 1rem;
border: 1px solid var(--evcc-gray-50);
padding: 1rem 1rem 0.5rem;
transition: border-color var(--evcc-transition-fast) linear;
background: var(--evcc-box);
}
.root:hover {
border-color: var(--evcc-gray);
}
.icon:empty {
display: none;
}
</style>
64 changes: 64 additions & 0 deletions assets/js/components/Config/DeviceTags.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<template>
<div v-if="tags" class="d-flex mb-2 flex-wrap">
<span
v-for="(entry, index) in entries"
:key="index"
class="badge text-bg-secondary me-2 mb-2"
:class="{
'text-bg-secondary': !entry.error,
'text-bg-danger': entry.error,
}"
>
<strong>{{ $t(`config.deviceValue.${entry.name}`) }}:</strong>
{{ fmtDeviceValue(entry) }}
</span>
</div>
</template>
<script>
import formatter from "../../mixins/formatter";
export default {
name: "DeviceTags",
props: {
tags: Object,
},
mixins: [formatter],
computed: {
entries() {
return Object.entries(this.tags).map(([name, { value, error }]) => {
return { name, value, error };
});
},
},
methods: {
fmtDeviceValue(entry) {
const { name, value } = entry;
switch (name) {
case "power":
return this.fmtKw(value);
case "energy":
case "capacity":
case "chargedEnergy":
return this.fmtKWh(value * 1e3);
case "soc":
return `${this.fmtNumber(value, 1)}%`;
case "odometer":
case "range":
return `${this.fmtNumber(value, 0)} km`;
case "phaseCurrents":
return value.map((v) => this.fmtNumber(v, 0)).join(" ") + " A";
case "phaseVoltages":
return value.map((v) => this.fmtNumber(v, 0)).join(" ") + " V";
case "phasePowers":
return value.map((v) => this.fmtKw(v)).join(", ");
case "chargeStatus":
return value;
case "socLimit":
return `${this.fmtNumber(value)}%`;
}
return value;
},
},
};
</script>
<style scoped></style>
11 changes: 5 additions & 6 deletions assets/js/components/Config/FormRow.vue
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
<!-- eslint-disable vue/no-v-html -->
<template>
<div class="mb-3">
<div class="mb-4">
<label :for="id">
<div class="form-label">
{{ label }}
<small v-if="optional">{{ $t("config.form.optional") }}</small>
<small v-if="optional" class="evcc-gray">{{ $t("config.form.optional") }}</small>
</div>
</label>
<div :class="smallValue ? 'w-50' : 'w-100'">
<div class="w-100">
<slot />
</div>
<div class="form-text">
<div class="form-text evcc-gray">
<div v-if="example">{{ $t("config.form.example") }}: {{ example }}</div>
<div v-if="help" v-html="helpHtml"></div>
<div v-if="help" v-html="helpHtml" class="text-gray"></div>
</div>
</div>
</template>
Expand All @@ -27,7 +27,6 @@ export default {
label: String,
help: String,
optional: Boolean,
smallValue: Boolean,
example: String,
},
computed: {
Expand Down
Loading

0 comments on commit 186ba62

Please sign in to comment.