yunohost-admin/app/src/views/tool/ToolMigrations.vue

149 lines
4.3 KiB
Vue

<script setup lang="ts">
import { reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import api from '@/api'
import type ViewBase from '@/components/globals/ViewBase.vue'
import { useAutoModal } from '@/composables/useAutoModal'
// FIXME not tested with pending migrations (disclaimer and stuff)
const { t } = useI18n()
const modalConfirm = useAutoModal()
const viewElem = ref<InstanceType<typeof ViewBase> | null>(null)
const queries = [
['GET', 'migrations?pending'],
['GET', 'migrations?done'],
]
const pending = ref()
const done = ref()
const checked = reactive({})
function onQueriesResponse({ migrations: pending_ }, { migrations: done_ }) {
done.value = done_.length ? done_.reverse() : null
pending_.forEach((migration) => {
if (migration.disclaimer) {
migration.disclaimer = migration.disclaimer.replaceAll('\n', '<br>')
checked[migration.id] = null
}
})
// FIXME change to pending
pending.value = pending_.length ? pending_.reverse() : null
}
function runMigrations() {
// Display an error on migration's disclaimer that aren't checked.
for (const [id, value] of Object.entries(checked)) {
if (value !== true) {
checked[id] = false
}
}
// Check that every migration's disclaimer has been checked.
if (Object.values(checked).every((value) => value === true)) {
api
.put('migrations?accept_disclaimer', {}, 'migrations.run')
.then(() => viewElem.value!.fetchQueries())
}
}
async function skipMigration(id) {
const confirmed = await modalConfirm(t('confirm_migrations_skip'))
if (!confirmed) return
api
.put('/migrations/' + id, { skip: '', targets: id }, 'migration.skip')
.then(() => viewElem.value!.fetchQueries())
}
</script>
<template>
<ViewBase
:queries="queries"
@queries-response="onQueriesResponse"
ref="viewElem"
>
<!-- PENDING MIGRATIONS -->
<YCard :title="$t('migrations_pending')" icon="cogs" no-body>
<template #header-buttons v-if="pending">
<BButton size="sm" variant="success" @click="runMigrations">
<YIcon iname="play" /> {{ $t('run') }}
</BButton>
</template>
<BCardBody v-if="pending === null">
<span class="text-success">
<YIcon iname="check-circle" /> {{ $t('migrations_no_pending') }}
</span>
</BCardBody>
<BListGroup v-else-if="pending" flush>
<BListGroupItem
v-for="{ number, description, id, disclaimer } in pending"
:key="number"
>
<div class="d-flex align-items-center">
{{ number }}. {{ description }}
<div class="ms-auto">
<BButton @click="skipMigration(id)" size="sm" variant="warning">
<YIcon iname="close" /> {{ $t('skip') }}
</BButton>
</div>
</div>
<template v-if="disclaimer">
<hr />
<p v-html="disclaimer" />
<BFormCheckbox
:id="'checkbox-' + number"
:name="'checkbox-' + number"
:aria-describedby="'checkbox-feedback-' + number"
v-model="checked[id]"
>
{{ $t('migrations_disclaimer_check_message') }}
</BFormCheckbox>
<BFormInvalidFeedback
v-if="checked[id] === false"
:state="false"
:id="'checkbox-feedback-' + number"
>
{{ $t('migrations_disclaimer_not_checked') }}
</BFormInvalidFeedback>
</template>
</BListGroupItem>
</BListGroup>
</YCard>
<!-- DONE MIGRATIONS -->
<YCard
:title="$t('migrations_done')"
icon="cogs"
collapsable
collapsed
no-body
>
<BCardBody v-if="done === null">
<span class="text-success">
<YIcon iname="check-circle" /> {{ $t('migrations_no_done') }}
</span>
</BCardBody>
<BListGroup flush v-else-if="done">
<BListGroupItem v-for="{ number, description } in done" :key="number">
{{ number }}. {{ description }}
</BListGroupItem>
</BListGroup>
</YCard>
<template #skeleton>
<CardListSkeleton :item-count="3" />
<BCard no-body>
<template #header>
<BSkeleton width="30%" height="36px" class="m-0" />
</template>
</BCard>
</template>
</ViewBase>
</template>