Skip to content

Commit

Permalink
Add setting modals to top navigation (evcc-io#11405)
Browse files Browse the repository at this point in the history
  • Loading branch information
naltatis authored Jan 6, 2024
1 parent 0b90672 commit 41a5dd7
Show file tree
Hide file tree
Showing 13 changed files with 207 additions and 46 deletions.
1 change: 1 addition & 0 deletions assets/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
--evcc-accent2: var(--evcc-dark-green);
--evcc-accent3: var(--evcc-darker-green);
--bs-primary: var(--evcc-dark-green);
--bs-border-color-translucent: rgba(255, 255, 255, 0.175);
}

html {
Expand Down
40 changes: 15 additions & 25 deletions assets/js/components/Energyflow/Energyflow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<div
class="energyflow cursor-pointer position-relative"
:class="{ 'energyflow--open': detailsOpen }"
data-testid="energyflow"
@click="toggleDetails"
>
<div class="row">
Expand Down Expand Up @@ -78,6 +79,7 @@
:details="batterySoc"
:detailsFmt="batteryFmt"
detailsClickable
data-testid="energyflow-entry-batterydischarge"
@details-clicked="openBatterySettingsModal"
/>
<EnergyflowEntry
Expand All @@ -87,8 +89,9 @@
:powerInKw="powerInKw"
:details="detailsValue(tariffGrid, tariffCo2)"
:detailsFmt="detailsFmt"
:detailsClickable="smartCostAvailable"
:detailsClickable="gridModalAvailable"
:detailsTooltip="detailsTooltip(tariffGrid, tariffCo2)"
data-testid="energyflow-entry-gridimport"
@details-clicked="openGridSettingsModal"
/>
</div>
Expand Down Expand Up @@ -157,8 +160,6 @@
</div>
</div>
</div>
<GridSettingsModal v-bind="gridSettings" />
<BatterySettingsModal v-bind="batterySettings" />
</div>
</template>

Expand All @@ -167,22 +168,19 @@ import "@h2d2/shopicons/es/filled/square";
import Modal from "bootstrap/js/dist/modal";
import Visualization from "./Visualization.vue";
import EnergyflowEntry from "./EnergyflowEntry.vue";
import GridSettingsModal from "../GridSettingsModal.vue";
import formatter from "../../mixins/formatter";
import AnimatedNumber from "../AnimatedNumber.vue";
import settings from "../../settings";
import { CO2_TYPE, PRICE_DYNAMIC_TYPE, PRICE_FORECAST_TYPE } from "../../units";
import { CO2_TYPE } from "../../units";
import collector from "../../mixins/collector";
import BatterySettingsModal from "../BatterySettingsModal.vue";
import gridModalAvailable from "../../utils/gridModalAvailable";
export default {
name: "Energyflow",
components: {
Visualization,
EnergyflowEntry,
AnimatedNumber,
GridSettingsModal,
BatterySettingsModal,
},
mixins: [formatter, collector],
props: {
Expand Down Expand Up @@ -217,8 +215,8 @@ export default {
return { detailsOpen: false, detailsCompleteHeight: null, gridSettingsModal: null };
},
computed: {
smartCostAvailable: function () {
return [CO2_TYPE, PRICE_DYNAMIC_TYPE, PRICE_FORECAST_TYPE].includes(this.smartCostType);
gridModalAvailable: function () {
return gridModalAvailable(this.smartCostType);
},
gridImport: function () {
return Math.max(0, this.gridPower);
Expand Down Expand Up @@ -288,27 +286,15 @@ export default {
batteryFmt() {
return (soc) => `${Math.round(soc)}%`;
},
gridSettings() {
return this.collectProps(GridSettingsModal);
},
batterySettings() {
return this.collectProps(BatterySettingsModal);
},
co2Available() {
return this.smartCostType === CO2_TYPE;
},
},
mounted() {
this.gridSettingsModal = Modal.getOrCreateInstance(
document.querySelector("#gridSettingsModal")
);
this.batterySettingsModal = Modal.getOrCreateInstance(
document.querySelector("#batterySettingsModal")
);
window.addEventListener("resize", this.updateHeight);
// height must be calculated in case of initially open details
if (settings.energyflowDetails) {
setTimeout(this.toggleDetails, 50);
setTimeout(this.toggleDetails, 100);
}
},
unmounted() {
Expand Down Expand Up @@ -349,10 +335,14 @@ export default {
this.detailsCompleteHeight = this.$refs.detailsInner.offsetHeight;
},
openGridSettingsModal() {
this.gridSettingsModal.show();
const modal = Modal.getOrCreateInstance(document.getElementById("gridSettingsModal"));
modal.show();
},
openBatterySettingsModal() {
this.batterySettingsModal.show();
const modal = Modal.getOrCreateInstance(
document.getElementById("batterySettingsModal")
);
modal.show();
},
},
};
Expand Down
1 change: 1 addition & 0 deletions assets/js/components/Energyflow/EnergyflowEntry.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
ref="details"
class="fw-normal"
:class="{ 'text-decoration-underline': detailsClickable }"
data-testid="energyflow-entry-details"
data-bs-toggle="tooltip"
:tabindex="detailsClickable ? 0 : undefined"
@click="detailsClicked"
Expand Down
45 changes: 35 additions & 10 deletions assets/js/components/TopNavigation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,32 @@
{{ $t("header.sessions") }}
</router-link>
</li>
<li><hr class="dropdown-divider" /></li>
<li>
<button type="button" class="dropdown-item" @click="openSettingsModal">
<span
v-if="sponsorTokenExpires"
class="d-inline-block p-1 rounded-circle bg-danger border border-light rounded-circle"
></span>
{{ $t("header.settings") }}
{{ $t("settings.title") }}
</button>
</li>
<li v-if="batteryModalAvailable">
<button type="button" class="dropdown-item" @click="openBatterySettingsModal">
{{ $t("batterySettings.modalTitle") }}
</button>
</li>
<li v-if="gridModalAvailable">
<button type="button" class="dropdown-item" @click="openGridSettingsModal">
{{ $t("gridSettings.modalTitle") }}
</button>
</li>
<li v-if="$hiddenFeatures()">
<router-link class="dropdown-item" to="/config"> Configuration 🧪 </router-link>
<router-link class="dropdown-item" to="/config">
Device Configuration 🧪
</router-link>
</li>
<li><hr class="dropdown-divider" /></li>
<template v-if="providerLogins.length > 0">
<li><hr class="dropdown-divider" /></li>
<li>
Expand Down Expand Up @@ -73,8 +87,6 @@
</a>
</li>
</ul>
<GlobalSettingsModal v-bind="globalSettingsModalProps" />
<HelpModal />
</div>
</template>

Expand All @@ -85,15 +97,13 @@ import "@h2d2/shopicons/es/regular/gift";
import "@h2d2/shopicons/es/regular/moonstars";
import "@h2d2/shopicons/es/regular/menu";
import "@h2d2/shopicons/es/regular/newtab";
import GlobalSettingsModal from "./GlobalSettingsModal.vue";
import HelpModal from "./HelpModal.vue";
import collector from "../mixins/collector";
import gridModalAvailable from "../utils/gridModalAvailable";
import baseAPI from "../baseapi";
export default {
name: "TopNavigation",
components: { GlobalSettingsModal, HelpModal },
mixins: [collector],
props: {
vehicleLogins: {
Expand All @@ -104,11 +114,10 @@ export default {
},
sponsor: String,
sponsorTokenExpires: Number,
batteryConfigured: Boolean,
smartCostType: String,
},
computed: {
globalSettingsModalProps: function () {
return this.collectProps(GlobalSettingsModal);
},
logoutCount() {
return this.providerLogins.filter((login) => !login.loggedIn).length;
},
Expand All @@ -126,6 +135,12 @@ export default {
showBadge() {
return this.loginRequired || this.sponsorTokenExpires;
},
batteryModalAvailable() {
return this.batteryConfigured;
},
gridModalAvailable: function () {
return gridModalAvailable(this.smartCostType);
},
},
mounted() {
const $el = document.getElementById("topNavigatonDropdown");
Expand Down Expand Up @@ -155,6 +170,16 @@ export default {
const modal = Modal.getOrCreateInstance(document.getElementById("helpModal"));
modal.show();
},
openBatterySettingsModal() {
const modal = Modal.getOrCreateInstance(
document.getElementById("batterySettingsModal")
);
modal.show();
},
openGridSettingsModal() {
const modal = Modal.getOrCreateInstance(document.getElementById("gridSettingsModal"));
modal.show();
},
},
};
</script>
Expand Down
7 changes: 6 additions & 1 deletion assets/js/mixins/collector.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
export default {
methods: {
// collect all target component properties from current instance
collectProps: function (component) {
collectProps: function (component, state) {
let data = {};
for (var p in component.props) {
// check in optional state
if (state && p in state) {
data[p] = state[p];
}
// check in current instance
if (p in this) {
data[p] = this[p];
}
Expand Down
5 changes: 5 additions & 0 deletions assets/js/utils/gridModalAvailable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { CO2_TYPE, PRICE_DYNAMIC_TYPE, PRICE_FORECAST_TYPE } from "../units";

export default function (smartCostType) {
return [CO2_TYPE, PRICE_DYNAMIC_TYPE, PRICE_FORECAST_TYPE].includes(smartCostType);
}
30 changes: 30 additions & 0 deletions assets/js/views/App.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
<template>
<div class="app">
<router-view :notifications="notifications" :offline="offline"></router-view>

<GlobalSettingsModal v-bind="globalSettingsProps" />
<BatterySettingsModal v-if="batteryModalAvailabe" v-bind="batterySettingsProps" />
<GridSettingsModal v-if="gridModalAvailable" v-bind="gridSettingsProps" />
<HelpModal />
</div>
</template>

<script>
import store from "../store";
import GlobalSettingsModal from "../components/GlobalSettingsModal.vue";
import BatterySettingsModal from "../components/BatterySettingsModal.vue";
import GridSettingsModal from "../components/GridSettingsModal.vue";
import HelpModal from "../components/HelpModal.vue";
import collector from "../mixins/collector";
import gridModalAvailable from "../utils/gridModalAvailable";
export default {
name: "App",
components: { GlobalSettingsModal, HelpModal, BatterySettingsModal, GridSettingsModal },
mixins: [collector],
props: {
notifications: Array,
offline: Boolean,
Expand All @@ -20,6 +33,23 @@ export default {
const siteTitle = store.state.siteTitle;
return { title: siteTitle ? `${siteTitle} | evcc` : "evcc" };
},
computed: {
gridModalAvailable: function () {
return gridModalAvailable(store.state.smartCostType);
},
batteryModalAvailabe: function () {
return store.state.batteryConfigured;
},
globalSettingsProps: function () {
return this.collectProps(GlobalSettingsModal, store.state);
},
batterySettingsProps() {
return this.collectProps(BatterySettingsModal, store.state);
},
gridSettingsProps() {
return this.collectProps(GridSettingsModal, store.state);
},
},
mounted: function () {
this.connect();
document.addEventListener("visibilitychange", this.pageVisibilityChanged, false);
Expand Down
3 changes: 1 addition & 2 deletions i18n/de.toml
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ github = "GitHub"
login = "Fahrzeug-Logins"
needHelp = "Hilfe benötigt?"
sessions = "Ladevorgänge"
settings = "Einstellungen"

[help]
discussionsButton = "GitHub Discussions"
Expand Down Expand Up @@ -384,7 +383,7 @@ allVehicles = "Alle Fahrzeuge"
filter = "Filtern"

[settings]
title = "Einstellungen"
title = "Allgemeine Einstellungen"

[settings.hiddenFeatures]
label = "Experimentell"
Expand Down
9 changes: 4 additions & 5 deletions i18n/en.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ legendTitle = "How should solar energy be used?"
legendTopAutostart = "starts automatically"
legendTopName = "battery supported charging"
legendTopSubline = "without interruptions"
modalTitle = "Battery settings"
modalTitle = "Battery Settings"

[batterySettings.bufferStart]
full = "when nearly full"
Expand Down Expand Up @@ -134,10 +134,9 @@ about = "About"
blog = "Blog"
docs = "Documentation"
github = "GitHub"
login = "Vehicle logins"
login = "Vehicle Logins"
needHelp = "Need help?"
sessions = "Charging sessions"
settings = "Settings"
sessions = "Charging Sessions"

[help]
discussionsButton = "GitHub discussions"
Expand Down Expand Up @@ -381,7 +380,7 @@ allVehicles = "all vehicles"
filter = "Filter"

[settings]
title = "Settings"
title = "General Settings"

[settings.hiddenFeatures]
label = "Experimental"
Expand Down
2 changes: 1 addition & 1 deletion tariff/fixed.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func NewFixedFromConfig(other map[string]interface{}) (api.Tariff, error) {

t := &Fixed{
clock: clock.New(),
dynamic: len(cc.Zones) > 1,
dynamic: len(cc.Zones) >= 1,
}

for _, z := range cc.Zones {
Expand Down
2 changes: 1 addition & 1 deletion tests/limits.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { test, expect } = require("@playwright/test");
const { start, stop, restart } = require("./evcc");
const { start, stop } = require("./evcc");
const { startSimulator, stopSimulator, SIMULATOR_URL } = require("./simulator");

const CONFIG = "simulator.evcc.yaml";
Expand Down
Loading

0 comments on commit 41a5dd7

Please sign in to comment.