refactor: rework async DiagnosisView

This commit is contained in:
axolotle 2024-08-13 00:22:58 +02:00
parent dd6522a8b3
commit 50a464016b
2 changed files with 87 additions and 95 deletions

View file

@ -197,3 +197,22 @@ export type BackupList = {
size: number size: number
}> }>
} }
// DIAGNOSIS
export type Diagnosis = {
reports: {
id: string
cached_for: number
items: {
meta: Obj
status: 'INFO' | 'SUCCESS' | 'WARNING' | 'ERROR'
data: Obj
summary: string
details?: string[]
ignored: boolean
}[]
timestamp: number
description: string
}[]
}

View file

@ -1,83 +1,69 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'
import api from '@/api' import api from '@/api'
import { useInitialQueries } from '@/composables/useInitialQueries'
import { distanceToNow } from '@/helpers/filters/date' import { distanceToNow } from '@/helpers/filters/date'
import { DEFAULT_STATUS_ICON } from '@/helpers/yunohostArguments' import { STATUS_VARIANT, isOkStatus } from '@/helpers/yunohostArguments'
import type { StateStatus } from '@/types/commons'
import type { Diagnosis } from '@/types/core/api'
const { loading, refetch } = useInitialQueries( const reports = await api
[ .fetchAll<[null, Diagnosis | null]>([
{ {
method: 'PUT', method: 'PUT',
uri: 'diagnosis/run?except_if_never_ran_yet', uri: 'diagnosis/run?except_if_never_ran_yet',
humanKey: 'diagnosis.run', humanKey: 'diagnosis.run',
}, },
{ uri: 'diagnosis?full' }, { uri: 'diagnosis?full' },
], ])
{ showModal: true, onQueriesResponse }, .then(([_, diagnosis]) => {
) if (!diagnosis) return null
const reports = ref() return diagnosis.reports.map((report) => {
const badges = {
function onQueriesResponse(_: any, reportsData: any) { warnings: 0,
if (reportsData === null) { errors: 0,
reports.value = null ignoreds: 0,
return
}
const reports_ = reportsData.reports
for (const report of reports_) {
report.warnings = 0
report.errors = 0
report.ignoreds = 0
for (const item of report.items) {
const status = (item.variant = item.status.toLowerCase())
item.issue = false
if (item.ignored) {
report.ignoreds++
}
if (status === 'warning') {
item.issue = true
if (!item.ignored) {
report.warnings++
}
} else if (status === 'error') {
item.variant = 'danger'
item.issue = true
if (!item.ignored) {
report.errors++
}
} }
item.icon = const items = report.items.map((item) => {
DEFAULT_STATUS_ICON[item.variant as 'success' | 'warning' | 'danger'] const status = item.status.toLowerCase() as StateStatus
} const variant = STATUS_VARIANT[status]
const issue = !isOkStatus(status)
report.noIssues = report.warnings + report.errors === 0 if (item.ignored) badges.ignoreds++
} else if (issue) badges[`${status}s`]++
reports.value = reports_
}
function runDiagnosis({ id = null, description } = {}) { return { ...item, status, issue, variant }
const param = id !== null ? '?force' : '' })
const data = id !== null ? { categories: [id] } : {} 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 api
.put({ .put({
uri: 'diagnosis/run' + param, uri: 'diagnosis/run' + id ? '?force' : '',
data, data: id ? { categories: [id] } : {},
humanKey: { humanKey: {
key: 'diagnosis.run' + (id !== null ? '_specific' : ''), key: 'diagnosis.run' + (id ? '_specific' : ''),
description, description: report?.description,
}, },
}) })
.then(() => refetch(false)) .then(() => api.refetch())
} }
function toggleIgnoreIssue(action, report, item) { function toggleIgnoreIssue(
action: 'ignore' | 'unignore',
report: Report,
item: Report['items'][number],
) {
const filterArgs = [report.id].concat( const filterArgs = [report.id].concat(
Object.entries(item.meta).map((entries) => entries.join('=')), Object.entries(item.meta).map((entries) => entries.join('=')),
) )
@ -86,16 +72,14 @@ function toggleIgnoreIssue(action, report, item) {
.put({ .put({
uri: 'diagnosis/' + action, uri: 'diagnosis/' + action,
data: { filter: filterArgs }, data: { filter: filterArgs },
humanKey: `diagnosis.${action}.${item.status.toLowerCase()}`, humanKey: `diagnosis.${action}.${item.status}`,
}) })
.then(() => { .then(() => {
item.ignored = action === 'ignore' item.ignored = action === 'ignore'
if (item.ignored) { const count = item.ignored ? 1 : -1
report[item.status.toLowerCase() + 's']-- report.ignoreds += count
report.ignoreds++ if (!isOkStatus(item.status)) {
} else { report[`${item.status}s`] -= count
report[item.status.toLowerCase() + 's']++
report.ignoreds--
} }
}) })
} }
@ -108,26 +92,26 @@ function shareLogs() {
</script> </script>
<template> <template>
<ViewBase :loading="loading"> <div>
<template #top-bar-group-right> <TopBar>
<BButton @click="shareLogs" variant="success"> <template #group-right>
<YIcon iname="cloud-upload" /> {{ $t('logs_share_with_yunopaste') }} <BButton @click="shareLogs" variant="success">
</BButton> <YIcon iname="cloud-upload" /> {{ $t('logs_share_with_yunopaste') }}
</template>
<template #top>
<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> </BButton>
</div> </template>
</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 --> <!-- REPORT CARD -->
<YCard <YCard
@ -186,7 +170,6 @@ function shareLogs() {
v-for="(item, i) in report.items" v-for="(item, i) in report.items"
:key="i" :key="i"
:variant="item.variant" :variant="item.variant"
:icon="item.Icon"
:faded="item.ignored" :faded="item.ignored"
> >
<div class="item-button d-flex align-items-center"> <div class="item-button d-flex align-items-center">
@ -236,17 +219,7 @@ function shareLogs() {
</YListGroupItem> </YListGroupItem>
</BListGroup> </BListGroup>
</YCard> </YCard>
</div>
<template #skeleton>
<CardListSkeleton />
<BCard no-body>
<template #header>
<BSkeleton width="30%" height="36px" class="m-0" />
</template>
</BCard>
<CardListSkeleton />
</template>
</ViewBase>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>