Compare commits
2 commits
2ec8bb4303
...
a199f2465f
Author | SHA1 | Date | |
---|---|---|---|
|
a199f2465f | ||
|
95e65cd005 |
16 changed files with 194 additions and 170 deletions
|
@ -12,6 +12,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- The style has been entirely reworked using Bootstrap instead of Bulma
|
||||||
|
|
||||||
|
|
||||||
## [0.3.0] - 2023-08-25
|
## [0.3.0] - 2023-08-25
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -23,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- The local part cannot contain the separator
|
- The local part cannot contain the separator
|
||||||
- The HTML lang attribute is now set to the appropriate language
|
- The HTML lang attribute is now set to the appropriate language
|
||||||
|
|
||||||
|
|
||||||
## [0.2.0] - 2023-08-11
|
## [0.2.0] - 2023-08-11
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
# Sub-Address KEy (SAKE) app
|
# Sub-Address KEy (SAKE) app
|
||||||
|
|
||||||
Web application that can be used to generate new sub-addresses as defined in the [Sub-Address KEy (SAKE) filter](https://github.com/breard-r/opensmtpd-filter-sake).
|
Web application that can be used to generate new sub-addresses as defined in the [Sub-Address KEy (SAKE) filter](https://git.what.tf/rodolphe/opensmtpd-filter-sake).
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
Download the build from [the latest released version](https://github.com/breard-r/sake-app/releases). Extract the archive and configure your web server to serve those files.
|
Download the build from [the latest released version](https://git.what.tf/rodolphe/sake-app/releases). Extract the archive and configure your web server to serve those files.
|
||||||
|
|
||||||
That's it. The final build is plain HTML/CSS/JS with a few assets, therefore there is no back-end to configure.
|
That's it. The final build is plain HTML/CSS/JS with a few assets, therefore there is no back-end to configure.
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="utf-8">
|
||||||
<link rel="icon" href="favicon.ico">
|
<link rel="icon" href="favicon.ico">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Sub-Address KEy</title>
|
<title>Sub-Address KEy</title>
|
||||||
|
|
47
package-lock.json
generated
47
package-lock.json
generated
|
@ -10,9 +10,10 @@
|
||||||
"license": "(MIT OR Apache-2.0)",
|
"license": "(MIT OR Apache-2.0)",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/hashes": "^1.3.1",
|
"@noble/hashes": "^1.3.1",
|
||||||
|
"@popperjs/core": "^2.11.8",
|
||||||
"@vueuse/core": "^10.2.1",
|
"@vueuse/core": "^10.2.1",
|
||||||
"base32-encode": "^2.0.0",
|
"base32-encode": "^2.0.0",
|
||||||
"bulma": "^0.9.4",
|
"bootstrap": "^5.3.2",
|
||||||
"vue": "^3.3.4",
|
"vue": "^3.3.4",
|
||||||
"vue-i18n": "^9.2.2",
|
"vue-i18n": "^9.2.2",
|
||||||
"vue-qrcode-reader": "^5.1.0",
|
"vue-qrcode-reader": "^5.1.0",
|
||||||
|
@ -583,6 +584,15 @@
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@popperjs/core": {
|
||||||
|
"version": "2.11.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||||
|
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/popperjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/pluginutils": {
|
"node_modules/@rollup/pluginutils": {
|
||||||
"version": "5.0.4",
|
"version": "5.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.4.tgz",
|
||||||
|
@ -907,6 +917,24 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bootstrap": {
|
||||||
|
"version": "5.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.2.tgz",
|
||||||
|
"integrity": "sha512-D32nmNWiQHo94BKHLmOrdjlL05q1c8oxbtBphQFb9Z5to6eGRDCm0QgeaZ4zFBHzfg2++rqa2JkqCcxDy0sH0g==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/twbs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/bootstrap"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"peerDependencies": {
|
||||||
|
"@popperjs/core": "^2.11.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/braces": {
|
"node_modules/braces": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||||
|
@ -919,11 +947,6 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/bulma": {
|
|
||||||
"version": "0.9.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/bulma/-/bulma-0.9.4.tgz",
|
|
||||||
"integrity": "sha512-86FlT5+1GrsgKbPLRRY7cGDg8fsJiP/jzTqXXVqiUZZ2aZT8uemEOHlU1CDU+TxklPEZ11HZNNWclRBBecP4CQ=="
|
|
||||||
},
|
|
||||||
"node_modules/chokidar": {
|
"node_modules/chokidar": {
|
||||||
"version": "3.5.3",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||||
|
@ -1523,9 +1546,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/sass": {
|
"node_modules/sass": {
|
||||||
"version": "1.67.0",
|
"version": "1.68.0",
|
||||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.67.0.tgz",
|
"resolved": "https://registry.npmjs.org/sass/-/sass-1.68.0.tgz",
|
||||||
"integrity": "sha512-SVrO9ZeX/QQyEGtuZYCVxoeAL5vGlYjJ9p4i4HFuekWl8y/LtJ7tJc10Z+ck1c8xOuoBm2MYzcLfTAffD0pl/A==",
|
"integrity": "sha512-Lmj9lM/fef0nQswm1J2HJcEsBUba4wgNx2fea6yJHODREoMFnwRpZydBnX/RjyXw2REIwdkbqE4hrTo4qfDBUA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chokidar": ">=3.0.0 <4.0.0",
|
"chokidar": ">=3.0.0 <4.0.0",
|
||||||
|
@ -1733,9 +1756,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vue-router": {
|
"node_modules/vue-router": {
|
||||||
"version": "4.2.4",
|
"version": "4.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.2.5.tgz",
|
||||||
"integrity": "sha512-9PISkmaCO02OzPVOMq2w82ilty6+xJmQrarYZDkjZBfl4RvYAlt4PKnEX21oW4KTtWfa9OuO/b3qk1Od3AEdCQ==",
|
"integrity": "sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vue/devtools-api": "^6.5.0"
|
"@vue/devtools-api": "^6.5.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,9 +15,10 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/hashes": "^1.3.1",
|
"@noble/hashes": "^1.3.1",
|
||||||
|
"@popperjs/core": "^2.11.8",
|
||||||
"@vueuse/core": "^10.2.1",
|
"@vueuse/core": "^10.2.1",
|
||||||
"base32-encode": "^2.0.0",
|
"base32-encode": "^2.0.0",
|
||||||
"bulma": "^0.9.4",
|
"bootstrap": "^5.3.2",
|
||||||
"vue": "^3.3.4",
|
"vue": "^3.3.4",
|
||||||
"vue-i18n": "^9.2.2",
|
"vue-i18n": "^9.2.2",
|
||||||
"vue-qrcode-reader": "^5.1.0",
|
"vue-qrcode-reader": "^5.1.0",
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
@charset "utf-8"
|
@charset "utf-8"
|
||||||
|
|
||||||
@import "node_modules/bulma/bulma"
|
@import "variables.scss"
|
||||||
|
@import "bootstrap/scss/bootstrap"
|
||||||
a[href]
|
|
||||||
text-decoration: underline
|
|
||||||
|
|
||||||
.navbar-menu a[class~="navbar-item"]
|
|
||||||
text-decoration: none
|
|
||||||
|
|
7
src/assets/variables.scss
Normal file
7
src/assets/variables.scss
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
$container-max-widths: (
|
||||||
|
sm: 600px,
|
||||||
|
md: 700px,
|
||||||
|
lg: 800px,
|
||||||
|
xl: 820px,
|
||||||
|
xxl: 840px
|
||||||
|
);
|
5
src/components/ButtonGroupComponent.vue
Normal file
5
src/components/ButtonGroupComponent.vue
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<template>
|
||||||
|
<div class="d-grid gap-2 col-6 mx-auto">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -1,11 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<section class="section">
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="columns is-centered">
|
|
||||||
<div class="column is-three-fifths">
|
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,35 +1,34 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { RouterLink } from 'vue-router';
|
import { RouterLink } from 'vue-router';
|
||||||
|
import LayoutComponent from '../components/LayoutComponent.vue';
|
||||||
const menuActive = ref(false);
|
import { Popover } from 'bootstrap';
|
||||||
const toggleBurger = () => {
|
|
||||||
menuActive.value = !menuActive.value;
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
<div class="columns is-centered">
|
<div class="container-fluid">
|
||||||
<div class="column is-three-fifths">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNavBar" aria-controls="mainNavBar" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<nav class="navbar" role="navigation" aria-label="main navigation">
|
<span class="navbar-toggler-icon"></span>
|
||||||
<div class="navbar-brand">
|
</button>
|
||||||
<a role="button" class="navbar-burger" :class="{ 'is-active': menuActive }" aria-label="menu" aria-expanded="false" @click="toggleBurger">
|
<div class="collapse navbar-collapse" id="mainNavBar">
|
||||||
<span aria-hidden="true"></span>
|
<LayoutComponent>
|
||||||
<span aria-hidden="true"></span>
|
<ul class="navbar-nav justify-content-end flex-grow-1 pe-3">
|
||||||
<span aria-hidden="true"></span>
|
<li class="nav-item">
|
||||||
</a>
|
<RouterLink to="/add-account" class="nav-link">{{ $t("navbar.addAccount") }}</RouterLink>
|
||||||
</div>
|
</li>
|
||||||
<div class="navbar-menu" :class="{ 'is-active': menuActive }">
|
<li class="nav-item">
|
||||||
<div class="navbar-end">
|
<RouterLink to="/manage-accounts" class="nav-link">{{ $t("navbar.manageAccounts") }}</RouterLink>
|
||||||
<RouterLink to="/add-account" class="navbar-item">{{ $t("navbar.addAccount") }}</RouterLink>
|
</li>
|
||||||
<RouterLink to="/manage-accounts" class="navbar-item">{{ $t("navbar.manageAccounts") }}</RouterLink>
|
<li class="nav-item">
|
||||||
<RouterLink to="/config" class="navbar-item">{{ $t("navbar.config") }}</RouterLink>
|
<RouterLink to="/config" class="nav-link">{{ $t("navbar.config") }}</RouterLink>
|
||||||
<RouterLink to="/about" class="navbar-item">{{ $t("navbar.about") }}</RouterLink>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<RouterLink to="/about" class="nav-link">{{ $t("navbar.about") }}</RouterLink>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</LayoutComponent>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { version } from '../../package.json';
|
import { version } from '../../package.json';
|
||||||
|
import ButtonGroupComponent from '../components/ButtonGroupComponent.vue';
|
||||||
import LayoutComponent from '../components/LayoutComponent.vue';
|
import LayoutComponent from '../components/LayoutComponent.vue';
|
||||||
import ExternalLinkComponent from '../components/ExternalLinkComponent.vue';
|
import ExternalLinkComponent from '../components/ExternalLinkComponent.vue';
|
||||||
|
|
||||||
|
@ -14,9 +15,9 @@ const toMainView = () => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<LayoutComponent>
|
<LayoutComponent>
|
||||||
<h1 class="title is-1">{{ $t("about.title") }}</h1>
|
<h1>{{ $t("about.title") }}</h1>
|
||||||
<h4 class="subtitle is-4">{{ $t("about.name") }}</h4>
|
<h4>{{ $t("about.name") }}</h4>
|
||||||
<div class="block">
|
|
||||||
<i18n-t scope="global" keypath="about.version" tag="p">
|
<i18n-t scope="global" keypath="about.version" tag="p">
|
||||||
<template v-slot:version>{{ version }}</template>
|
<template v-slot:version>{{ version }}</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
|
@ -33,11 +34,9 @@ const toMainView = () => {
|
||||||
<ExternalLinkComponent :url="repoUrl" :name="repoUrl" />
|
<ExternalLinkComponent :url="repoUrl" :name="repoUrl" />
|
||||||
</template>
|
</template>
|
||||||
</i18n-t>
|
</i18n-t>
|
||||||
</div>
|
|
||||||
<div class="block">
|
<ButtonGroupComponent>
|
||||||
<div class="buttons is-centered">
|
<button type="button" class="btn btn-secondary" @click="toMainView">{{ $t("about.close") }}</button>
|
||||||
<button class="button is-light" @click="toMainView">{{ $t("about.close") }}</button>
|
</ButtonGroupComponent>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</LayoutComponent>
|
</LayoutComponent>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { useStorage } from '@vueuse/core';
|
||||||
import { QrcodeStream, setZXingModuleOverrides } from 'vue-qrcode-reader';
|
import { QrcodeStream, setZXingModuleOverrides } from 'vue-qrcode-reader';
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
import base32Encode from 'base32-encode';
|
import base32Encode from 'base32-encode';
|
||||||
|
import ButtonGroupComponent from '../components/ButtonGroupComponent.vue';
|
||||||
import LayoutComponent from '../components/LayoutComponent.vue';
|
import LayoutComponent from '../components/LayoutComponent.vue';
|
||||||
import wasmFile from "../../node_modules/@sec-ant/zxing-wasm/dist/reader/zxing_reader.wasm?url";
|
import wasmFile from "../../node_modules/@sec-ant/zxing-wasm/dist/reader/zxing_reader.wasm?url";
|
||||||
|
|
||||||
|
@ -131,43 +132,38 @@ const resetErrorMessage = () => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<LayoutComponent>
|
<LayoutComponent>
|
||||||
<h1 class="title is-1">{{ $t("addAccount.title") }}</h1>
|
<h1>{{ $t("addAccount.title") }}</h1>
|
||||||
<div class="notification is-danger is-light" v-if="errorMessageId">
|
|
||||||
<button class="delete" @click="resetErrorMessage"></button>
|
<div class="alert alert-danger alert-dismissible fade show" role="alert" v-if="errorMessageId">
|
||||||
{{ $t(errorMessageId) }}
|
{{ $t(errorMessageId) }}
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close" @click="resetErrorMessage"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="container" id="new-account-error-msg-container"></div>
|
|
||||||
<div class="field">
|
<div class="mb-3">
|
||||||
<label class="label" for="new-addr-local-part">{{ $t("addAccount.localPart") }}</label>
|
<label class="form-label" for="new-addr-local-part">{{ $t("addAccount.localPart") }}</label>
|
||||||
<div class="control">
|
<input class="form-control" type="text" id="new-addr-local-part" v-model="localPart">
|
||||||
<input class="input" type="text" id="new-addr-local-part" v-model="localPart">
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" for="new-addr-separator">{{ $t("addAccount.separator") }}</label>
|
||||||
|
<input class="form-control" type="text" id="new-addr-separator" v-model="separator">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" for="new-addr-domain">{{ $t("addAccount.domainName") }}</label>
|
||||||
|
<input class="form-control" type="text" id="new-addr-domain" placeholder="example.org" v-model="domainName">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" for="new-addr-key">{{ $t("addAccount.privateKey") }}</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input class="form-control" type="text" id="new-addr-key" v-model="privateKey">
|
||||||
|
<button class="btn btn-primary" type="button" @click="showQrCodeScanner">{{ $t("addAccount.scan") }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
|
||||||
<label class="label" for="new-addr-separator">{{ $t("addAccount.separator") }}</label>
|
|
||||||
<div class="control">
|
|
||||||
<input class="input" type="text" id="new-addr-separator" v-model="separator">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label class="label" for="new-addr-domain">{{ $t("addAccount.domainName") }}</label>
|
|
||||||
<div class="control">
|
|
||||||
<input class="input" type="text" id="new-addr-domain" placeholder="example.org" v-model="domainName">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<label class="label" for="new-addr-key">{{ $t("addAccount.privateKey") }}</label>
|
|
||||||
<div class="field has-addons">
|
|
||||||
<div class="control is-expanded">
|
|
||||||
<input class="input" type="text" id="new-addr-key" v-model="privateKey">
|
|
||||||
</div>
|
|
||||||
<p class="control">
|
|
||||||
<a class="button is-primary" @click="showQrCodeScanner">{{ $t("addAccount.scan") }}</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<qrcode-stream v-if="scanQrCode" @detect="onQrCodeDetected" @error="onQrCodeError"></qrcode-stream>
|
<qrcode-stream v-if="scanQrCode" @detect="onQrCodeDetected" @error="onQrCodeError"></qrcode-stream>
|
||||||
<div class="buttons is-centered">
|
|
||||||
<button class="button is-primary" :disabled="addDisabled" @click="addAccount">{{ $t("addAccount.addAccount") }}</button>
|
<ButtonGroupComponent>
|
||||||
<button class="button is-light" v-if="!cancellDisabled" @click="toMainView">{{ $t("addAccount.cancel") }}</button>
|
<button type="button" class="btn btn-primary" :disabled="addDisabled" @click="addAccount">{{ $t("addAccount.addAccount") }}</button>
|
||||||
</div>
|
<button type="button" class="btn btn-secondary" v-if="!cancellDisabled" @click="toMainView">{{ $t("addAccount.cancel") }}</button>
|
||||||
|
</ButtonGroupComponent>
|
||||||
</LayoutComponent>
|
</LayoutComponent>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { watch } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useStorage } from '@vueuse/core';
|
import { useStorage } from '@vueuse/core';
|
||||||
|
import ButtonGroupComponent from '../components/ButtonGroupComponent.vue';
|
||||||
import LayoutComponent from '../components/LayoutComponent.vue';
|
import LayoutComponent from '../components/LayoutComponent.vue';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -21,19 +22,17 @@ watch(locale, async (newLocale) => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<LayoutComponent>
|
<LayoutComponent>
|
||||||
<h1 class="title is-1">{{ $t("config.title") }}</h1>
|
<h1>{{ $t("config.title") }}</h1>
|
||||||
<div class="field">
|
|
||||||
<label class="label" for="app-language">{{ $t("config.language") }}</label>
|
<div class="mb-3">
|
||||||
<div class="control">
|
<label class="form-label" for="app-language">{{ $t("config.language") }}</label>
|
||||||
<div class="select is-fullwidth">
|
<select class="form-select" id="app-language" v-model="$i18n.locale">
|
||||||
<select id="app-language" v-model="$i18n.locale">
|
|
||||||
<option v-for="locale_id in $i18n.availableLocales" :key="`locale-${locale_id}`" :value="locale_id">{{ $t("locale_name", 1, { locale: locale_id}) }}</option>
|
<option v-for="locale_id in $i18n.availableLocales" :key="`locale-${locale_id}`" :value="locale_id">{{ $t("locale_name", 1, { locale: locale_id}) }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
<ButtonGroupComponent>
|
||||||
<div class="buttons is-centered">
|
<button type="button" class="btn btn-secondary" @click="toMainView">{{ $t("about.close") }}</button>
|
||||||
<button class="button is-light" @click="toMainView">{{ $t("config.close") }}</button>
|
</ButtonGroupComponent>
|
||||||
</div>
|
|
||||||
</LayoutComponent>
|
</LayoutComponent>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { useStorage } from '@vueuse/core';
|
import { useStorage } from '@vueuse/core';
|
||||||
|
import ButtonGroupComponent from '../components/ButtonGroupComponent.vue';
|
||||||
import LayoutComponent from '../components/LayoutComponent.vue';
|
import LayoutComponent from '../components/LayoutComponent.vue';
|
||||||
|
|
||||||
const accounts = useStorage('sake-accounts', []);
|
const accounts = useStorage('sake-accounts', []);
|
||||||
|
@ -19,13 +20,15 @@ const toMainView = () => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<LayoutComponent>
|
<LayoutComponent>
|
||||||
<h1 class="title is-1">{{ $t("deleteAccount.title") }}</h1>
|
<h1>{{ $t("deleteAccount.title") }}</h1>
|
||||||
|
|
||||||
<p>{{ $t("deleteAccount.account") }}</p>
|
<p>{{ $t("deleteAccount.account") }}</p>
|
||||||
<p class="has-text-weight-semibold is-size-5">{{ account.localPart }}@{{ account.domain }}</p>
|
<p class="has-text-weight-semibold is-size-5">{{ account.localPart }}@{{ account.domain }}</p>
|
||||||
<p>{{ $t("deleteAccount.confirm") }}</p>
|
<p>{{ $t("deleteAccount.confirm") }}</p>
|
||||||
<div class="buttons is-centered">
|
|
||||||
<button class="button is-danger" @click="deleteAccount">{{ $t("deleteAccount.delete") }}</button>
|
<ButtonGroupComponent>
|
||||||
<button class="button is-light" @click="toMainView">{{ $t("deleteAccount.cancel") }}</button>
|
<button type="button" class="btn btn-danger" @click="deleteAccount">{{ $t("deleteAccount.delete") }}</button>
|
||||||
</div>
|
<button type="button" class="btn btn-secondary" @click="toMainView">{{ $t("deleteAccount.cancel") }}</button>
|
||||||
|
</ButtonGroupComponent>
|
||||||
</LayoutComponent>
|
</LayoutComponent>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { useStorage } from '@vueuse/core';
|
||||||
import { hmac } from '@noble/hashes/hmac';
|
import { hmac } from '@noble/hashes/hmac';
|
||||||
import { sha256 } from '@noble/hashes/sha256';
|
import { sha256 } from '@noble/hashes/sha256';
|
||||||
import base32Encode from 'base32-encode';
|
import base32Encode from 'base32-encode';
|
||||||
|
import ButtonGroupComponent from '../components/ButtonGroupComponent.vue';
|
||||||
import LayoutComponent from '../components/LayoutComponent.vue';
|
import LayoutComponent from '../components/LayoutComponent.vue';
|
||||||
import NavBarComponent from '../components/NavBarComponent.vue';
|
import NavBarComponent from '../components/NavBarComponent.vue';
|
||||||
|
|
||||||
|
@ -52,32 +53,28 @@ const resetForm = () => {
|
||||||
<template>
|
<template>
|
||||||
<NavBarComponent />
|
<NavBarComponent />
|
||||||
<LayoutComponent>
|
<LayoutComponent>
|
||||||
<h1 class="title is-1">{{ $t("main.title") }}</h1>
|
<h1>{{ $t("main.title") }}</h1>
|
||||||
<div class="field">
|
|
||||||
<label class="label" for="account-name">{{ $t("main.account") }}</label>
|
<div class="mb-3">
|
||||||
<div class="control">
|
<label class="form-label" for="account-name">{{ $t("main.account") }}</label>
|
||||||
<div class="select is-fullwidth">
|
<select class="form-select" id="account-name" v-model="selectedAccountId">
|
||||||
<select id="account-name" v-model="selectedAccountId">
|
|
||||||
<option v-for="account in accounts" :key="account.id" :value="account.id">{{ account.localPart }}@{{ account.domain }}</option>
|
<option v-for="account in accounts" :key="account.id" :value="account.id">{{ account.localPart }}@{{ account.domain }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" for="sub-addr-name">{{ $t("main.name") }}</label>
|
||||||
|
<input class="form-control" type="text" id="sub-addr-name" :placeholder="$t('main.input')" v-model="subAddrName">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label" for="generated-addr">{{ $t("main.address") }}</label>
|
||||||
|
<input class="form-control" type="text" id="generated-addr" v-model="generatedAddr" disabled>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
|
||||||
<label class="label" for="sub-addr-name">{{ $t("main.name") }}</label>
|
<ButtonGroupComponent>
|
||||||
<div class="control">
|
<button type="button" class="btn btn-primary" @click="copyAddr">{{ $t("main.copy") }}</button>
|
||||||
<input class="input" type="text" id="sub-addr-name" :placeholder="$t('main.input')" v-model="subAddrName">
|
<button type="button" class="btn btn-secondary" @click="resetForm">{{ $t("main.reset") }}</button>
|
||||||
</div>
|
</ButtonGroupComponent>
|
||||||
</div>
|
|
||||||
<div class="field">
|
|
||||||
<label class="label" for="generated-addr">{{ $t("main.address") }}</label>
|
|
||||||
<div class="control">
|
|
||||||
<input class="input" type="text" id="generated-addr" v-model="generatedAddr" disabled>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="buttons is-centered">
|
|
||||||
<button class="button is-primary" @click="copyAddr">{{ $t("main.copy") }}</button>
|
|
||||||
<button class="button is-light" @click="resetForm">{{ $t("main.reset") }}</button>
|
|
||||||
</div>
|
|
||||||
</LayoutComponent>
|
</LayoutComponent>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import { sortAccounts } from '../accounts';
|
import { sortAccounts } from '../accounts';
|
||||||
import { RouterLink, useRouter } from 'vue-router';
|
import { RouterLink, useRouter } from 'vue-router';
|
||||||
import { useStorage } from '@vueuse/core';
|
import { useStorage } from '@vueuse/core';
|
||||||
|
import ButtonGroupComponent from '../components/ButtonGroupComponent.vue';
|
||||||
import LayoutComponent from '../components/LayoutComponent.vue';
|
import LayoutComponent from '../components/LayoutComponent.vue';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -17,25 +18,23 @@ const toMainView = () => {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<LayoutComponent>
|
<LayoutComponent>
|
||||||
<h1 class="title is-1">{{ $t("manageAccounts.title") }}</h1>
|
<h1>{{ $t("manageAccounts.title") }}</h1>
|
||||||
<div class="block">
|
|
||||||
<table class="table is-fullwidth">
|
<table class="table">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="account in accounts">
|
<tr v-for="account in accounts">
|
||||||
<th class="has-text-right is-vcentered">
|
<th class="text-end align-middle">
|
||||||
{{ account.localPart }}@{{ account.domain }}
|
{{ account.localPart }}@{{ account.domain }}
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
<button class="button is-danger" @click="deleteAccount(account.id)">{{ $t("manageAccounts.delete") }}</button>
|
<button type="button" class="btn btn-danger" @click="deleteAccount(account.id)">{{ $t("manageAccounts.delete") }}</button>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
|
||||||
<div class="block">
|
<ButtonGroupComponent>
|
||||||
<div class="buttons is-centered">
|
<button type="button" class="btn btn-secondary" @click="toMainView">{{ $t("manageAccounts.close") }}</button>
|
||||||
<button class="button is-light" @click="toMainView">{{ $t("manageAccounts.close") }}</button>
|
</ButtonGroupComponent>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</LayoutComponent>
|
</LayoutComponent>
|
||||||
</template>
|
</template>
|
||||||
|
|
Loading…
Reference in a new issue