yunohost-admin/app/src/views/diagnosis/DiagnosisView.vue
2024-08-13 11:56:52 +02:00

243 lines
6.2 KiB
Vue

<script setup lang="ts">
import api from '@/api'
import { distanceToNow } from '@/helpers/filters/date'
import { STATUS_VARIANT, isOkStatus } from '@/helpers/yunohostArguments'
import type { StateStatus } from '@/types/commons'
import type { Diagnosis } from '@/types/core/api'
const reports = await api
.fetchAll<[null, Diagnosis | null]>([
{
method: 'PUT',
uri: 'diagnosis/run?except_if_never_ran_yet',
humanKey: 'diagnosis.run',
},
{ uri: 'diagnosis?full' },
])
.then(([_, diagnosis]) => {
if (!diagnosis) return null
return diagnosis.reports.map((report) => {
const badges = {
warnings: 0,
errors: 0,
ignoreds: 0,
}
const items = report.items.map((item) => {
const status = item.status.toLowerCase() as StateStatus
const variant = STATUS_VARIANT[status]
const issue = !isOkStatus(status)
if (item.ignored) badges.ignoreds++
else if (issue) badges[`${status}s`]++
return { ...item, status, issue, variant }
})
return {
...report,
...badges,
items,
noIssues: badges.warnings + badges.errors === 0,
}
})
})
type Report = Exclude<typeof reports, null>[number]
function runDiagnosis(report?: { id: string; description: string }) {
const id = report?.id
api
.put({
uri: 'diagnosis/run' + id ? '?force' : '',
data: id ? { categories: [id] } : {},
humanKey: {
key: 'diagnosis.run' + (id ? '_specific' : ''),
description: report?.description,
},
})
.then(() => api.refetch())
}
function toggleIgnoreIssue(
action: 'ignore' | 'unignore',
report: Report,
item: Report['items'][number],
) {
const filterArgs = [report.id].concat(
Object.entries(item.meta).map((entries) => entries.join('=')),
)
api
.put({
uri: 'diagnosis/' + action,
data: { filter: filterArgs },
humanKey: `diagnosis.${action}.${item.status}`,
})
.then(() => {
item.ignored = action === 'ignore'
const count = item.ignored ? 1 : -1
report.ignoreds += count
if (!isOkStatus(item.status)) {
report[`${item.status}s`] -= count
}
})
}
function shareLogs() {
api.get('diagnosis?share').then(({ url }) => {
window.open(url, '_blank')
})
}
</script>
<template>
<div>
<TopBar>
<template #group-right>
<BButton variant="success" @click="shareLogs">
<YIcon iname="cloud-upload" /> {{ $t('logs_share_with_yunopaste') }}
</BButton>
</template>
</TopBar>
<div class="alert alert-info">
{{ $t(reports ? 'diagnosis_explanation' : 'diagnosis_first_run') }}
<BButton
v-if="reports === null"
class="d-block mt-2"
variant="info"
@click="runDiagnosis()"
>
<YIcon iname="stethoscope" /> {{ $t('run_first_diagnosis') }}
</BButton>
</div>
<!-- REPORT CARD -->
<YCard
v-for="report in reports"
:key="report.id"
collapsable
:collapsed="report.noIssues"
no-body
button-unbreak="lg"
>
<!-- REPORT HEADER -->
<template #header>
<h2>{{ report.description }}</h2>
<div class="">
<BBadge
v-if="report.noIssues"
v-t="'everything_good'"
variant="success"
/>
<BBadge
v-if="report.errors"
v-t="{ path: 'issues', args: { count: report.errors } }"
variant="danger"
/>
<BBadge
v-if="report.warnings"
v-t="{ path: 'warnings', args: { count: report.warnings } }"
variant="warning"
/>
<BBadge
v-if="report.ignoreds"
v-t="{ path: 'ignored', args: { count: report.ignoreds } }"
/>
</div>
</template>
<template #header-buttons>
<BButton
size="sm"
:variant="report.items ? 'info' : 'success'"
@click="runDiagnosis(report)"
>
<YIcon iname="refresh" /> {{ $t('rerun_diagnosis') }}
</BButton>
</template>
<!-- REPORT BODY -->
<p class="last-time-run">
{{ $t('last_ran') }} {{ distanceToNow(report.timestamp, true, true) }}
</p>
<BListGroup flush>
<!-- REPORT ITEM -->
<YListGroupItem
v-for="(item, i) in report.items"
:key="i"
:variant="item.variant"
:faded="item.ignored"
>
<div class="item-button d-flex align-items-center">
<p class="mb-0 me-2" v-html="item.summary" />
<div class="d-flex flex-column flex-lg-row ms-auto">
<BButton
v-if="item.ignored"
size="sm"
@click="toggleIgnoreIssue('unignore', report, item)"
>
<YIcon iname="bell" /> {{ $t('unignore') }}
</BButton>
<BButton
v-else-if="item.issue"
variant="warning"
size="sm"
@click="toggleIgnoreIssue('ignore', report, item)"
>
<YIcon iname="bell-slash" /> {{ $t('ignore') }}
</BButton>
<BButton
v-if="item.details"
v-b-toggle="`collapse-${report.id}-item-${i}`"
size="sm"
variant="outline-dark"
class="ms-lg-2 mt-2 mt-lg-0"
>
<YIcon iname="level-down" /> {{ $t('details') }}
</BButton>
</div>
</div>
<BCollapse
v-if="item.details"
:id="`collapse-${report.id}-item-${i}`"
>
<ul class="mt-2 ps-4">
<li
v-for="(detail, index) in item.details"
:key="index"
v-html="detail"
/>
</ul>
</BCollapse>
</YListGroupItem>
</BListGroup>
</YCard>
</div>
</template>
<style lang="scss" scoped>
.badge + .badge {
margin-left: 0.5rem;
}
p.last-time-run {
margin: 0.75rem 1rem;
}
.list-group {
border-top: $list-group-border-width solid $list-group-border-color;
}
.item-button {
button {
min-width: 6rem;
}
}
</style>