Updated API Keys UI
The UI has now been moved to the settings page. Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com>
This commit is contained in:
parent
06ee68dc0e
commit
11fa690e09
|
@ -0,0 +1,214 @@
|
||||||
|
<template>
|
||||||
|
<form @submit.prevent="submit">
|
||||||
|
<div ref="keyaddmodal" class="modal fade" tabindex="-1" data-bs-backdrop="static">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">
|
||||||
|
{{ $t("Add API Key") }}
|
||||||
|
</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" />
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<!-- Name -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="name" class="form-label">{{ $t("Name") }}</label>
|
||||||
|
<input
|
||||||
|
id="name" v-model="key.name" type="text" class="form-control"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Expiry -->
|
||||||
|
<div class="my-3">
|
||||||
|
<label class="form-label">{{ $t("Expiry date") }}</label>
|
||||||
|
<Datepicker
|
||||||
|
v-model="key.expires"
|
||||||
|
:dark="$root.isDark"
|
||||||
|
:monthChangeOnScroll="false"
|
||||||
|
:minDate="minDate"
|
||||||
|
format="yyyy-MM-dd HH:mm"
|
||||||
|
modelType="yyyy-MM-dd HH:mm:ss"
|
||||||
|
:required="!noExpire"
|
||||||
|
:disabled="noExpire"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input
|
||||||
|
id="no-expire" v-model="noExpire" class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
>
|
||||||
|
<label class="form-check-label" for="no-expire">{{
|
||||||
|
$t("Don't expire")
|
||||||
|
}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button
|
||||||
|
id="monitor-submit-btn" class="btn btn-primary" type="submit"
|
||||||
|
:disabled="processing"
|
||||||
|
>
|
||||||
|
{{ $t("Generate") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ref="keymodal" class="modal fade" tabindex="-1" data-bs-backdrop="static">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">
|
||||||
|
{{ $t("Key Added") }}
|
||||||
|
</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
{{ $t("apiKeyAddedMsg") }}
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<CopyableInput v-model="clearKey" disabled="disabled" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">
|
||||||
|
{{ $t('Continue') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Modal } from "bootstrap";
|
||||||
|
import { useToast } from "vue-toastification";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import Datepicker from "@vuepic/vue-datepicker";
|
||||||
|
import CopyableInput from "./CopyableInput.vue";
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
CopyableInput,
|
||||||
|
Datepicker
|
||||||
|
},
|
||||||
|
props: {},
|
||||||
|
// emits: [ "added" ],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
keyaddmodal: null,
|
||||||
|
keymodal: null,
|
||||||
|
processing: false,
|
||||||
|
key: {},
|
||||||
|
dark: (this.$root.theme === "dark"),
|
||||||
|
minDate: this.$root.date(dayjs()) + " 00:00",
|
||||||
|
clearKey: null,
|
||||||
|
noExpire: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.keyaddmodal = new Modal(this.$refs.keyaddmodal);
|
||||||
|
this.keymodal = new Modal(this.$refs.keymodal);
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Show modal
|
||||||
|
*/
|
||||||
|
show() {
|
||||||
|
this.id = null;
|
||||||
|
this.key = {
|
||||||
|
name: "",
|
||||||
|
expires: this.minDate,
|
||||||
|
active: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.keyaddmodal.show();
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Submit data to server */
|
||||||
|
async submit() {
|
||||||
|
this.processing = true;
|
||||||
|
|
||||||
|
if (this.noExpire) {
|
||||||
|
this.key.expires = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$root.addAPIKey(this.key, async (res) => {
|
||||||
|
this.keyaddmodal.hide();
|
||||||
|
if (res.ok) {
|
||||||
|
this.clearKey = res.key;
|
||||||
|
this.keymodal.show();
|
||||||
|
this.clearForm();
|
||||||
|
} else {
|
||||||
|
toast.error(res.msg);
|
||||||
|
}
|
||||||
|
this.processing = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "../assets/vars.scss";
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
.modal-dialog .form-text, .modal-dialog p {
|
||||||
|
color: $dark-font-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadow-box {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-calendar::-webkit-calendar-picker-indicator {
|
||||||
|
filter: invert(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.weekday-picker {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 40px;
|
||||||
|
|
||||||
|
.form-check-inline {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.day-picker {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 40px;
|
||||||
|
|
||||||
|
.form-check-inline {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,17 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<transition name="slide-fade" appear>
|
|
||||||
<div>
|
<div>
|
||||||
<h1 class="mb-3">
|
<div class="add-btn">
|
||||||
{{ $t("API Keys") }}
|
<button class="btn btn-primary me-2" type="button" @click="$refs.apiKeyDialog.show()">
|
||||||
</h1>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<router-link to="/apikeys/add" class="btn btn-primary mb-3">
|
|
||||||
<font-awesome-icon icon="plus" /> {{ $t("Add API Key") }}
|
<font-awesome-icon icon="plus" /> {{ $t("Add API Key") }}
|
||||||
</router-link>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="shadow-box">
|
<div>
|
||||||
<span v-if="Object.keys(keyList).length === 0" class="d-flex align-items-center justify-content-center my-3">
|
<span v-if="Object.keys(keyList).length === 0" class="d-flex align-items-center justify-content-center my-3">
|
||||||
{{ $t("No API Keys") }}
|
{{ $t("No API Keys") }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -69,17 +64,20 @@
|
||||||
<Confirm ref="confirmDelete" btn-style="btn-danger" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="deleteKey">
|
<Confirm ref="confirmDelete" btn-style="btn-danger" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="deleteKey">
|
||||||
{{ $t("deleteAPIKeyMsg") }}
|
{{ $t("deleteAPIKeyMsg") }}
|
||||||
</Confirm>
|
</Confirm>
|
||||||
|
|
||||||
|
<APIKeyDialog ref="apiKeyDialog" />
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Confirm from "../components/Confirm.vue";
|
import APIKeyDialog from "../../components/APIKeyDialog.vue";
|
||||||
|
import Confirm from "../Confirm.vue";
|
||||||
import { useToast } from "vue-toastification";
|
import { useToast } from "vue-toastification";
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
APIKeyDialog,
|
||||||
Confirm,
|
Confirm,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
@ -111,7 +109,6 @@ export default {
|
||||||
this.$root.deleteAPIKey(this.selectedKeyID, (res) => {
|
this.$root.deleteAPIKey(this.selectedKeyID, (res) => {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
toast.success(res.msg);
|
toast.success(res.msg);
|
||||||
this.$router.push("/apikeys");
|
|
||||||
} else {
|
} else {
|
||||||
toast.error(res.msg);
|
toast.error(res.msg);
|
||||||
}
|
}
|
||||||
|
@ -148,7 +145,7 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "../assets/vars.scss";
|
@import "../../assets/vars.scss";
|
||||||
|
|
||||||
.mobile {
|
.mobile {
|
||||||
.item {
|
.item {
|
||||||
|
@ -158,6 +155,10 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.add-btn {
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
|
@ -44,7 +44,6 @@ import {
|
||||||
faWrench,
|
faWrench,
|
||||||
faHeartbeat,
|
faHeartbeat,
|
||||||
faFilter,
|
faFilter,
|
||||||
faKey,
|
|
||||||
faInfoCircle,
|
faInfoCircle,
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
|
@ -90,7 +89,6 @@ library.add(
|
||||||
faWrench,
|
faWrench,
|
||||||
faHeartbeat,
|
faHeartbeat,
|
||||||
faFilter,
|
faFilter,
|
||||||
faKey,
|
|
||||||
faInfoCircle,
|
faInfoCircle,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -57,12 +57,6 @@
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>
|
|
||||||
<router-link to="/apikeys" class="dropdown-item" :class="{ active: $route.path.includes('manage-apikeys') }">
|
|
||||||
<font-awesome-icon icon="key" /> {{ $t("API Keys") }}
|
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<router-link to="/settings/general" class="dropdown-item" :class="{ active: $route.path.includes('settings') }">
|
<router-link to="/settings/general" class="dropdown-item" :class="{ active: $route.path.includes('settings') }">
|
||||||
<font-awesome-icon icon="cog" /> {{ $t("Settings") }}
|
<font-awesome-icon icon="cog" /> {{ $t("Settings") }}
|
||||||
|
|
|
@ -1,199 +0,0 @@
|
||||||
<template>
|
|
||||||
<transition name="slide-fade" appear>
|
|
||||||
<div>
|
|
||||||
<h1 class="mb-3">{{ $t("Add API Key") }}</h1>
|
|
||||||
<form @submit.prevent="submit">
|
|
||||||
<div class="shadow-box">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xl-10">
|
|
||||||
<!-- Title -->
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="name" class="form-label">{{ $t("Name") }}</label>
|
|
||||||
<input
|
|
||||||
id="name" v-model="key.name" type="text" class="form-control"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2 class="mt-5">{{ $t("Expiry") }}</h2>
|
|
||||||
|
|
||||||
<!-- Expiry -->
|
|
||||||
<div class="my-3">
|
|
||||||
<label class="form-label">{{ $t("Expiry date") }}</label>
|
|
||||||
<Datepicker
|
|
||||||
v-model="key.expires"
|
|
||||||
:dark="$root.isDark"
|
|
||||||
:monthChangeOnScroll="false"
|
|
||||||
:minDate="minDate"
|
|
||||||
format="yyyy-MM-dd HH:mm"
|
|
||||||
modelType="yyyy-MM-dd HH:mm:ss"
|
|
||||||
:required="!noExpire"
|
|
||||||
:disabled="noExpire"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="form-check mb-2">
|
|
||||||
<input
|
|
||||||
id="no-expire" v-model="noExpire" class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
>
|
|
||||||
<label class="form-check-label" for="no-expire">{{
|
|
||||||
$t("Don't expire")
|
|
||||||
}}</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-4 mb-1">
|
|
||||||
<button
|
|
||||||
id="monitor-submit-btn" class="btn btn-primary" type="submit"
|
|
||||||
:disabled="processing"
|
|
||||||
>
|
|
||||||
{{ $t("Generate") }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<Confirm
|
|
||||||
ref="keyAdded"
|
|
||||||
:yes-text="$t('Continue')"
|
|
||||||
:no-text="$t('Add Another')"
|
|
||||||
:title="$t('Key Added')"
|
|
||||||
@yes="postAdd"
|
|
||||||
@no="clearForm"
|
|
||||||
>
|
|
||||||
<p>{{ $t("apiKeyAddedMsg") }}</p>
|
|
||||||
<p>{{ clearKey }}</p>
|
|
||||||
</Confirm>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
import { useToast } from "vue-toastification";
|
|
||||||
import dayjs from "dayjs";
|
|
||||||
import Datepicker from "@vuepic/vue-datepicker";
|
|
||||||
import Confirm from "../components/Confirm.vue";
|
|
||||||
|
|
||||||
const toast = useToast();
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Confirm,
|
|
||||||
Datepicker
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
processing: false,
|
|
||||||
key: {},
|
|
||||||
dark: (this.$root.theme === "dark"),
|
|
||||||
minDate: this.$root.date(dayjs()) + " 00:00",
|
|
||||||
clearKey: null,
|
|
||||||
noExpire: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
"$route.fullPath"() {
|
|
||||||
this.init();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.init();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
/** Initialise page */
|
|
||||||
init() {
|
|
||||||
this.clearForm();
|
|
||||||
},
|
|
||||||
|
|
||||||
/** Redirect user to apikey list */
|
|
||||||
postAdd() {
|
|
||||||
this.$router.push("/apikeys");
|
|
||||||
},
|
|
||||||
|
|
||||||
/** Clear the form */
|
|
||||||
clearForm() {
|
|
||||||
this.key = {
|
|
||||||
name: "",
|
|
||||||
expires: this.minDate,
|
|
||||||
active: 1,
|
|
||||||
};
|
|
||||||
this.noExpire = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
/** Submit data to server */
|
|
||||||
async submit() {
|
|
||||||
this.processing = true;
|
|
||||||
|
|
||||||
if (this.noExpire) {
|
|
||||||
this.key.expires = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$root.addAPIKey(this.key, async (res) => {
|
|
||||||
if (res.ok) {
|
|
||||||
this.clearKey = res.key;
|
|
||||||
this.$refs.keyAdded.show();
|
|
||||||
this.clearForm();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
toast.error(res.msg);
|
|
||||||
}
|
|
||||||
this.processing = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.shadow-box {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
min-height: 150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark-calendar::-webkit-calendar-picker-indicator {
|
|
||||||
filter: invert(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.weekday-picker {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
|
|
||||||
& > div {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
width: 40px;
|
|
||||||
|
|
||||||
.form-check-inline {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.day-picker {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
& > div {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
width: 40px;
|
|
||||||
|
|
||||||
.form-check-inline {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
|
@ -7,9 +7,6 @@
|
||||||
<router-link to="/maintenance" class="nav-link">
|
<router-link to="/maintenance" class="nav-link">
|
||||||
<font-awesome-icon icon="wrench" /> {{ $t("Maintenance") }}
|
<font-awesome-icon icon="wrench" /> {{ $t("Maintenance") }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link to="/apikeys" class="nav-link" :class="{ active: $route.path.includes('manage-apikeys') }">
|
|
||||||
<font-awesome-icon icon="key" /> {{ $t("API Keys") }}
|
|
||||||
</router-link>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 v-show="show" class="mb-3">
|
<h1 v-show="show" class="mb-3">
|
||||||
|
@ -110,6 +107,9 @@ export default {
|
||||||
security: {
|
security: {
|
||||||
title: this.$t("Security"),
|
title: this.$t("Security"),
|
||||||
},
|
},
|
||||||
|
"api-keys": {
|
||||||
|
title: this.$t("API Keys")
|
||||||
|
},
|
||||||
proxies: {
|
proxies: {
|
||||||
title: this.$t("Proxies"),
|
title: this.$t("Proxies"),
|
||||||
},
|
},
|
||||||
|
|
|
@ -18,8 +18,7 @@ import NotFound from "./pages/NotFound.vue";
|
||||||
import DockerHosts from "./components/settings/Docker.vue";
|
import DockerHosts from "./components/settings/Docker.vue";
|
||||||
import MaintenanceDetails from "./pages/MaintenanceDetails.vue";
|
import MaintenanceDetails from "./pages/MaintenanceDetails.vue";
|
||||||
import ManageMaintenance from "./pages/ManageMaintenance.vue";
|
import ManageMaintenance from "./pages/ManageMaintenance.vue";
|
||||||
import ManageAPIKeys from "./pages/ManageAPIKeys.vue";
|
import APIKeys from "./components/settings/APIKeys.vue";
|
||||||
import AddAPIKey from "./pages/AddAPIKey.vue";
|
|
||||||
import Plugins from "./components/settings/Plugins.vue";
|
import Plugins from "./components/settings/Plugins.vue";
|
||||||
|
|
||||||
// Settings - Sub Pages
|
// Settings - Sub Pages
|
||||||
|
@ -115,6 +114,10 @@ const routes = [
|
||||||
path: "security",
|
path: "security",
|
||||||
component: Security,
|
component: Security,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "api-keys",
|
||||||
|
component: APIKeys,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "proxies",
|
path: "proxies",
|
||||||
component: Proxies,
|
component: Proxies,
|
||||||
|
@ -157,14 +160,6 @@ const routes = [
|
||||||
path: "/maintenance/edit/:id",
|
path: "/maintenance/edit/:id",
|
||||||
component: EditMaintenance,
|
component: EditMaintenance,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "/apikeys",
|
|
||||||
component: ManageAPIKeys
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/apikeys/add",
|
|
||||||
component: AddAPIKey
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in New Issue