backup-functionality to dashboard

This commit is contained in:
hay-kot 2021-05-04 11:30:42 -08:00
commit ad71ab6603
10 changed files with 176 additions and 295 deletions

View file

@ -1,8 +1,8 @@
<template>
<div>
<v-checkbox
v-for="option in options"
:key="option.text"
v-for="(option, index) in options"
:key="index"
class="mb-n4 mt-n3"
dense
:label="option.text"
@ -61,6 +61,4 @@ export default {
},
},
};
</script>
<style lang="scss" scoped></style>
</script>

View file

@ -0,0 +1,142 @@
<template>
<div>
<BaseDialog
:title="$t('settings.backup.create-heading')"
titleIcon="mdi-database"
@submit="createBackup"
:submit-text="$t('general.create')"
:loading="loading"
>
<template v-slot:open="{ open }">
<v-btn @click="open" class="mx-2" small :color="color"> <v-icon left> mdi-plus </v-icon> Custom </v-btn>
</template>
<v-card-text class="mt-6">
<v-text-field dense :label="$t('settings.backup.backup-tag')" v-model="tag"></v-text-field>
</v-card-text>
<v-card-actions class="mt-n9 flex-wrap">
<v-switch v-model="fullBackup" :label="switchLabel"></v-switch>
<v-spacer></v-spacer>
</v-card-actions>
<v-expand-transition>
<div v-if="!fullBackup">
<v-card-text class="mt-n4">
<v-row>
<v-col sm="4">
<p>{{ $t("general.options") }}</p>
<ImportOptions @update-options="updateOptions" class="mt-5" />
</v-col>
<v-col>
<p>{{ $t("general.templates") }}</p>
<v-checkbox
v-for="template in availableTemplates"
:key="template"
class="mb-n4 mt-n3"
dense
:label="template"
@click="appendTemplate(template)"
></v-checkbox>
</v-col>
</v-row>
</v-card-text>
</div>
</v-expand-transition>
</BaseDialog>
</div>
</template>
<script>
import BaseDialog from "./BaseDialog";
import ImportOptions from "@/components/FormHelpers/ImportOptions";
import { api } from "@/api";
export default {
props: {
color: { default: "primary" },
},
components: {
BaseDialog,
ImportOptions,
},
data() {
return {
tag: null,
fullBackup: true,
loading: false,
options: {
recipes: true,
settings: true,
themes: true,
pages: true,
users: true,
groups: true,
},
availableTemplates: [],
selectedTemplates: [],
};
},
computed: {
switchLabel() {
if (this.fullBackup) {
return this.$t("settings.backup.full-backup");
} else return this.$t("settings.backup.partial-backup");
},
},
mounted() {
this.resetData();
this.getAvailableBackups();
},
methods: {
resetData() {
this.tag = null;
this.fullBackup = true;
this.loading = false;
this.options = {
recipes: true,
settings: true,
themes: true,
pages: true,
users: true,
groups: true,
};
this.availableTemplates = [];
this.selectedTemplates = [];
},
updateOptions(options) {
this.options = options;
},
async getAvailableBackups() {
const response = await api.backups.requestAvailable();
response.templates.forEach(element => {
this.availableTemplates.push(element);
});
},
async createBackup() {
this.loading = true;
const data = {
tag: this.tag,
options: {
recipes: this.options.recipes,
settings: this.options.settings,
pages: this.options.pages,
themes: this.options.themes,
users: this.options.users,
groups: this.options.groups,
},
templates: this.selectedTemplates,
};
if (await api.backups.create(data)) {
this.$emit("created");
}
this.loading = false;
},
appendTemplate(templateName) {
if (this.selectedTemplates.includes(templateName)) {
let index = this.selectedTemplates.indexOf(templateName);
if (index !== -1) {
this.selectedTemplates.splice(index, 1);
}
} else this.selectedTemplates.push(templateName);
},
},
};
</script>

View file

