refactor: update config panels views using useConfigPanels

This commit is contained in:
axolotle 2024-07-25 10:08:11 +02:00
parent 8af40dd04a
commit 678638534b
4 changed files with 126 additions and 173 deletions

View file

@ -141,22 +141,15 @@ const routes = [
},
},
{
path: '/domains/:name',
name: 'domain-info',
path: '/domains/:name/:tabId?',
component: () => import('@/views/domain/DomainInfo.vue'),
props: true,
children: [
{
name: 'domain-info',
path: ':tabId?',
component: () => import('@/components/ConfigPanel.vue'),
props: true,
meta: {
routerParams: ['name'], // Override router key params to avoid view recreation at tab change.
args: { param: 'name' },
breadcrumb: ['domain-list', 'domain-info'],
},
},
],
meta: {
routerParams: ['name'], // Override router key params to avoid view recreation at tab change.
args: { param: 'name' },
breadcrumb: ['domain-list', 'domain-info'],
},
},
/*
@ -202,22 +195,15 @@ const routes = [
},
},
{
path: '/apps/:id',
name: 'app-info',
path: '/apps/:id/:tabId?',
component: () => import('@/views/app/AppInfo.vue'),
props: true,
children: [
{
name: 'app-info',
path: ':tabId?',
component: () => import('@/components/ConfigPanel.vue'),
props: true,
meta: {
routerParams: ['id'], // Override router key params to avoid view recreation at tab change.
args: { param: 'id' },
breadcrumb: ['app-list', 'app-info'],
},
},
],
meta: {
routerParams: ['id'], // Override router key params to avoid view recreation at tab change.
args: { param: 'id' },
breadcrumb: ['app-list', 'app-info'],
},
},
/*
@ -315,21 +301,15 @@ const routes = [
},
},
{
path: '/tools/settings',
name: 'tool-settings',
path: '/tools/settings/:tabId?',
component: () => import('@/views/tool/ToolSettings.vue'),
children: [
{
name: 'tool-settings',
path: ':tabId?',
component: () => import('@/components/ConfigPanel.vue'),
props: true,
meta: {
routerParams: [],
args: { trad: 'tools_yunohost_settings' },
breadcrumb: ['tool-list', 'tool-settings'],
},
},
],
props: true,
meta: {
routerParams: [],
args: { trad: 'tools_yunohost_settings' },
breadcrumb: ['tool-list', 'tool-settings'],
},
},
{
name: 'tool-power',

View file

@ -1,26 +1,29 @@
<script setup lang="ts">
import { useVuelidate } from '@vuelidate/core'
import { computed, reactive, ref } from 'vue'
import { computed, reactive, ref, shallowRef } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'
import api, { objectToParams } from '@/api'
import { APIBadRequestError, type APIError } from '@/api/errors'
import ConfigPanels from '@/components/ConfigPanels.vue'
import { type APIError } from '@/api/errors'
import ConfigPanelsComponent from '@/components/ConfigPanels.vue'
import type {
ConfigPanelsProps,
OnPanelApply,
} from '@/composables/configPanels'
import { formatConfigPanels, useConfigPanels } from '@/composables/configPanels'
import { useAutoModal } from '@/composables/useAutoModal'
import { useInitialQueries } from '@/composables/useInitialQueries'
import { isEmptyValue } from '@/helpers/commons'
import { humanPermissionName } from '@/helpers/filters/human'
import { helpers, required } from '@/helpers/validators'
import {
formatFormData,
formatI18nField,
formatYunoHostConfigPanels,
} from '@/helpers/yunohostArguments'
import { formatI18nField } from '@/helpers/yunohostArguments'
import { useStoreGetters } from '@/store/utils'
import type { CoreConfigPanels } from '@/types/core/options'
const props = defineProps<{
id: string
tabId?: string
}>()
const { t } = useI18n()
@ -60,18 +63,8 @@ const { loading, refetch } = useInitialQueries(
const app = ref()
const purge = ref(false)
const config_panel_err = ref(null)
const config = ref({
panels: [
// Fake integration of operations in config panels
{
hasApplyButton: false,
id: 'operations',
name: t('operations'),
},
],
validations: {},
})
const configPanelErr = ref('')
const config = shallowRef<ConfigPanelsProps | undefined>()
const doc = ref()
const currentTab = computed(() => {
@ -215,47 +208,39 @@ async function onQueriesResponse(app_: any) {
await api
.get(`apps/${props.id}/config?full`)
.then((cp) => {
const config_ = formatYunoHostConfigPanels(cp)
// reinject 'operations' fake config tab
config_.panels.unshift(config.panels[0])
config.value = config_
const config_ = cp as CoreConfigPanels
// Fake integration of operations in config panels
config_.panels.unshift({
id: 'operations',
name: t('operations'),
})
config.value = useConfigPanels(
formatConfigPanels(config_),
() => props.tabId,
onPanelApply,
)
})
.catch((err: APIError) => {
config_panel_err.value = err.message
configPanelErr.value = err.message
})
}
}
async function onConfigSubmit({ id, form, action, name }) {
const args = await formatFormData(form, {
removeEmpty: false,
removeNull: true,
})
const onPanelApply: OnPanelApply = ({ panelId, data, action }, onError) => {
api
.put(
action
? `apps/${props.id}/actions/${action}`
: `apps/${props.id}/config/${id}`,
isEmptyValue(args) ? {} : { args: objectToParams(args) },
: `apps/${props.id}/config/${panelId}`,
isEmptyValue(data) ? {} : { args: objectToParams(data) },
{
key: `apps.${action ? 'action' : 'update'}_config`,
id,
id: panelId,
name: props.id,
},
)
.then(() => refetch())
.catch((err: APIError) => {
if (!(err instanceof APIBadRequestError)) throw err
const panel = config.value.panels.find((panel) => panel.id === id)!
if (err.data.name) {
Object.assign(externalResults, {
forms: { [panel.id]: { [err.data.name]: [err.data.error] } },
})
} else {
panel.serverError = err.message
}
})
.catch(onError)
}
function changeLabel(permName, data) {
@ -439,20 +424,23 @@ async function uninstall() {
<VueShowdown :markdown="app.description" />
</section>
<YAlert v-if="config_panel_err" class="mb-4" variant="danger" icon="bug">
<YAlert v-if="configPanelErr" class="mb-4" variant="danger" icon="bug">
<p>{{ $t('app.info.config_panel_error') }}</p>
<p>{{ config_panel_err }}</p>
<p>{{ configPanelErr }}</p>
<p>{{ $t('app.info.config_panel_error_please_report') }}</p>
</YAlert>
<!-- BASIC INFOS -->
<ConfigPanels
v-bind="config"
:external-results="externalResults"
@apply="onConfigSubmit"
<ConfigPanelsComponent
v-if="config"
v-model="config.form"
:panel="config.panel.value"
:validations="config.v.value"
:routes="config.routes"
@apply="config.onPanelApply"
>
<!-- OPERATIONS TAB -->
<template v-if="currentTab === 'operations'" #tab-top>
<template v-if="currentTab === 'operations'" #default>
<!-- CHANGE PERMISSIONS LABEL -->
<BFormGroup
:label="$t('app_manage_label_and_tiles')"
@ -584,7 +572,7 @@ async function uninstall() {
</template>
</BFormGroup>
</template>
</ConfigPanels>
</ConfigPanelsComponent>
<BCard v-if="app && app.doc.admin.length" no-body>
<BTabs card fill pills>

View file

@ -1,22 +1,25 @@
<script setup lang="ts">
import { computed, reactive, ref } from 'vue'
import { computed, ref, shallowRef } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'
import { useStore } from 'vuex'
import api, { objectToParams } from '@/api'
import ConfigPanels from '@/components/ConfigPanels.vue'
import ConfigPanelsComponent from '@/components/ConfigPanels.vue'
import type {
ConfigPanelsProps,
OnPanelApply,
} from '@/composables/configPanels'
import { formatConfigPanels, useConfigPanels } from '@/composables/configPanels'
import { useAutoModal } from '@/composables/useAutoModal'
import { useInitialQueries } from '@/composables/useInitialQueries'
import {
formatFormData,
formatYunoHostConfigPanels,
} from '@/helpers/yunohostArguments'
import { useStoreGetters } from '@/store/utils'
import type { CoreConfigPanels } from '@/types/core/options'
import DomainDns from '@/views/domain/DomainDns.vue'
const props = defineProps<{
name: string
tabId?: string
}>()
const { t } = useI18n()
@ -34,14 +37,9 @@ const { loading, refetch } = useInitialQueries(
)
const { mainDomain } = useStoreGetters()
const config = ref({})
const externalResults = reactive({})
const config = shallowRef<ConfigPanelsProps | undefined>()
const unsubscribeDomainFromDyndns = ref(false)
const currentTab = computed(() => {
return route.params.tabId
})
const domain = computed(() => {
return store.getters.domain(props.name)
})
@ -80,40 +78,33 @@ const isMainDynDomain = computed(() => {
)
})
function onQueriesResponse(domains: any, domain: any, config_: any) {
config.value = formatYunoHostConfigPanels(config_)
function onQueriesResponse(
domains: any,
domain: any,
config_: CoreConfigPanels,
) {
config.value = useConfigPanels(
formatConfigPanels(config_),
() => props.tabId,
onPanelApply,
)
}
async function onConfigSubmit({ id, form, action, name }) {
const args = await formatFormData(form, {
removeEmpty: false,
removeNull: true,
})
const onPanelApply: OnPanelApply = ({ panelId, data, action }, onError) => {
api
.put(
action
? `domain/${props.name}/actions/${action}`
: `domains/${props.name}/config/${id}`,
{ args: objectToParams(args) },
: `domains/${props.name}/config/${panelId}`,
{ args: objectToParams(data) },
{
key: `domains.${action ? 'action' : 'update'}_config`,
id,
id: panelId,
name: props.name,
},
)
.then(() => refetch())
.catch((err) => {
if (err.name !== 'APIBadRequestError') throw err
const panel = config.value.panels.find((panel) => panel.id === id)
if (err.data.name) {
Object.assign(externalResults, {
forms: { [panel.id]: { [err.data.name]: [err.data.error] } },
})
} else {
panel.serverError = err.message
}
})
.catch(onError)
}
async function deleteDomain() {
@ -254,16 +245,18 @@ async function setAsDefaultDomain() {
</DescriptionRow>
</YCard>
<ConfigPanels
v-if="config.panels"
v-bind="config"
:external-results="externalResults"
@apply="onConfigSubmit"
<ConfigPanelsComponent
v-if="config"
v-model="config.form"
:panel="config.panel.value"
:validations="config.v.value"
:routes="config.routes"
@apply="config.onPanelApply"
>
<template v-if="currentTab === 'dns'" #tab-after>
<template v-if="tabId === 'dns'" #tab-after>
<DomainDns :name="name" />
</template>
</ConfigPanels>
</ConfigPanelsComponent>
<BModal
v-if="domain"

View file

@ -1,61 +1,53 @@
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { shallowRef } from 'vue'
import api, { objectToParams } from '@/api'
import { APIBadRequestError, type APIError } from '@/api/errors'
import ConfigPanels from '@/components/ConfigPanels.vue'
import ConfigPanelsComponent from '@/components/ConfigPanels.vue'
import type {
ConfigPanelsProps,
OnPanelApply,
} from '@/composables/configPanels'
import { formatConfigPanels, useConfigPanels } from '@/composables/configPanels'
import { useInitialQueries } from '@/composables/useInitialQueries'
import {
formatFormData,
formatYunoHostConfigPanels,
} from '@/helpers/yunohostArguments'
import type { CoreConfigPanels } from '@/types/core/options'
const props = defineProps<{ tabId?: string }>()
const { loading, refetch } = useInitialQueries([['GET', 'settings?full']], {
onQueriesResponse,
})
const config = ref({})
// FIXME user proper useValidate stuff
const externalResults = reactive({})
const config = shallowRef<ConfigPanelsProps | undefined>()
function onQueriesResponse(config_: any) {
config.value = formatYunoHostConfigPanels(config_)
function onQueriesResponse(config_: CoreConfigPanels) {
config.value = useConfigPanels(
formatConfigPanels(config_),
() => props.tabId,
onPanelApply,
)
}
async function onConfigSubmit({ id, form }) {
const args = await formatFormData(form, {
removeEmpty: false,
removeNull: true,
})
const onPanelApply: OnPanelApply = ({ panelId, data }, onError) => {
// FIXME no route for potential action
api
.put(
`settings/${id}`,
{ args: objectToParams(args) },
{ key: 'settings.update', panel: id },
`settings/${panelId}`,
{ args: objectToParams(data) },
{ key: 'settings.update', panel: panelId },
)
.then(() => refetch())
.catch((err: APIError) => {
if (!(err instanceof APIBadRequestError)) throw err
const panel = config.value.panels.find((panel) => panel.id === id)
if (err.data.name) {
Object.assign(externalResults, {
forms: { [panel.id]: { [err.data.name]: [err.data.error] } },
})
} else {
panel.serverError = err.message
}
})
.catch(onError)
}
</script>
<template>
<ViewBase :loading="loading" skeleton="CardFormSkeleton">
<ConfigPanels
v-if="config.panels"
v-bind="config"
:external-results="externalResults"
@apply="onConfigSubmit"
<ConfigPanelsComponent
v-if="config"
v-model="config.form"
:panel="config.panel.value"
:validations="config.v.value"
:routes="config.routes"
@apply="config.onPanelApply"
/>
</ViewBase>
</template>