mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Display missing extensions (#21704)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import type { ExtensionType } from '@directus/extensions';
|
||||
|
||||
export const extensionTypeIconMap: Record<ExtensionType, string> = {
|
||||
export const extensionTypeIconMap: Record<ExtensionType | 'missing', string> = {
|
||||
interface: 'design_services',
|
||||
display: 'label',
|
||||
layout: 'dataset',
|
||||
@@ -11,4 +11,5 @@ export const extensionTypeIconMap: Record<ExtensionType, string> = {
|
||||
endpoint: 'api',
|
||||
operation: 'flowsheet',
|
||||
bundle: 'hub',
|
||||
missing: 'warning',
|
||||
};
|
||||
|
||||
@@ -80,6 +80,7 @@ extension_hooks: Hooks
|
||||
extension_endpoints: Endpoints
|
||||
extension_operations: Operations
|
||||
extension_bundles: Bundles
|
||||
extension_missing: Missing
|
||||
extension_reload_required: Page Reload Required
|
||||
extension_reload_required_copy: A page reload is required to see the changes after enabling or disabling app extensions.
|
||||
extension_reload_now: Reload now
|
||||
|
||||
@@ -6,12 +6,12 @@ import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const props = defineProps<{
|
||||
type: ExtensionType;
|
||||
type: ExtensionType | 'missing';
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const label = computed(() => t(`extension_${pluralize(props.type)}`));
|
||||
const label = computed(() => t(`extension_${props.type !== 'missing' ? pluralize(props.type) : props.type}`));
|
||||
|
||||
const icon = computed(() => extensionTypeIconMap[props.type]);
|
||||
</script>
|
||||
|
||||
@@ -29,8 +29,16 @@ const devMode = import.meta.env.DEV;
|
||||
|
||||
const saving = ref(false);
|
||||
|
||||
const type = computed(() => props.extension.schema?.type);
|
||||
const icon = computed(() => (type.value ? extensionTypeIconMap[type.value] : 'warning'));
|
||||
const type = computed(() => props.extension.schema?.type ?? 'missing');
|
||||
const icon = computed(() => extensionTypeIconMap[type.value]);
|
||||
|
||||
const disabled = computed(() => {
|
||||
if (type.value === 'missing') return true;
|
||||
if (devMode) return false;
|
||||
|
||||
return !props.extension.meta.enabled;
|
||||
});
|
||||
|
||||
const disableLocked = computed(() => devMode && isOrHasAppExtension.value);
|
||||
const uninstallLocked = computed(() => props.extension.meta.source !== 'registry');
|
||||
|
||||
@@ -52,11 +60,13 @@ const isPartiallyEnabled = computed(() => {
|
||||
});
|
||||
|
||||
const isOrHasAppExtension = computed(() => {
|
||||
if (type.value === 'bundle') {
|
||||
const type = props.extension.schema?.type;
|
||||
|
||||
if (type === 'bundle') {
|
||||
return props.children.some((e) => isAppExtension(e.schema?.type));
|
||||
}
|
||||
|
||||
return isAppExtension(type.value);
|
||||
return isAppExtension(type);
|
||||
});
|
||||
|
||||
const state = computed<{ text: string; status: ExtensionStatus }>(() => {
|
||||
@@ -102,7 +112,7 @@ const uninstall = async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-list-item block :class="{ disabled: devMode ? false : !extension.meta.enabled }">
|
||||
<v-list-item block :class="{ disabled }">
|
||||
<v-list-item-icon v-tooltip="t(`extension_${type}`)"><v-icon :name="icon" small /></v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
<span class="monospace">
|
||||
@@ -120,24 +130,26 @@ const uninstall = async () => {
|
||||
</span>
|
||||
</v-list-item-content>
|
||||
|
||||
<span v-if="saving" class="spinner">
|
||||
<v-progress-circular indeterminate small />
|
||||
</span>
|
||||
<template v-if="type !== 'missing'">
|
||||
<span v-if="saving" class="spinner">
|
||||
<v-progress-circular indeterminate small />
|
||||
</span>
|
||||
|
||||
<v-chip class="state" :class="state.status" small>
|
||||
{{ state.text }}
|
||||
</v-chip>
|
||||
<v-chip class="state" :class="state.status" small>
|
||||
{{ state.text }}
|
||||
</v-chip>
|
||||
|
||||
<extension-item-options
|
||||
class="options"
|
||||
:type="type"
|
||||
:status="state.status"
|
||||
:disable-locked="disableLocked"
|
||||
:uninstall-locked="uninstallLocked"
|
||||
:bundle-entry="bundleEntry"
|
||||
@toggle-status="toggleState"
|
||||
@uninstall="uninstall"
|
||||
/>
|
||||
<extension-item-options
|
||||
class="options"
|
||||
:type="type"
|
||||
:status="state.status"
|
||||
:disable-locked="disableLocked"
|
||||
:uninstall-locked="uninstallLocked"
|
||||
:bundle-entry="bundleEntry"
|
||||
@toggle-status="toggleState"
|
||||
@uninstall="uninstall"
|
||||
/>
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<v-list v-if="children.length > 0" class="nested" :class="{ partial: isPartialEnabled }">
|
||||
|
||||
@@ -16,8 +16,19 @@ const extensionsStore = useExtensionsStore();
|
||||
const { extensions, loading } = storeToRefs(extensionsStore);
|
||||
|
||||
const bundled = computed(() => extensionsStore.extensions.filter(({ bundle }) => bundle !== null));
|
||||
|
||||
const regular = computed(() => extensionsStore.extensions.filter(({ bundle }) => bundle === null));
|
||||
const extensionsByType = computed(() => groupBy(regular.value, 'schema.type'));
|
||||
|
||||
const extensionsByType = computed(() => {
|
||||
const groups = groupBy(regular.value, 'schema.type');
|
||||
|
||||
if ('undefined' in groups) {
|
||||
groups['missing'] = groups['undefined'];
|
||||
delete groups['undefined'];
|
||||
}
|
||||
|
||||
return groups;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
Reference in New Issue
Block a user