mirror of
https://github.com/directus/directus.git
synced 2026-04-03 03:00:39 -04:00
Improve error handling for app extensions (#17191)
* add util function to get vue component name * add global error handler * add v-error-boundary component * use error boundary to wrap insights panels * use error boundary to wrap form interfaces * use error boundary in render display and template * use error boundary in extension options * use error boundary for flows operation overview * extract default options-overview into a component * add tests
This commit is contained in:
@@ -121,18 +121,27 @@
|
||||
>
|
||||
{{ t('no_data') }}
|
||||
</div>
|
||||
<component
|
||||
:is="`panel-${tile.data.type}`"
|
||||
v-else
|
||||
v-bind="tile.data.options"
|
||||
:id="tile.id"
|
||||
:dashboard="primaryKey"
|
||||
:show-header="tile.showHeader"
|
||||
:height="tile.height"
|
||||
:width="tile.width"
|
||||
:now="now"
|
||||
:data="data[tile.id]"
|
||||
/>
|
||||
<v-error-boundary v-else :name="`panel-${tile.data.type}`">
|
||||
<component
|
||||
:is="`panel-${tile.data.type}`"
|
||||
v-bind="tile.data.options"
|
||||
:id="tile.id"
|
||||
:dashboard="primaryKey"
|
||||
:show-header="tile.showHeader"
|
||||
:height="tile.height"
|
||||
:width="tile.width"
|
||||
:now="now"
|
||||
:data="data[tile.id]"
|
||||
/>
|
||||
|
||||
<template #fallback="{ error }">
|
||||
<div class="panel-error">
|
||||
<v-icon name="warning" />
|
||||
{{ t('unexpected_error') }}
|
||||
<v-error :error="error" />
|
||||
</div>
|
||||
</template>
|
||||
</v-error-boundary>
|
||||
</div>
|
||||
</template>
|
||||
</v-workspace>
|
||||
|
||||
@@ -14,14 +14,18 @@
|
||||
primary-key="+"
|
||||
/>
|
||||
|
||||
<component
|
||||
:is="`${type}-options-${extensionInfo!.id}`"
|
||||
v-else
|
||||
:value="optionsValues"
|
||||
:collection="collection"
|
||||
:field="field"
|
||||
@input="optionsValues = $event"
|
||||
/>
|
||||
<v-error-boundary v-else :name="`${type}-options-${extensionInfo!.id}`">
|
||||
<component
|
||||
:is="`${type}-options-${extensionInfo!.id}`"
|
||||
:value="optionsValues"
|
||||
:collection="collection"
|
||||
:field="field"
|
||||
@input="optionsValues = $event"
|
||||
/>
|
||||
<template #fallback>
|
||||
<v-notice type="warning">{{ t('unexpected_error') }}</v-notice>
|
||||
</template>
|
||||
</v-error-boundary>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
@@ -79,33 +79,36 @@
|
||||
<v-icon name="adjust" />
|
||||
</div>
|
||||
</template>
|
||||
<div v-if="typeof currentOperation?.overview === 'function'" class="block">
|
||||
<div v-tooltip="panel.key" class="name">
|
||||
{{ panel.id === '$trigger' ? t(`triggers.${panel.type}.name`) : panel.name }}
|
||||
<v-error-boundary
|
||||
v-if="typeof currentOperation?.overview === 'function'"
|
||||
:name="`operation-overview-${currentOperation.id}`"
|
||||
>
|
||||
<div class="block">
|
||||
<options-overview :panel="panel" :current-operation="currentOperation" :flow="flow" />
|
||||
</div>
|
||||
<dl class="options-overview selectable">
|
||||
<div
|
||||
v-for="{ label, text, copyable } of translate(currentOperation?.overview(panel.options ?? {}, { flow }))"
|
||||
:key="label"
|
||||
>
|
||||
<dt>{{ label }}</dt>
|
||||
<dd>{{ text }}</dd>
|
||||
<v-icon
|
||||
v-if="isCopySupported && copyable"
|
||||
name="copy"
|
||||
small
|
||||
clickable
|
||||
class="clipboard-icon"
|
||||
@click="copyToClipboard(text)"
|
||||
/>
|
||||
|
||||
<template #fallback="{ error: optionsOverviewError }">
|
||||
<div class="options-overview-error">
|
||||
<v-icon name="warning" />
|
||||
{{ t('unexpected_error') }}
|
||||
<v-error :error="optionsOverviewError" />
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<component
|
||||
:is="`operation-overview-${currentOperation.id}`"
|
||||
</template>
|
||||
</v-error-boundary>
|
||||
<v-error-boundary
|
||||
v-else-if="currentOperation && 'id' in currentOperation"
|
||||
:options="currentOperation"
|
||||
/>
|
||||
:name="`operation-overview-${currentOperation.id}`"
|
||||
>
|
||||
<component :is="`operation-overview-${currentOperation.id}`" :options="currentOperation" />
|
||||
|
||||
<template #fallback="{ error: operationOverviewError }">
|
||||
<div class="options-overview-error">
|
||||
<v-icon name="warning" />
|
||||
{{ t('unexpected_error') }}
|
||||
<v-error :error="operationOverviewError" />
|
||||
</div>
|
||||
</template>
|
||||
</v-error-boundary>
|
||||
<template v-if="panel.id === '$trigger'" #footer>
|
||||
<div class="status-footer" :class="flowStatus">
|
||||
<display-color
|
||||
@@ -135,15 +138,14 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useClipboard } from '@/composables/use-clipboard';
|
||||
import { useExtensions } from '@/extensions';
|
||||
import { translate } from '@/utils/translate-object-values';
|
||||
import { Vector2 } from '@/utils/vector2';
|
||||
import { FlowRaw } from '@directus/shared/types';
|
||||
import { computed, ref, toRefs } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { ATTACHMENT_OFFSET, REJECT_OFFSET, RESOLVE_OFFSET } from '../constants';
|
||||
import { getTriggers } from '../triggers';
|
||||
import OptionsOverview from './options-overview.vue';
|
||||
|
||||
export type Target = 'resolve' | 'reject';
|
||||
export type ArrowInfo = {
|
||||
@@ -194,8 +196,6 @@ const emit = defineEmits([
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { isCopySupported, copyToClipboard } = useClipboard();
|
||||
|
||||
const styleVars = {
|
||||
'--reject-left': REJECT_OFFSET.x + 'px',
|
||||
'--reject-top': REJECT_OFFSET.y + 'px',
|
||||
@@ -483,27 +483,20 @@ function pointerLeave() {
|
||||
}
|
||||
}
|
||||
|
||||
.options-overview {
|
||||
> div {
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.options-overview-error {
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
dt {
|
||||
flex-basis: 100%;
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
--v-icon-color: var(--danger);
|
||||
|
||||
dd {
|
||||
font-family: var(--family-monospace);
|
||||
flex-basis: 0;
|
||||
}
|
||||
|
||||
.clipboard-icon {
|
||||
--v-icon-color: var(--foreground-subdued);
|
||||
--v-icon-color-hover: var(--foreground-normal);
|
||||
margin-left: 4px;
|
||||
.v-error {
|
||||
margin-top: 8px;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div v-tooltip="panel.key" class="name">
|
||||
{{ panel.id === '$trigger' ? t(`triggers.${panel.type}.name`) : panel.name }}
|
||||
</div>
|
||||
<dl class="options-overview selectable">
|
||||
<div
|
||||
v-for="{ label, text, copyable } of translate(currentOperation?.overview(panel.options ?? {}, { flow }))"
|
||||
:key="label"
|
||||
>
|
||||
<dt>{{ label }}</dt>
|
||||
<dd>{{ text }}</dd>
|
||||
<v-icon
|
||||
v-if="isCopySupported && copyable"
|
||||
name="copy"
|
||||
small
|
||||
clickable
|
||||
class="clipboard-icon"
|
||||
@click="copyToClipboard(text)"
|
||||
/>
|
||||
</div>
|
||||
</dl>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useClipboard } from '@/composables/use-clipboard';
|
||||
import { translate } from '@/utils/translate-object-values';
|
||||
import { FlowRaw } from '@directus/shared/types';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
defineProps<{
|
||||
panel: Record<string, any>;
|
||||
currentOperation: any;
|
||||
flow: FlowRaw;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const { isCopySupported, copyToClipboard } = useClipboard();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.options-overview {
|
||||
> div {
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
dt {
|
||||
flex-basis: 100%;
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
|
||||
dd {
|
||||
font-family: var(--family-monospace);
|
||||
flex-basis: 0;
|
||||
}
|
||||
|
||||
.clipboard-icon {
|
||||
--v-icon-color: var(--foreground-subdued);
|
||||
--v-icon-color-hover: var(--foreground-normal);
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user