@ -3,14 +3,14 @@
<slot name="open" v-bind="{ open }"> </slot>
<v-dialog v-model="dialog" :width="modalWidth + 'px'" :content-class="top ? 'top-dialog' : undefined">
<v-card class="pb-10" height="100%">
<v-app-bar dark :color="color" class="mt-n1 mb-2">
<v-app-bar dark :color="color" class="mt-n1 mb-0">
<v-icon large left>
{{ titleIcon }}
</v-icon>
<v-toolbar-title class="headline"> {{ title }} </v-toolbar-title>
<v-spacer></v-spacer>
</v-app-bar>
<v-progress-linear v-if="loading" indeterminate color="primary"></v-progress-linear>
<v-progress-linear class="mt-1" v-if="loading" indeterminate color="primary"></v-progress-linear>
<slot> </slot>
<v-card-actions>
<slot name="card-actions">
@ -19,7 +19,7 @@
</v-btn>
<v-spacer></v-spacer>
<v-btn color="success" @click="submitEvent">
{{ $t("general.submit") }}
{{ submitText }}
</v-btn>
</slot>
</v-card-actions>
@ -31,6 +31,7 @@
</template>
<script>
import i18n from "@/i18n";
export default {
props: {
color: {
@ -51,16 +52,34 @@ export default {
top: {
default: false,
},
submitText: {
default: () => i18n.t("general.create"),
},
},
data() {
return {
dialog: false,
submitted: false,
};
},
computed: {
determineClose() {
return this.submitted && !this.loading;
},
},
watch: {
determineClose() {
this.submitted = false;
this.dialog = false;
},
dialog(val) {
if (val) this.submitted = false;
},
},
methods: {
submitEvent() {
this.$emit("submit");
this.close();
this.submitted = true;
},
open() {
this.dialog = true;

View file

@ -48,7 +48,7 @@
</template>
<script>
import ImportOptions from "./ImportOptions";
import ImportOptions from "@/components/FormHelpers/ImportOptions";
import TheDownloadBtn from "@/components/UI/Buttons/TheDownloadBtn.vue";
import { backupURLs } from "@/api/backup";
export default {

View file

@ -167,11 +167,6 @@ export default {
to: "/admin/manage-users",
title: this.$t("settings.manage-users"),
},
{
icon: "mdi-backup-restore",
to: "/admin/backups",
title: this.$t("settings.backup-and-exports"),
},
{
icon: "mdi-database-import",
to: "/admin/migrations",

View file

@ -1,79 +0,0 @@
<template>
<div>
<ImportDialog
:name="selectedName"
:date="selectedDate"
ref="import_dialog"
@import="importBackup"
@delete="deleteBackup"
/>
<v-row>
<v-col :cols="12" :sm="6" :md="6" :lg="4" :xl="4" v-for="backup in backups" :key="backup.name">
<v-card hover outlined @click="openDialog(backup)">
<v-card-text>
<v-row align="center">
<v-col cols="2">
<v-icon large color="primary">mdi-backup-restore</v-icon>
</v-col>
<v-col cols="10">
<div class="text-truncate">
<strong>{{ backup.name }}</strong>
</div>
<div class="text-truncate">{{ $d(Date.parse(backup.date), "medium") }}</div>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-col>
</v-row>
</div>
</template>
<script>
import ImportDialog from "./ImportDialog";
import { api } from "@/api";
export default {
props: {
backups: Array,
},
components: {
ImportDialog,
},
data() {
return {
selectedName: "",
selectedDate: "",
loading: false,
};
},
methods: {
openDialog(backup) {
this.selectedDate = backup.date;
this.selectedName = backup.name;
this.$refs.import_dialog.open();
},
async importBackup(data) {
this.$emit("loading");
const response = await api.backups.import(data.name, data);
if (response) {
let importData = response.data;
this.$emit("finished", importData);
} else {
this.$emit("finished");
}
},
async deleteBackup(data) {
this.$emit("loading");
if (await api.backups.delete(data.name)) {
this.selectedBackup = null;
}
this.backupLoading = false;
this.$emit("finished");
},
},
};
</script>
<style></style>

View file

@ -1,113 +0,0 @@
<template>
<v-card :loading="loading">
<v-card-title> {{ $t("settings.backup.create-heading") }} </v-card-title>
<v-card-text class="mt-n3">
<v-text-field dense :label="$t('settings.backup.backup-tag')" v-model="tag"></v-text-field>
</v-card-text>
<v-card-actions class="mt-n9 flex-wrap">
<v-switch v-model="fullBackup" :label="switchLabel"></v-switch>
<v-spacer></v-spacer>
<v-btn color="success" text @click="createBackup()">
{{ $t("general.create") }}
</v-btn>
</v-card-actions>
<v-expand-transition>
<div v-if="!fullBackup">
<v-card-text class="mt-n4">
<v-row>
<v-col sm="4">
<p>{{ $t("general.options") }}</p>
<ImportOptions @update-options="updateOptions" class="mt-5" />
</v-col>
<v-col>
<p>{{ $t("general.templates") }}</p>
<v-checkbox
v-for="template in availableTemplates"
:key="template"
class="mb-n4 mt-n3"
dense
:label="template"
@click="appendTemplate(template)"
></v-checkbox>
</v-col>
</v-row>
</v-card-text>
</div>
</v-expand-transition>
</v-card>
</template>
<script>
import ImportOptions from "./ImportOptions";
import { api } from "@/api";
export default {
components: { ImportOptions },
data() {
return {
tag: null,
fullBackup: true,
loading: false,
options: {
recipes: true,
settings: true,
themes: true,
users: true,
groups: true,
},
availableTemplates: [],
selectedTemplates: [],
};
},
mounted() {
this.getAvailableBackups();
},
computed: {
switchLabel() {
if (this.fullBackup) {
return this.$t("settings.backup.full-backup");
} else return this.$t("settings.backup.partial-backup");
},
},
methods: {
updateOptions(options) {
this.options = options;
},
async getAvailableBackups() {
let response = await api.backups.requestAvailable();
response.templates.forEach(element => {
this.availableTemplates.push(element);
});
},
async createBackup() {
this.loading = true;
let data = {
tag: this.tag,
options: {
recipes: this.options.recipes,
settings: this.options.settings,
themes: this.options.themes,
users: this.options.users,
groups: this.options.groups,
},
templates: this.selectedTemplates,
};
if (await api.backups.create(data)) {
this.$emit("created");
}
this.loading = false;
},
appendTemplate(templateName) {
if (this.selectedTemplates.includes(templateName)) {
let index = this.selectedTemplates.indexOf(templateName);
if (index !== -1) {
this.selectedTemplates.splice(index, 1);
}
} else this.selectedTemplates.push(templateName);
},
},
};
</script>
<style></style>

View file

@ -1,75 +0,0 @@
<template>
<v-card :loading="backupLoading" class="mt-3">
<v-card-title class="headline">
{{ $t("settings.backup-and-exports") }}
</v-card-title>
<v-divider></v-divider>
<v-card-text>
<v-row>
<v-col cols="12" md="6" sm="12">
<NewBackupCard @created="processFinished" />
</v-col>
<v-col cols="12" md="6" sm="12">
<p>
{{ $t("settings.backup-info") }}
</p>
</v-col>
</v-row>
<v-divider class="my-3"></v-divider>
<v-card-title class="mt-n6">
{{ $t("settings.available-backups") }}
<span>
<TheUploadBtn class="mt-1" url="/api/backups/upload" @uploaded="getAvailableBackups" />
</span>
<v-spacer></v-spacer>
</v-card-title>
<AvailableBackupCard @loading="backupLoading = true" @finished="processFinished" :backups="availableBackups" />
<ImportSummaryDialog ref="report" :import-data="importData" />
</v-card-text>
</v-card>
</template>
<script>
import { api } from "@/api";
import TheUploadBtn from "@/components/UI/Buttons/TheUploadBtn";
import ImportSummaryDialog from "@/components/ImportSummaryDialog";
import AvailableBackupCard from "@/pages/Admin/Backup/AvailableBackupCard";
import NewBackupCard from "@/pages/Admin/Backup/NewBackupCard";
export default {
components: {
TheUploadBtn,
AvailableBackupCard,
NewBackupCard,
ImportSummaryDialog,
},
data() {
return {
failedImports: [],
successfulImports: [],
backupLoading: false,
availableBackups: [],
importData: [],
};
},
mounted() {
this.getAvailableBackups();
},
methods: {
async getAvailableBackups() {
let response = await api.backups.requestAvailable();
this.availableBackups = response.imports;
this.availableTemplates = response.templates;
},
processFinished(data) {
this.getAvailableBackups();
this.backupLoading = false;
this.$refs.report.open(data);
},
},
};
</script>
<style></style>

View file

@ -26,6 +26,8 @@
</v-btn>
</template>
</TheUploadBtn>
<BackupDialog :color="color" />
<v-btn :loading="loading" class="mx-2" small :color="color" @click="createBackup">
<v-icon left> mdi-plus </v-icon> Create
</v-btn>
@ -36,7 +38,7 @@
<v-list-item @click.prevent="openDialog(item)">
<v-list-item-avatar>
<v-icon large dark :color="color">
mdi-backup-restore
mdi-database
</v-icon>
</v-list-item-avatar>
@ -66,9 +68,10 @@ import TheUploadBtn from "@/components/UI/Buttons/TheUploadBtn";
import ImportSummaryDialog from "@/components/ImportSummaryDialog";
import { api } from "@/api";
import StatCard from "./StatCard";
import ImportDialog from "../Backup/ImportDialog";
import BackupDialog from "@/components/UI/Dialogs/BackupDialog";
import ImportDialog from "@/components/UI/Dialogs/ImportDialog";
export default {
components: { StatCard, ImportDialog, TheUploadBtn, ImportSummaryDialog },
components: { StatCard, ImportDialog, TheUploadBtn, ImportSummaryDialog, BackupDialog },
data() {
return {
color: "accent",
@ -91,7 +94,6 @@ export default {
async getAvailableBackups() {
const response = await api.backups.requestAvailable();
this.availableBackups = response.imports;
console.log(this.availableBackups);
},
async deleteBackup(name) {
@ -106,6 +108,7 @@ export default {
this.selectedName = backup.name;
this.$refs.import_dialog.open();
},
async importBackup(data) {
this.loading = true;
const response = await api.backups.import(data.name, data);

View file

@ -1,5 +1,4 @@
import Admin from "@/pages/Admin";
import Backup from "@/pages/Admin/Backup";
import Theme from "@/pages/Admin/Theme";
import MealPlanner from "@/pages/Admin/MealPlanner";
import Migration from "@/pages/Admin/Migration";
@ -31,14 +30,6 @@ export const adminRoutes = {
title: "settings.profile",
},
},
{
path: "backups",
component: Backup,
meta: {
title: "settings.backup-and-exports",
},
},
{
path: "themes",
component: Theme,