Files
cloudron-box/dashboard/src/components/BackupSiteScheduleDialog.vue
T

136 lines
5.1 KiB
Vue
Raw Normal View History

<script setup>
import { ref, useTemplateRef, computed } from 'vue';
import { Radiobutton, Dialog, FormGroup, SingleSelect, MultiSelect } from '@cloudron/pankow';
import BackupSitesModel from '../models/BackupSitesModel.js';
2025-10-07 14:37:53 +02:00
import { cronDays, cronHours } from '../utils.js';
const emit = defineEmits([ 'success' ]);
const backupSitesModel = BackupSitesModel.create();
2025-11-13 11:18:50 +01:00
const site = ref({});
const busy = ref(false);
const formError = ref('');
const dialog = useTemplateRef('dialog');
const scheduleType = ref('');
const days = ref([]);
const hours = ref([]);
const configureRetention = ref(''); // this is 'name' and not 'id' of backupRetentions because SingleSelect needs strings
const isConfigureValid = computed(() => {
return !!days.value.length && !!hours.value.length;
});
async function onSubmit() {
if (!isConfigureValid.value) return;
busy.value = true;
2025-09-23 12:44:36 +02:00
let schedule;
if (scheduleType.value === 'pattern') {
2025-09-23 12:44:36 +02:00
let daysPattern;
if (days.value.length === 7) daysPattern = '*';
else daysPattern = days.value;
2025-09-23 12:44:36 +02:00
let hoursPattern;
if (hours.value.length === 24) hoursPattern = '*';
else hoursPattern = hours.value;
2025-09-23 12:44:36 +02:00
schedule = `00 00 ${hoursPattern} * * ${daysPattern}`;
} else {
schedule = 'never';
}
2025-11-13 11:18:50 +01:00
let [error] = await backupSitesModel.setSchedule(site.value.id, schedule);
if (error) {
busy.value = false;
formError.value = error.body ? error.body.message : 'Internal error';
return console.error(error);
}
const selectedRetention = BackupSitesModel.backupRetentions.find(function (x) { return x.name === configureRetention.value; });
2025-11-13 11:18:50 +01:00
[error] = await backupSitesModel.setRetention(site.value.id, selectedRetention.id);
if (error) {
busy.value = false;
formError.value = error.body ? error.body.message : 'Internal error';
return console.error(error);
}
emit('success');
dialog.value.close();
busy.value = false;
}
defineExpose({
2025-11-13 11:18:50 +01:00
async open(s) {
site.value = s;
busy.value = false;
formError.value = false;
2025-11-13 11:18:50 +01:00
const currentRetentionString = JSON.stringify(site.value.retention);
const selectedRetention = BackupSitesModel.backupRetentions.find(function (x) { return JSON.stringify(x.id) === currentRetentionString; });
configureRetention.value = selectedRetention ? selectedRetention.name : BackupSitesModel.backupRetentions[0].name;
2025-11-13 11:18:50 +01:00
if (site.value.schedule === 'never') {
scheduleType.value = 'never';
2025-09-23 12:44:36 +02:00
} else {
scheduleType.value = 'pattern';
2025-09-23 12:44:36 +02:00
2025-11-13 11:18:50 +01:00
const tmp = site.value.schedule.split(' ');
2025-09-23 12:44:36 +02:00
const tmpHours = tmp[2].split(',');
const tmpDays = tmp[5].split(',');
2025-10-07 14:37:53 +02:00
if (tmpDays[0] === '*') days.value = cronDays.map((day) => { return day.id; });
2025-09-23 12:44:36 +02:00
else days.value = tmpDays.map((day) => { return parseInt(day, 10); });
2025-10-07 14:37:53 +02:00
if (tmpHours[0] === '*') hours.value = cronHours.map(h => h.id);
2025-09-23 12:44:36 +02:00
else hours.value = tmpHours.map((hour) => { return parseInt(hour, 10); });
}
dialog.value.open();
}
});
</script>
<template>
<Dialog ref="dialog"
:title="$t('backups.configureBackupSchedule.title')"
reject-style="secondary"
2025-09-23 12:15:27 +02:00
:reject-label="$t('main.dialog.cancel')"
:reject-active="!busy"
:confirm-label="$t('main.dialog.save')"
:confirm-busy="busy"
:confirm-active="isConfigureValid"
@confirm="onSubmit()"
>
2025-11-13 11:18:50 +01:00
<p>{{ $t('backups.configureBackupSchedule.schedule.context', { name: site.name }) }}</p>
<div class="error-label" v-show="formError">{{ formError }}</div>
<form @submit.prevent="onSubmit()" autocomplete="off">
<fieldset>
<FormGroup>
2025-11-13 11:18:50 +01:00
<label for="daysInput">{{ $t('backups.configureBackupSchedule.schedule.title') }}</label>
<div description v-html="$t('backups.configureBackupSchedule.schedule.description')"></div>
<Radiobutton v-model="scheduleType" value="never" :label="$t('backups.configureBackupSchedule.disable')"/>
<Radiobutton v-model="scheduleType" value="pattern" :label="$t('backups.configureBackupSchedule.enable')"/>
<div v-if="scheduleType === 'pattern'" style="display: flex; align-items: center; gap: 10px; margin: 10px">
<div>{{ $t('backups.configureBackupSchedule.days') }}: <MultiSelect id="daysInput" v-model="days" :options="cronDays" option-key="id" option-label="name"></MultiSelect></div>
<div>{{ $t('backups.configureBackupSchedule.hours') }}: <MultiSelect v-model="hours" :options="cronHours" option-key="id" option-label="name"></MultiSelect></div>
<div class="text-small text-danger" v-show="scheduleType === 'pattern' && !(hours.length !== 0 && days.length !== 0)">{{ $t('settings.updateScheduleDialog.selectOne') }}</div>
</div>
</FormGroup>
<FormGroup>
<label for="retentionInput">{{ $t('backups.configureBackupSchedule.retentionPolicy') }}</label>
<!-- we do not used id as key because SingleSelect can only handle strings -->
<SingleSelect id="retentionInput" v-model="configureRetention" :options="BackupSitesModel.backupRetentions" option-key="name" option-label="name" />
</FormGroup>
</fieldset>
</form>
</Dialog>
</template>