diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 000000000..8921a8121
Binary files /dev/null and b/.DS_Store differ
diff --git a/.eslintrc.js b/.eslintrc.js
index 0ec89fa61..492a94d8a 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -6,9 +6,10 @@ module.exports = {
},
extends: [
"eslint:recommended",
- "plugin:vue/recommended",
+ "plugin:vue/vue3-recommended",
"plugin:prettier/recommended",
"prettier",
+ "plugin:storybook/recommended",
],
rules: {
"vue/require-default-prop": "off",
diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml
index 6580b1680..d03df648d 100644
--- a/.github/workflows/default.yml
+++ b/.github/workflows/default.yml
@@ -72,6 +72,11 @@ jobs:
runs-on: ubuntu-latest
steps:
+ - name: Set up Node
+ uses: actions/setup-node@v2
+ with:
+ node-version: "16"
+
- name: Cache NPM modules
uses: actions/cache@v2
with:
diff --git a/.gitignore b/.gitignore
index b0806936c..6c051daf4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,4 @@ ca-cert.srl
server-key.pem
ca-key.pem
cmd/wip
+fly.toml
\ No newline at end of file
diff --git a/.storybook/main.js b/.storybook/main.js
index 08826898c..ddef68bcf 100644
--- a/.storybook/main.js
+++ b/.storybook/main.js
@@ -1,4 +1,23 @@
+const { loadConfigFromFile } = require("vite");
+const path = require("path");
+
module.exports = {
stories: ["../assets/js/**/*.stories.mdx", "../assets/js/**/*.stories.@(js|jsx|ts|tsx)"],
addons: ["@storybook/addon-links", "@storybook/addon-essentials", "@storybook/addon-postcss"],
+ framework: "@storybook/vue3",
+ core: {
+ builder: "storybook-builder-vite",
+ },
+ staticDirs: ["../assets"],
+ async viteFinal(config, b) {
+ const { config: userConfig } = await loadConfigFromFile(
+ path.resolve(__dirname, "../vite.config.ts")
+ );
+ const vuePlugin = userConfig.plugins.find((p) => p.name === "vite:vue");
+
+ const vuePluginIndex = config.plugins.findIndex((p) => p.name === "vite:vue");
+ config.plugins[vuePluginIndex] = vuePlugin;
+
+ return config;
+ },
};
diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html
deleted file mode 100644
index 7f0b8485c..000000000
--- a/.storybook/preview-head.html
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/.storybook/preview.js b/.storybook/preview.js
index 5d00c0212..bfc6cb9f2 100644
--- a/.storybook/preview.js
+++ b/.storybook/preview.js
@@ -1,4 +1,26 @@
+import "bootstrap/dist/css/bootstrap.min.css";
+import "bootstrap";
+import smoothscroll from "smoothscroll-polyfill";
+import { app } from "@storybook/vue3";
+import i18n from "../assets/js/i18n";
+import "../assets/css/app.css";
+
+smoothscroll.polyfill();
+app.use(i18n);
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
-}
\ No newline at end of file
+ backgrounds: {
+ default: "dark",
+ values: [
+ {
+ name: "light",
+ value: "#ffffff",
+ },
+ {
+ name: "dark",
+ value: "#28293e",
+ },
+ ],
+ },
+};
diff --git a/Dockerfile b/Dockerfile
index 52799c35a..b06943cb8 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -12,7 +12,7 @@ RUN make install-ui
# build ui
COPY assets assets
-COPY vue.config.js vue.config.js
+COPY vite.config.js vite.config.js
COPY .eslintrc.js .eslintrc.js
RUN make clean ui
diff --git a/README.md b/README.md
index eded9c338..f8244ed11 100644
--- a/README.md
+++ b/README.md
@@ -41,14 +41,51 @@ Evcc is an extensible EV Charge Controller with PV integration implemented in [G
You'll find everything you need in our [documentation](https://docs.evcc.io/) (German).
-## Build
+## Contribute
-To build EVCC from source, [Go][2] 1.18 and [Node][3] 16 are required:
+To build evcc from source, [Go][2] 1.18 and [Node][3] 16 are required.
+
+Build and run go backend. The UI becomes available at http://127.0.0.1:7070/
```sh
make
+./evcc
```
+For frontend development start the Vue toolchain in dev-mode. Open http://127.0.0.1:7071/ to get to the livelreloading development server. It pulls its data from port 7070 (see above).
+
+```sh
+npm install
+npm run start
+```
+
+### Code formatting
+
+We use linters (golangci-lint, Prettier) to keep a coherent source code formatting. It's recommended to use the format-on-save feature of your editor. For VSCode use the [Go](https://marketplace.visualstudio.com/items?itemName=golang.Go), [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) and [Veture](https://marketplace.visualstudio.com/items?itemName=octref.vetur) extension. You can manually reformat your code by running:
+
+```sh
+make lint
+make lint-ui
+```
+
+### Changing UI code
+
+To ensure reproducability the build frontend artifacts are part of the source code repository. If you've made changes to frontend code, please make sure to rebuild the production assets before you commit.
+
+```sh
+make ui
+```
+
+### Changing templates
+
+Evcc supports a massive amount of different devices. To keep our documentation and website in sync with the latest software the core project (this repo) generates meta-data that's pushed to the `docs` and `evcc.io` repository. Make sure to update this meta-data every time you make changes to a templates.
+
+```sh
+make docs
+```
+
+If you miss one of the above steps Gitub Actions will likely trigger a **Porcelain** error.
+
## Sponsorship
Evcc believes in open source software. We're committed to provide best in class EV charging experience.
diff --git a/assets/.DS_Store b/assets/.DS_Store
new file mode 100644
index 000000000..6808ad310
Binary files /dev/null and b/assets/.DS_Store differ
diff --git a/assets/css/app.css b/assets/css/app.css
index 6d357dec6..5c6faa575 100644
--- a/assets/css/app.css
+++ b/assets/css/app.css
@@ -1,14 +1,67 @@
+@font-face {
+ font-family: "Montserrat";
+ src: url("../font/Montserrat-Medium.woff2") format("woff2");
+ font-weight: normal;
+ font-style: normal;
+}
+@font-face {
+ font-family: "Montserrat";
+ src: url("../font/Montserrat-Bold.woff2") format("woff2");
+ font-weight: bold;
+ font-style: normal;
+}
+
:root {
- --evcc-green: #66d85a;
- --evcc-dark-green: #3aba2c;
- --evcc-yellow: #ffe000;
+ --evcc-green: #baffcb;
+ --evcc-dark-green: #0fde41;
+ --evcc-yellow: #faf000;
+ --bs-gray-dark: #28293e;
+ --bs-gray-medium: #93949e;
--evcc-grid: var(--bs-gray-dark);
--evcc-self: var(--evcc-dark-green);
--evcc-export: var(--evcc-yellow);
+ --evcc-transition-slow: 1000ms;
+ --evcc-transition-medium: 500ms;
+ --evcc-transition-fast: 250ms;
+ --evcc-transition-very-fast: 100ms;
+
--bs-primary: var(--evcc-dark-green);
--bs-primary-rgb: 58, 186, 44;
+
+ --bs-body-font-size: 14px;
+ --bs-font-sans-serif: Montserrat, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
+ Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
+ "Segoe UI Symbol", "Noto Color Emoji";
+}
+
+html {
+ /* prevents content jump when scrollbar is activate */
+ width: 100vw;
+ overflow-x: hidden;
+}
+
+body {
+ background-color: var(--evcc-gray-dark);
+}
+
+h1,
+h2,
+h3,
+h4 {
+ font-weight: bold;
+}
+
+h1,
+h2 {
+ font-size: 1.25rem;
+ text-transform: uppercase;
+}
+
+h3,
+h4 {
+ font-size: 1.25rem;
}
.bg-primary {
@@ -27,22 +80,33 @@ a {
opacity: 0.25;
}
+.rounded {
+ border-radius: 10px !important;
+}
+
.btn-primary,
.btn-primary:focus {
background-color: var(--bs-gray-dark);
border-color: var(--bs-gray-dark);
}
+
.btn-primary:hover,
.btn-primary:active {
background-color: var(--evcc-dark-green);
border-color: var(--evcc-dark-green);
}
-.btn-outline-primary {
+.btn-outline-primary,
+.btn-outline-primary:focus {
color: var(--bs-gray-dark);
+ background-color: transparent;
+ border-width: 2px;
border-color: var(--bs-gray-dark);
}
-.btn-outline-primary:hover {
- background-color: var(--evcc-dark-green);
+.btn-outline-primary:hover,
+.btn-outline-primary:active {
+ color: var(--evcc-dark-green);
+ background-color: transparent;
+ border-width: 2px;
border-color: var(--evcc-dark-green);
}
.text-evcc {
@@ -52,6 +116,18 @@ a {
.text-grid {
color: var(--evcc-grid);
}
+.text-dark {
+ color: var(--bs-gray-dark);
+}
+.text-gray {
+ color: var(--bs-gray-medium);
+}
+.text-gray-medium {
+ color: var(--bs-gray-medium);
+}
+.bg-dark {
+ background-color: var(--bs-gray-dark) !important;
+}
@media screen and (min-width: 400px) and (max-width: 574px) {
.d-xs-none {
@@ -62,6 +138,33 @@ a {
}
}
+.modal-backdrop.show {
+ opacity: 0.8;
+}
+
+.modal-header {
+ padding-bottom: 1.5rem;
+}
+
+.modal-title {
+ font-weight: bold;
+ font-size: 22px;
+}
+
+.modal-content {
+ border-radius: 1rem;
+ padding: 1rem;
+}
+
+.modal-body {
+ padding-top: 1.5rem;
+ padding-bottom: 1.5rem;
+}
+
+.modal-footer {
+ padding-top: 1.5rem;
+}
+
.cursor-pointer {
cursor: pointer;
}
@@ -69,3 +172,13 @@ a {
.v-popper__inner {
margin: 0 12px !important;
}
+
+.small,
+small {
+ font-size: var(--bs-body-font-size);
+ color: var(--bs-gray-medium) !important;
+}
+
+.btn-close {
+ opacity: 1;
+}
diff --git a/assets/font/Montserrat-Bold.woff2 b/assets/font/Montserrat-Bold.woff2
new file mode 100644
index 000000000..a75e620ac
Binary files /dev/null and b/assets/font/Montserrat-Bold.woff2 differ
diff --git a/assets/font/Montserrat-Medium.woff2 b/assets/font/Montserrat-Medium.woff2
new file mode 100644
index 000000000..11098b7df
Binary files /dev/null and b/assets/font/Montserrat-Medium.woff2 differ
diff --git a/assets/ico/browserconfig.xml b/assets/ico/browserconfig.xml
deleted file mode 100644
index bc5d0fe32..000000000
--- a/assets/ico/browserconfig.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-