mirror of
https://github.com/directus/directus.git
synced 2026-01-30 04:07:57 -05:00
Restructure state management in setup flow
This commit is contained in:
@@ -1,11 +0,0 @@
|
||||
sonar.organization=directus
|
||||
sonar.projectKey=app-next
|
||||
|
||||
sonar.sources=src
|
||||
sonar.exclusions=src/**/*.story.ts,src/**/*.test.ts
|
||||
|
||||
sonar.javascript.lcov.reportPaths=coverage/lcov.info
|
||||
sonar.testExecutionReportPaths=coverage/sonar.xml
|
||||
|
||||
sonar.pullrequest.provider=github
|
||||
sonar.pullrequest.github.repository=directus/app-next
|
||||
@@ -1,4 +1,5 @@
|
||||
import { computed, Ref } from '@vue/composition-api';
|
||||
import { clone } from 'lodash';
|
||||
|
||||
export default function useSync<T, K extends keyof T>(
|
||||
props: T,
|
||||
@@ -8,7 +9,7 @@ export default function useSync<T, K extends keyof T>(
|
||||
): Ref<Readonly<T[K]>> {
|
||||
return computed<T[K]>({
|
||||
get() {
|
||||
return props[key];
|
||||
return clone(props[key]);
|
||||
},
|
||||
set(newVal) {
|
||||
emit(`update:${key}`, newVal);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="actions">
|
||||
<v-button secondary :to="`/settings/data-model/${collection}`">
|
||||
<v-button secondary @click="$emit('cancel')">
|
||||
{{ $t('cancel') }}
|
||||
</v-button>
|
||||
<div class="spacer" />
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<div>
|
||||
<h2 class="type-title">{{ $t('display_setup_title') }}</h2>
|
||||
|
||||
<v-fancy-select class="select" :items="selectItems" v-model="_field.system.display" />
|
||||
<v-fancy-select class="select" :items="selectItems" v-model="fieldData.system.display" />
|
||||
|
||||
<template v-if="_field.system.display">
|
||||
<template v-if="fieldData.system.display">
|
||||
<v-form
|
||||
v-if="
|
||||
selectedDisplay.options &&
|
||||
@@ -13,7 +13,7 @@
|
||||
"
|
||||
:fields="selectedDisplay.options"
|
||||
primary-key="+"
|
||||
v-model="_field.system.options"
|
||||
v-model="fieldData.system.options"
|
||||
/>
|
||||
|
||||
<v-notice v-else>
|
||||
@@ -26,24 +26,20 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from '@vue/composition-api';
|
||||
import displays from '@/displays';
|
||||
import useSync from '@/composables/use-sync';
|
||||
|
||||
import { state } from '../store';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
fieldData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const _field = useSync(props, 'fieldData', emit);
|
||||
const availabledisplays = computed(() =>
|
||||
const availableDisplays = computed(() =>
|
||||
displays.filter((display) => {
|
||||
const matchesType = display.types.includes(props.fieldData.database?.type || 'alias');
|
||||
const matchesType = display.types.includes(state.fieldData?.type || 'alias');
|
||||
const matchesRelation = true;
|
||||
|
||||
// if (props.type === 'standard') {
|
||||
@@ -61,7 +57,7 @@ export default defineComponent({
|
||||
);
|
||||
|
||||
const selectItems = computed(() =>
|
||||
availabledisplays.value.map((display) => ({
|
||||
availableDisplays.value.map((display) => ({
|
||||
text: display.name,
|
||||
value: display.id,
|
||||
icon: display.icon,
|
||||
@@ -69,10 +65,10 @@ export default defineComponent({
|
||||
);
|
||||
|
||||
const selectedDisplay = computed(() => {
|
||||
return displays.find((display) => display.id === _field.value.system.display);
|
||||
return displays.find((display) => display.id === state.fieldData.system.display);
|
||||
});
|
||||
|
||||
return { _field, selectItems, selectedDisplay };
|
||||
return { fieldData: state.fieldData, selectItems, selectedDisplay };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<div>
|
||||
<h2 class="type-title">{{ $t('interface_setup_title') }}</h2>
|
||||
|
||||
<v-fancy-select class="select" :items="selectItems" v-model="_field.system.interface" />
|
||||
<v-fancy-select class="select" :items="selectItems" v-model="fieldData.system.interface" />
|
||||
|
||||
<template v-if="_field.system.interface">
|
||||
<template v-if="fieldData.system.interface">
|
||||
<v-form
|
||||
v-if="
|
||||
selectedInterface.options &&
|
||||
@@ -13,7 +13,7 @@
|
||||
"
|
||||
:fields="selectedInterface.options"
|
||||
primary-key="+"
|
||||
v-model="_field.system.options"
|
||||
v-model="fieldData.system.options"
|
||||
/>
|
||||
|
||||
<v-notice v-else>
|
||||
@@ -26,24 +26,20 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from '@vue/composition-api';
|
||||
import interfaces from '@/interfaces';
|
||||
import useSync from '@/composables/use-sync';
|
||||
|
||||
import { state } from '../store';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
fieldData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const _field = useSync(props, 'fieldData', emit);
|
||||
const availableInterfaces = computed(() =>
|
||||
interfaces.filter((inter) => {
|
||||
const matchesType = inter.types.includes(props.fieldData.database?.type || 'alias');
|
||||
const matchesType = inter.types.includes(state.fieldData?.type || 'alias');
|
||||
let matchesRelation = false;
|
||||
|
||||
if (props.type === 'standard') {
|
||||
@@ -69,10 +65,10 @@ export default defineComponent({
|
||||
);
|
||||
|
||||
const selectedInterface = computed(() => {
|
||||
return interfaces.find((inter) => inter.id === _field.value.system.interface);
|
||||
return interfaces.find((inter) => inter.id === state.fieldData.system.interface);
|
||||
});
|
||||
|
||||
return { _field, selectItems, selectedInterface };
|
||||
return { fieldData: state.fieldData, selectItems, selectedInterface };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -12,18 +12,14 @@
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('related_collection') }}</div>
|
||||
<v-select
|
||||
:disabled="type === 'files'"
|
||||
:items="collectionItems"
|
||||
v-model="_relations[1].collection_one"
|
||||
/>
|
||||
<v-select :disabled="type === 'files'" :items="collectionItems" v-model="relations[1].collection_one" />
|
||||
</div>
|
||||
<v-input disabled :value="_relations[0].primary_one" />
|
||||
<v-select :disabled="!junctionCollection" :items="junctionFields" v-model="_relations[0].field_many" />
|
||||
<v-input disabled :value="relations[0].primary_one" />
|
||||
<v-select :disabled="!junctionCollection" :items="junctionFields" v-model="relations[0].field_many" />
|
||||
<div class="spacer" />
|
||||
<div class="spacer" />
|
||||
<v-select :disabled="!junctionCollection" :items="junctionFields" v-model="_relations[1].field_many" />
|
||||
<v-input disabled :value="_relations[1].primary_one" />
|
||||
<v-select :disabled="!junctionCollection" :items="junctionFields" v-model="relations[1].field_many" />
|
||||
<v-input disabled :value="relations[1].primary_one" />
|
||||
<v-icon name="arrow_forward" />
|
||||
<v-icon name="arrow_backward" />
|
||||
</div>
|
||||
@@ -36,31 +32,22 @@ import { orderBy } from 'lodash';
|
||||
import useCollectionsStore from '@/stores/collections';
|
||||
import useFieldsStore from '@/stores/fields';
|
||||
import { Relation } from '@/stores/relations/types';
|
||||
import useSync from '@/composables/use-sync';
|
||||
import { Field } from '@/stores/fields/types';
|
||||
|
||||
import { state } from '../store';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
relations: {
|
||||
type: Array as PropType<Relation[]>,
|
||||
required: true,
|
||||
},
|
||||
fieldData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
collection: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const _relations = useSync(props, 'relations', emit);
|
||||
|
||||
const collectionsStore = useCollectionsStore();
|
||||
const fieldsStore = useFieldsStore();
|
||||
|
||||
@@ -86,11 +73,11 @@ export default defineComponent({
|
||||
|
||||
const junctionCollection = computed({
|
||||
get() {
|
||||
return _relations.value[0].collection_many;
|
||||
return state.relations[0].collection_many;
|
||||
},
|
||||
set(collection: string) {
|
||||
_relations.value[0].collection_many = collection;
|
||||
_relations.value[1].collection_many = collection;
|
||||
state.relations[0].collection_many = collection;
|
||||
state.relations[1].collection_many = collection;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -101,11 +88,11 @@ export default defineComponent({
|
||||
text: field.field,
|
||||
value: field.field,
|
||||
disabled:
|
||||
_relations.value[0].field_many === field.field || _relations.value[1].field_many === field.field,
|
||||
state.relations[0].field_many === field.field || state.relations[1].field_many === field.field,
|
||||
}));
|
||||
});
|
||||
|
||||
return { _relations, collectionItems, junctionCollection, junctionFields };
|
||||
return { relations: state.relations, collectionItems, junctionCollection, junctionFields };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
<div class="grid">
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('this_collection') }}</div>
|
||||
<v-input disabled :value="_relations[0].collection_many" />
|
||||
<v-input disabled :value="relations[0].collection_many" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('related_collection') }}</div>
|
||||
<v-select
|
||||
:placeholder="$t('choose_a_collection')"
|
||||
:items="items"
|
||||
v-model="_relations[0].collection_one"
|
||||
v-model="relations[0].collection_one"
|
||||
/>
|
||||
</div>
|
||||
<v-input disabled :value="fieldData.field" />
|
||||
@@ -45,33 +45,20 @@ import useCollectionsStore from '@/stores/collections';
|
||||
import useFieldsStore from '@/stores/fields';
|
||||
import i18n from '@/lang';
|
||||
|
||||
import { state } from '../store';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
relations: {
|
||||
type: Array as PropType<Relation[]>,
|
||||
required: true,
|
||||
},
|
||||
newFields: {
|
||||
type: Array as PropType<DeepPartial<Field>[]>,
|
||||
required: true,
|
||||
},
|
||||
fieldData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
collection: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const _relations = useSync(props, 'relations', emit);
|
||||
const _newFields = useSync(props, 'newFields', emit);
|
||||
|
||||
const collectionsStore = useCollectionsStore();
|
||||
const fieldsStore = useFieldsStore();
|
||||
|
||||
@@ -79,13 +66,13 @@ export default defineComponent({
|
||||
const { hasCorresponding, correspondingField, correspondingLabel } = useCorresponding();
|
||||
|
||||
return {
|
||||
_relations,
|
||||
_newFields,
|
||||
relations: state.relations,
|
||||
items,
|
||||
relatedPrimary,
|
||||
hasCorresponding,
|
||||
correspondingField,
|
||||
correspondingLabel,
|
||||
fieldData: state.fieldData,
|
||||
};
|
||||
|
||||
function useRelation() {
|
||||
@@ -110,8 +97,8 @@ export default defineComponent({
|
||||
);
|
||||
|
||||
const relatedPrimary = computed(() => {
|
||||
return _relations.value[0].collection_one
|
||||
? fieldsStore.getPrimaryKeyFieldForCollection(_relations.value[0].collection_one)?.field
|
||||
return state.relations[0].collection_one
|
||||
? fieldsStore.getPrimaryKeyFieldForCollection(state.relations[0].collection_one)?.field
|
||||
: null;
|
||||
});
|
||||
|
||||
@@ -121,14 +108,14 @@ export default defineComponent({
|
||||
function useCorresponding() {
|
||||
const hasCorresponding = computed({
|
||||
get() {
|
||||
return _newFields.value.length > 0;
|
||||
return state.newFields.length > 0;
|
||||
},
|
||||
set(enabled: boolean) {
|
||||
if (enabled === true) {
|
||||
_newFields.value = [
|
||||
state.newFields = [
|
||||
{
|
||||
field: '',
|
||||
collection: _relations.value[0].collection_one,
|
||||
collection: state.relations[0].collection_one,
|
||||
system: {
|
||||
special: 'o2m',
|
||||
interface: 'one-to-many',
|
||||
@@ -136,26 +123,26 @@ export default defineComponent({
|
||||
},
|
||||
];
|
||||
} else {
|
||||
_newFields.value = [];
|
||||
state.newFields = [];
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const correspondingField = computed({
|
||||
get() {
|
||||
return _newFields.value?.[0]?.field || null;
|
||||
return state.newFields?.[0]?.field || null;
|
||||
},
|
||||
set(field: string | null) {
|
||||
_newFields.value = [
|
||||
state.newFields = [
|
||||
{
|
||||
...(_newFields.value[0] || {}),
|
||||
...(state.newFields[0] || {}),
|
||||
field: field || '',
|
||||
},
|
||||
];
|
||||
|
||||
_relations.value = [
|
||||
state.relations = [
|
||||
{
|
||||
..._relations.value[0],
|
||||
...state.relations[0],
|
||||
field_one: field,
|
||||
},
|
||||
];
|
||||
@@ -163,8 +150,8 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const correspondingLabel = computed(() => {
|
||||
if (_relations.value[0].collection_one) {
|
||||
return i18n.t('add_o2m_to_collection', { collection: _relations.value[0].collection_one });
|
||||
if (state.relations[0].collection_one) {
|
||||
return i18n.t('add_o2m_to_collection', { collection: state.relations[0].collection_one });
|
||||
}
|
||||
|
||||
return i18n.t('add_field_related');
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
</div>
|
||||
<v-input disabled :value="currentCollectionPrimaryKey.field" />
|
||||
<v-select
|
||||
v-model="_relations[0].field_many"
|
||||
:disabled="!_relations[0].collection_many"
|
||||
v-model="relations[0].field_many"
|
||||
:disabled="!relations[0].collection_many"
|
||||
:items="fields"
|
||||
:placeholder="!_relations[0].collection_many ? $t('choose_a_collection') : $t('choose_a_field')"
|
||||
:placeholder="!relations[0].collection_many ? $t('choose_a_collection') : $t('choose_a_field')"
|
||||
/>
|
||||
<v-icon name="arrow_forward" />
|
||||
</div>
|
||||
@@ -32,39 +32,26 @@ import useFieldsStore from '@/stores/fields';
|
||||
import { orderBy } from 'lodash';
|
||||
import i18n from '@/lang';
|
||||
|
||||
import { state } from '../store';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
relations: {
|
||||
type: Array as PropType<Relation[]>,
|
||||
required: true,
|
||||
},
|
||||
newFields: {
|
||||
type: Array as PropType<DeepPartial<Field>[]>,
|
||||
required: true,
|
||||
},
|
||||
fieldData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
collection: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const _relations = useSync(props, 'relations', emit);
|
||||
const _newFields = useSync(props, 'newFields', emit);
|
||||
|
||||
const collectionsStore = useCollectionsStore();
|
||||
const fieldsStore = useFieldsStore();
|
||||
|
||||
const { items, fields, currentCollectionPrimaryKey, collectionMany } = useRelation();
|
||||
|
||||
return { _relations, items, fields, currentCollectionPrimaryKey, collectionMany };
|
||||
return { relations: state.relations, items, fields, currentCollectionPrimaryKey, collectionMany };
|
||||
|
||||
function useRelation() {
|
||||
const availableCollections = computed(() => {
|
||||
@@ -92,16 +79,15 @@ export default defineComponent({
|
||||
);
|
||||
|
||||
const fields = computed(() => {
|
||||
if (!_relations.value[0].collection_many) return [];
|
||||
if (!state.relations[0].collection_many) return [];
|
||||
|
||||
return fieldsStore.state.fields
|
||||
.filter((field) => {
|
||||
if (field.collection !== _relations.value[0].collection_many) return false;
|
||||
if (field.collection !== state.relations[0].collection_many) return false;
|
||||
|
||||
// Make sure the selected field matches the type of primary key of the current
|
||||
// collection. Otherwise you aren't able to properly save the primary key
|
||||
if (!field.database || field.database.type !== currentCollectionPrimaryKey.value.database.type)
|
||||
return false;
|
||||
if (!field.database || field.type !== currentCollectionPrimaryKey.value.type) return false;
|
||||
|
||||
return true;
|
||||
})
|
||||
@@ -110,49 +96,16 @@ export default defineComponent({
|
||||
|
||||
const collectionMany = computed({
|
||||
get() {
|
||||
return _relations.value[0].collection_many;
|
||||
return state.relations[0].collection_many!;
|
||||
},
|
||||
set(collection: string) {
|
||||
_relations.value[0].collection_many = collection;
|
||||
_relations.value[0].field_many = '';
|
||||
state.relations[0].collection_many = collection;
|
||||
state.relations[0].field_many = '';
|
||||
},
|
||||
});
|
||||
|
||||
return { availableCollections, items, fields, currentCollectionPrimaryKey, collectionMany };
|
||||
}
|
||||
|
||||
function useCorresponding() {
|
||||
const hasCorresponding = computed({
|
||||
get() {
|
||||
return _newFields.value.length > 0;
|
||||
},
|
||||
set(enabled: boolean) {
|
||||
if (enabled === true) {
|
||||
_newFields.value = [
|
||||
{
|
||||
field: _relations.value[0].field_many,
|
||||
collection: _relations.value[0].collection_many,
|
||||
system: {
|
||||
interface: 'many-to-one',
|
||||
},
|
||||
},
|
||||
];
|
||||
} else {
|
||||
_newFields.value = [];
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const correspondingLabel = computed(() => {
|
||||
if (_relations.value[0].collection_many) {
|
||||
return i18n.t('add_m2o_to_collection', { collection: _relations.value[0].collection_many });
|
||||
}
|
||||
|
||||
return i18n.t('add_field_related');
|
||||
});
|
||||
|
||||
return { hasCorresponding, correspondingLabel };
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,35 +1,13 @@
|
||||
<template>
|
||||
<relationship-m2o
|
||||
:collection="collection"
|
||||
:field-data="fieldData"
|
||||
:relations.sync="_relations"
|
||||
:new-fields.sync="_newFields"
|
||||
:type="type"
|
||||
v-if="type === 'm2o' || type === 'file'"
|
||||
/>
|
||||
<relationship-o2m
|
||||
:collection="collection"
|
||||
:field-data="fieldData"
|
||||
:relations.sync="_relations"
|
||||
:new-fields.sync="_newFields"
|
||||
:type="type"
|
||||
v-else-if="type === 'o2m'"
|
||||
/>
|
||||
<relationship-m2m
|
||||
:collection="collection"
|
||||
:field-data="fieldData"
|
||||
:relations.sync="_relations"
|
||||
:new-fields.sync="_newFields"
|
||||
:type="type"
|
||||
v-else-if="type === 'm2m' || type === 'files'"
|
||||
/>
|
||||
<relationship-m2o :collection="collection" :type="type" v-if="type === 'm2o' || type === 'file'" />
|
||||
<relationship-o2m :collection="collection" :type="type" v-else-if="type === 'o2m'" />
|
||||
<relationship-m2m :collection="collection" :type="type" v-else-if="type === 'm2m' || type === 'files'" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from '@vue/composition-api';
|
||||
import { Relation } from '@/stores/relations/types';
|
||||
import { Field } from '@/stores/fields/types';
|
||||
import useSync from '@/composables/use-sync';
|
||||
|
||||
import RelationshipM2o from './relationship-m2o.vue';
|
||||
import RelationshipO2m from './relationship-o2m.vue';
|
||||
@@ -46,28 +24,10 @@ export default defineComponent({
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
relations: {
|
||||
type: Array as PropType<Relation[]>,
|
||||
required: true,
|
||||
},
|
||||
newFields: {
|
||||
type: Array as PropType<DeepPartial<Field>[]>,
|
||||
required: true,
|
||||
},
|
||||
fieldData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
collection: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const _relations = useSync(props, 'relations', emit);
|
||||
const _newFields = useSync(props, 'newFields', emit);
|
||||
|
||||
return { _relations, _newFields };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -5,16 +5,16 @@
|
||||
<div class="form">
|
||||
<div class="field">
|
||||
<div class="label type-label">{{ $t('key') }}</div>
|
||||
<v-input autofocus class="monospace" v-model="_field.field" db-safe />
|
||||
<v-input :disabled="isExisting" autofocus class="monospace" v-model="fieldData.field" db-safe />
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<div class="label type-label">{{ $t('type') }}</div>
|
||||
<v-input v-if="!_field.database" :value="$t('alias')" disabled />
|
||||
<v-input v-if="!fieldData.database" :value="$t('alias')" disabled />
|
||||
<v-select
|
||||
v-else
|
||||
:disabled="typeDisabled"
|
||||
:value="_field.database.type"
|
||||
:disabled="typeDisabled || isExisting"
|
||||
:value="fieldData.type"
|
||||
@input="setType"
|
||||
:items="typesWithLabels"
|
||||
:placeholder="typePlaceholder"
|
||||
@@ -23,32 +23,32 @@
|
||||
|
||||
<div class="field full">
|
||||
<div class="label type-label">{{ $t('note') }}</div>
|
||||
<v-input v-model="_field.system.comment" :placeholder="$t('add_note')" />
|
||||
<v-input v-model="fieldData.system.comment" :placeholder="$t('add_note')" />
|
||||
</div>
|
||||
|
||||
<!-- @todo base default value field type on selected type -->
|
||||
<div class="field" v-if="_field.database">
|
||||
<div class="field" v-if="fieldData.database">
|
||||
<div class="label type-label">{{ $t('default_value') }}</div>
|
||||
<v-input
|
||||
class="monospace"
|
||||
v-model="_field.database.default_value"
|
||||
v-model="fieldData.database.default_value"
|
||||
:placeholder="$t('add_a_default_value')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="field" v-if="_field.database">
|
||||
<div class="field" v-if="fieldData.database">
|
||||
<div class="label type-label">{{ $t('length') }}</div>
|
||||
<v-input
|
||||
type="number"
|
||||
:placeholder="_field.database.type !== 'string' ? $t('not_available_for_type') : '255'"
|
||||
:disabled="_field.database.type !== 'string'"
|
||||
v-model="_field.database.max_length"
|
||||
:placeholder="fieldData.type !== 'string' ? $t('not_available_for_type') : '255'"
|
||||
:disabled="isExisting || fieldData.type !== 'string'"
|
||||
v-model="fieldData.database.max_length"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="field" v-if="_field.database">
|
||||
<div class="field" v-if="fieldData.database">
|
||||
<div class="label type-label">{{ $t('allow_null') }}</div>
|
||||
<v-checkbox v-model="_field.database.is_nullable" :label="$t('allow_null_label')" block />
|
||||
<v-checkbox v-model="fieldData.database.is_nullable" :label="$t('allow_null_label')" block />
|
||||
</div>
|
||||
|
||||
<!--
|
||||
@@ -56,7 +56,7 @@
|
||||
|
||||
<div class="field">
|
||||
<div class="label type-label">{{ $t('unique') }}</div>
|
||||
<v-input v-model="_field.database.unique" />
|
||||
<v-input v-model="fieldData.database.unique" />
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
@@ -67,11 +67,12 @@ import { defineComponent, computed } from '@vue/composition-api';
|
||||
import useSync from '@/composables/use-sync';
|
||||
import { types } from '@/stores/fields/types';
|
||||
import i18n from '@/lang';
|
||||
import { state } from '../store';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
fieldData: {
|
||||
type: Object,
|
||||
isExisting: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
@@ -80,8 +81,6 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const _field = useSync(props, 'fieldData', emit);
|
||||
|
||||
const typesWithLabels = computed(() =>
|
||||
types
|
||||
.filter((type) => {
|
||||
@@ -113,23 +112,22 @@ export default defineComponent({
|
||||
return i18n.t('choose_a_type');
|
||||
});
|
||||
|
||||
return { _field, typesWithLabels, setType, typeDisabled, typePlaceholder };
|
||||
return { fieldData: state.fieldData, typesWithLabels, setType, typeDisabled, typePlaceholder };
|
||||
|
||||
function setType(value: typeof types[number]) {
|
||||
if (value === 'uuid') {
|
||||
_field.value.system.special = 'uuid';
|
||||
state.fieldData.system.special = 'uuid';
|
||||
} else {
|
||||
_field.value.system.special = null;
|
||||
state.fieldData.system.special = null;
|
||||
}
|
||||
|
||||
// We'll reset the interface/display as they most likely won't work for the newly selected
|
||||
// type
|
||||
_field.value.system.interface = null;
|
||||
_field.value.system.options = null;
|
||||
_field.value.system.display = null;
|
||||
_field.value.system.display_options = null;
|
||||
|
||||
_field.value.database.type = value;
|
||||
state.fieldData.system.interface = null;
|
||||
state.fieldData.system.options = null;
|
||||
state.fieldData.system.display = null;
|
||||
state.fieldData.system.display_options = null;
|
||||
state.fieldData.type = value;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,37 +1,35 @@
|
||||
<template>
|
||||
<v-modal :active="active" title="Test" persistent>
|
||||
<template #sidebar>
|
||||
<setup-tabs :current.sync="currentTab" :tabs="tabs" :type="type" />
|
||||
<setup-tabs :current.sync="currentTab" :tabs="tabs" :type="localType" />
|
||||
</template>
|
||||
|
||||
<setup-schema
|
||||
:collection="collection"
|
||||
v-if="currentTab[0] === 'schema'"
|
||||
:field-data.sync="fieldData"
|
||||
:type="type"
|
||||
:is-existing="field !== '+'"
|
||||
:collection="collection"
|
||||
:type="localType"
|
||||
/>
|
||||
|
||||
<setup-relationship
|
||||
:collection="collection"
|
||||
v-if="currentTab[0] === 'relationship'"
|
||||
:field-data.sync="fieldData"
|
||||
:relations.sync="relations"
|
||||
:new-fields.sync="newFields"
|
||||
:type="type"
|
||||
:is-existing="field !== '+'"
|
||||
:collection="collection"
|
||||
:type="localType"
|
||||
/>
|
||||
|
||||
<setup-interface
|
||||
:collection="collection"
|
||||
v-if="currentTab[0] === 'interface'"
|
||||
:field-data.sync="fieldData"
|
||||
:type="type"
|
||||
:is-existing="field !== '+'"
|
||||
:collection="collection"
|
||||
:type="localType"
|
||||
/>
|
||||
|
||||
<setup-display
|
||||
:collection="collection"
|
||||
v-if="currentTab[0] === 'display'"
|
||||
:field-data.sync="fieldData"
|
||||
:type="type"
|
||||
:is-existing="field !== '+'"
|
||||
:collection="collection"
|
||||
:type="localType"
|
||||
/>
|
||||
|
||||
<template #footer>
|
||||
@@ -41,6 +39,7 @@
|
||||
:current.sync="currentTab"
|
||||
:tabs="tabs"
|
||||
@save="saveField"
|
||||
@cancel="cancelField"
|
||||
/>
|
||||
</template>
|
||||
</v-modal>
|
||||
@@ -59,9 +58,12 @@ import { isEmpty } from 'lodash';
|
||||
import api from '@/api';
|
||||
import { Relation } from '@/stores/relations/types';
|
||||
import { useFieldsStore } from '@/stores/fields';
|
||||
import { useRelationsStore } from '@/stores/relations';
|
||||
import { Field } from '@/stores/fields/types';
|
||||
import router from '@/router';
|
||||
|
||||
import { initLocalStore, state, clearLocalStore } from './store';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
SetupTabs,
|
||||
@@ -86,20 +88,45 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const fieldsStore = useFieldsStore();
|
||||
const relationsStore = useRelationsStore();
|
||||
|
||||
const localType = computed(() => {
|
||||
if (props.field === '+') return props.type;
|
||||
|
||||
let type: 'standard' | 'file' | 'files' | 'o2m' | 'm2m' | 'm2o' = 'standard';
|
||||
|
||||
const existingField = fieldsStore.getField(props.collection, props.field);
|
||||
type = getLocalTypeForField(props.collection, props.field);
|
||||
|
||||
return type;
|
||||
});
|
||||
|
||||
// This makes sure we still see the enter animation
|
||||
/** @todo fix this in the transition */
|
||||
const active = ref(false);
|
||||
onMounted(() => {
|
||||
active.value = true;
|
||||
});
|
||||
|
||||
const fieldsStore = useFieldsStore();
|
||||
initLocalStore(props.collection, props.field, localType.value);
|
||||
|
||||
const { tabs, currentTab } = useTabs();
|
||||
const { fieldData, relations, newFields } = useData();
|
||||
|
||||
const saving = ref(false);
|
||||
|
||||
return { active, tabs, currentTab, fieldData, saveField, saving, relations, newFields };
|
||||
return {
|
||||
active,
|
||||
tabs,
|
||||
currentTab,
|
||||
fieldData: state.fieldData,
|
||||
saveField,
|
||||
saving,
|
||||
relations: state.relations,
|
||||
newFields: state.newFields,
|
||||
cancelField,
|
||||
localType,
|
||||
};
|
||||
|
||||
function useTabs() {
|
||||
const tabs = computed(() => {
|
||||
@@ -121,7 +148,7 @@ export default defineComponent({
|
||||
},
|
||||
];
|
||||
|
||||
if (['o2m', 'm2o', 'm2m', 'files'].includes(props.type)) {
|
||||
if (['o2m', 'm2o', 'm2m', 'files'].includes(localType.value)) {
|
||||
tabs.splice(1, 0, {
|
||||
text: i18n.t('relationship'),
|
||||
value: 'relationship',
|
||||
@@ -138,241 +165,99 @@ export default defineComponent({
|
||||
|
||||
function relationshipDisabled() {
|
||||
return (
|
||||
isEmpty(fieldData.field) ||
|
||||
(['o2m', 'm2m', 'files', 'm2o'].includes(props.type) === false && isEmpty(fieldData.database.type))
|
||||
isEmpty(state.fieldData.field) ||
|
||||
(['o2m', 'm2m', 'files', 'm2o'].includes(localType.value) === false &&
|
||||
isEmpty(state.fieldData.type))
|
||||
);
|
||||
}
|
||||
|
||||
function interfaceDisplayDisabled() {
|
||||
if (['o2m', 'm2o', 'file'].includes(props.type)) {
|
||||
if (['o2m', 'm2o', 'file'].includes(localType.value)) {
|
||||
return (
|
||||
relations.value.length === 0 ||
|
||||
isEmpty(relations.value[0].collection_many) ||
|
||||
isEmpty(relations.value[0].field_many) ||
|
||||
isEmpty(relations.value[0].collection_one)
|
||||
state.relations.length === 0 ||
|
||||
isEmpty(state.relations[0].collection_many) ||
|
||||
isEmpty(state.relations[0].field_many) ||
|
||||
isEmpty(state.relations[0].collection_one)
|
||||
);
|
||||
}
|
||||
|
||||
if (['m2m', 'files'].includes(props.type)) {
|
||||
if (['m2m', 'files'].includes(localType.value)) {
|
||||
return (
|
||||
relations.value.length !== 2 ||
|
||||
isEmpty(relations.value[0].collection_many) ||
|
||||
isEmpty(relations.value[0].field_many) ||
|
||||
isEmpty(relations.value[0].field_one) ||
|
||||
isEmpty(relations.value[1].collection_many) ||
|
||||
isEmpty(relations.value[1].field_many) ||
|
||||
isEmpty(relations.value[1].collection_one)
|
||||
state.relations.length !== 2 ||
|
||||
isEmpty(state.relations[0].collection_many) ||
|
||||
isEmpty(state.relations[0].field_many) ||
|
||||
isEmpty(state.relations[0].field_one) ||
|
||||
isEmpty(state.relations[1].collection_many) ||
|
||||
isEmpty(state.relations[1].field_many) ||
|
||||
isEmpty(state.relations[1].collection_one)
|
||||
);
|
||||
}
|
||||
|
||||
return isEmpty(fieldData.field) || isEmpty(fieldData.database.type);
|
||||
return isEmpty(state.fieldData.field) || isEmpty(state.fieldData.type);
|
||||
}
|
||||
}
|
||||
|
||||
function useData() {
|
||||
/** @todo this should technically be a DeepPartial<Field>, but that's a bit annoying to deal with rn */
|
||||
const fieldData = reactive<any>({
|
||||
field: '',
|
||||
database: {
|
||||
type: undefined,
|
||||
default_value: undefined,
|
||||
max_length: undefined,
|
||||
is_nullable: true,
|
||||
},
|
||||
system: {
|
||||
hidden: false,
|
||||
interface: undefined,
|
||||
options: undefined,
|
||||
display: undefined,
|
||||
display_options: undefined,
|
||||
readonly: false,
|
||||
special: undefined,
|
||||
note: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const relations = ref<Partial<Relation>[]>([]);
|
||||
|
||||
// Allow the panes to create additional fields outside of this one. This is used to
|
||||
// auto generated related o2m columns / junction collections etc
|
||||
const newFields = ref<DeepPartial<Field>[]>([]);
|
||||
|
||||
if (props.type === 'file') {
|
||||
fieldData.database.type = 'uuid';
|
||||
|
||||
relations.value = [
|
||||
{
|
||||
collection_many: props.collection,
|
||||
field_many: '',
|
||||
primary_many: fieldsStore.getPrimaryKeyFieldForCollection(props.collection)?.field,
|
||||
collection_one: 'directus_files',
|
||||
primary_one: fieldsStore.getPrimaryKeyFieldForCollection('directus_files')?.field,
|
||||
},
|
||||
];
|
||||
|
||||
watch(
|
||||
() => fieldData.field,
|
||||
() => {
|
||||
relations.value[0].field_many = fieldData.field;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (props.type === 'm2o') {
|
||||
relations.value = [
|
||||
{
|
||||
collection_many: props.collection,
|
||||
field_many: '',
|
||||
primary_many: fieldsStore.getPrimaryKeyFieldForCollection(props.collection)?.field,
|
||||
collection_one: '',
|
||||
primary_one: fieldsStore.getPrimaryKeyFieldForCollection('directus_files')?.field,
|
||||
},
|
||||
];
|
||||
|
||||
watch(
|
||||
() => fieldData.field,
|
||||
() => {
|
||||
relations.value[0].field_many = fieldData.field;
|
||||
}
|
||||
);
|
||||
|
||||
// Make sure to keep the current m2o field type in sync with the primary key of the
|
||||
// selected related collection
|
||||
watch(
|
||||
() => relations.value[0].collection_one,
|
||||
() => {
|
||||
const field = fieldsStore.getPrimaryKeyFieldForCollection(relations.value[0].collection_one);
|
||||
fieldData.database.type = field.database.type;
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => relations.value[0].collection_one,
|
||||
() => {
|
||||
if (newFields.value.length > 0) {
|
||||
newFields.value[0].collection = relations.value[0].collection_one;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (props.type === 'o2m') {
|
||||
delete fieldData.database;
|
||||
|
||||
fieldData.system.special = 'o2m';
|
||||
|
||||
relations.value = [
|
||||
{
|
||||
collection_many: '',
|
||||
field_many: '',
|
||||
primary_many: '',
|
||||
|
||||
collection_one: props.collection,
|
||||
field_one: fieldData.field,
|
||||
primary_one: fieldsStore.getPrimaryKeyFieldForCollection(props.collection)?.field,
|
||||
},
|
||||
];
|
||||
|
||||
watch(
|
||||
() => fieldData.field,
|
||||
() => {
|
||||
relations.value[0].field_one = fieldData.field;
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => relations.value[0].collection_many,
|
||||
() => {
|
||||
relations.value[0].primary_many = fieldsStore.getPrimaryKeyFieldForCollection(
|
||||
relations.value[0].collection_many
|
||||
).field;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (props.type === 'm2m' || props.type === 'files') {
|
||||
delete fieldData.database;
|
||||
|
||||
fieldData.system.special = 'm2m';
|
||||
|
||||
relations.value = [
|
||||
{
|
||||
collection_many: '',
|
||||
field_many: '',
|
||||
primary_many: '',
|
||||
collection_one: props.collection,
|
||||
field_one: fieldData.field,
|
||||
primary_one: fieldsStore.getPrimaryKeyFieldForCollection(props.collection)?.field,
|
||||
},
|
||||
{
|
||||
collection_many: '',
|
||||
field_many: '',
|
||||
primary_many: '',
|
||||
collection_one: props.type === 'files' ? 'directus_files' : '',
|
||||
field_one: null,
|
||||
primary_one:
|
||||
props.type === 'files'
|
||||
? fieldsStore.getPrimaryKeyFieldForCollection('directus_files')?.field
|
||||
: '',
|
||||
},
|
||||
];
|
||||
|
||||
watch(
|
||||
() => fieldData.field,
|
||||
() => {
|
||||
relations.value[0].field_one = fieldData.field;
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => relations.value[0].collection_many,
|
||||
() => {
|
||||
const pkField = fieldsStore.getPrimaryKeyFieldForCollection(relations.value[0].collection_many)
|
||||
?.field;
|
||||
relations.value[0].primary_many = pkField;
|
||||
relations.value[1].primary_many = pkField;
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => relations.value[0].field_many,
|
||||
() => {
|
||||
relations.value[1].junction_field = relations.value[0].field_many;
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => relations.value[1].field_many,
|
||||
() => {
|
||||
relations.value[0].junction_field = relations.value[1].field_many;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return { fieldData, relations, newFields };
|
||||
}
|
||||
|
||||
async function saveField() {
|
||||
saving.value = true;
|
||||
|
||||
try {
|
||||
await api.post(`/fields/${props.collection}`, fieldData);
|
||||
if (props.field !== '+') {
|
||||
await api.patch(`/fields/${props.collection}/${props.field}`, state.fieldData);
|
||||
} else {
|
||||
await api.post(`/fields/${props.collection}`, state.fieldData);
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
newFields.value.map((newField) => {
|
||||
state.newFields.map((newField: Partial<Field>) => {
|
||||
return api.post(`/fields/${newField.collection}`, newField);
|
||||
})
|
||||
);
|
||||
|
||||
await api.post(`/relations`, relations.value);
|
||||
await api.post(`/relations`, state.relations);
|
||||
|
||||
router.push(`/settings/data-model/${props.collection}`);
|
||||
clearLocalStore();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function cancelField() {
|
||||
router.push(`/settings/data-model/${props.collection}`);
|
||||
clearLocalStore();
|
||||
}
|
||||
|
||||
function getLocalTypeForField(
|
||||
collection: string,
|
||||
field: string
|
||||
): 'standard' | 'file' | 'files' | 'o2m' | 'm2m' | 'm2o' {
|
||||
const fieldInfo = fieldsStore.getField(collection, field);
|
||||
const relations = relationsStore.getRelationsForField(collection, field);
|
||||
|
||||
if (relations.length === 0) return 'standard';
|
||||
|
||||
if (relations.length === 1) {
|
||||
const relation = relations[0];
|
||||
if (relation.collection_one === 'directus_files') return 'file';
|
||||
if (relation.collection_many === collection) return 'm2o';
|
||||
return 'o2m';
|
||||
}
|
||||
|
||||
if (relations.length === 2) {
|
||||
if (
|
||||
relations[0].collection_one === 'directus_files' ||
|
||||
relations[1].collection_one === 'directus_files'
|
||||
) {
|
||||
return 'files';
|
||||
} else {
|
||||
return 'm2m';
|
||||
}
|
||||
}
|
||||
|
||||
return 'standard';
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
240
src/modules/settings/routes/data-model/field-detail/store.ts
Normal file
240
src/modules/settings/routes/data-model/field-detail/store.ts
Normal file
@@ -0,0 +1,240 @@
|
||||
/**
|
||||
* This is a "local store" meant to make the field data shareable between the different panes
|
||||
* and components within the field setup modal flow.
|
||||
*
|
||||
* It's reset every time the modal opens and shouldn't be used outside of the field-detail flow.
|
||||
*/
|
||||
|
||||
import useFieldsStore from '@/stores/fields';
|
||||
import useRelationsStore from '@/stores/relations';
|
||||
import { reactive, watch } from '@vue/composition-api';
|
||||
|
||||
const fieldsStore = useFieldsStore();
|
||||
const relationsStore = useRelationsStore();
|
||||
|
||||
const state = reactive<any>({
|
||||
fieldData: {
|
||||
field: '',
|
||||
type: '',
|
||||
database: {
|
||||
default_value: undefined,
|
||||
max_length: undefined,
|
||||
is_nullable: true,
|
||||
},
|
||||
system: {
|
||||
hidden: false,
|
||||
interface: undefined,
|
||||
options: undefined,
|
||||
display: undefined,
|
||||
display_options: undefined,
|
||||
readonly: false,
|
||||
special: undefined,
|
||||
note: undefined,
|
||||
},
|
||||
},
|
||||
relations: [],
|
||||
newFields: [],
|
||||
});
|
||||
|
||||
export { state, initLocalStore, clearLocalStore };
|
||||
|
||||
function initLocalStore(
|
||||
collection: string,
|
||||
field: string,
|
||||
type: 'standard' | 'file' | 'files' | 'm2o' | 'o2m' | 'm2m'
|
||||
) {
|
||||
const isExisting = field !== '+';
|
||||
|
||||
if (isExisting) {
|
||||
const existingField = fieldsStore.getField(collection, field);
|
||||
|
||||
state.fieldData.field = existingField.field;
|
||||
state.fieldData.type = existingField.type;
|
||||
state.fieldData.database = existingField.database;
|
||||
state.fieldData.system = existingField.system;
|
||||
|
||||
state.relations = relationsStore.getRelationsForField(collection, field);
|
||||
}
|
||||
|
||||
if (type === 'file') {
|
||||
if (!isExisting) {
|
||||
state.fieldData.type = 'uuid';
|
||||
|
||||
state.relations = [
|
||||
{
|
||||
collection_many: collection,
|
||||
field_many: '',
|
||||
primary_many: fieldsStore.getPrimaryKeyFieldForCollection(collection)?.field,
|
||||
collection_one: 'directus_files',
|
||||
primary_one: fieldsStore.getPrimaryKeyFieldForCollection('directus_files')?.field,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
watch(
|
||||
() => state.fieldData.field,
|
||||
() => {
|
||||
state.relations[0].field_many = state.fieldData.field;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (type === 'm2o') {
|
||||
if (!isExisting) {
|
||||
state.relations = [
|
||||
{
|
||||
collection_many: collection,
|
||||
field_many: '',
|
||||
primary_many: fieldsStore.getPrimaryKeyFieldForCollection(collection)?.field,
|
||||
collection_one: '',
|
||||
primary_one: fieldsStore.getPrimaryKeyFieldForCollection('directus_files')?.field,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
watch(
|
||||
() => state.fieldData.field,
|
||||
() => {
|
||||
state.relations[0].field_many = state.fieldData.field;
|
||||
}
|
||||
);
|
||||
|
||||
// Make sure to keep the current m2o field type in sync with the primary key of the
|
||||
// selected related collection
|
||||
watch(
|
||||
() => state.relations[0].collection_one,
|
||||
() => {
|
||||
const field = fieldsStore.getPrimaryKeyFieldForCollection(state.relations[0].collection_one);
|
||||
state.fieldData.type = field.type;
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => state.relations[0].collection_one,
|
||||
() => {
|
||||
if (state.newFields.length > 0) {
|
||||
state.newFields[0].collection = state.relations[0].collection_one;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (type === 'o2m') {
|
||||
delete state.fieldData.database;
|
||||
|
||||
if (!isExisting) {
|
||||
state.fieldData.system.special = 'o2m';
|
||||
|
||||
state.relations.push = [
|
||||
{
|
||||
collection_many: '',
|
||||
field_many: '',
|
||||
primary_many: '',
|
||||
|
||||
collection_one: collection,
|
||||
field_one: state.fieldData.field,
|
||||
primary_one: fieldsStore.getPrimaryKeyFieldForCollection(collection)?.field,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
watch(
|
||||
() => state.fieldData.field,
|
||||
() => {
|
||||
state.relations[0].field_one = state.fieldData.field;
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => state.relations[0].collection_many,
|
||||
() => {
|
||||
state.relations[0].primary_many = fieldsStore.getPrimaryKeyFieldForCollection(
|
||||
state.relations[0].collection_many
|
||||
).field;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (type === 'm2m' || type === 'files') {
|
||||
delete state.fieldData.database;
|
||||
|
||||
if (!isExisting) {
|
||||
state.fieldData.system.special = 'm2m';
|
||||
|
||||
state.relations = [
|
||||
{
|
||||
collection_many: '',
|
||||
field_many: '',
|
||||
primary_many: '',
|
||||
collection_one: collection,
|
||||
field_one: state.fieldData.field,
|
||||
primary_one: fieldsStore.getPrimaryKeyFieldForCollection(collection)?.field,
|
||||
},
|
||||
{
|
||||
collection_many: '',
|
||||
field_many: '',
|
||||
primary_many: '',
|
||||
collection_one: type === 'files' ? 'directus_files' : '',
|
||||
field_one: null,
|
||||
primary_one:
|
||||
type === 'files' ? fieldsStore.getPrimaryKeyFieldForCollection('directus_files')?.field : '',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
watch(
|
||||
() => state.fieldData.field,
|
||||
() => {
|
||||
state.relations[0].field_one = state.fieldData.field;
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => state.relations[0].collection_many,
|
||||
() => {
|
||||
const pkField = fieldsStore.getPrimaryKeyFieldForCollection(state.relations[0].collection_many)?.field;
|
||||
state.relations[0].primary_many = pkField;
|
||||
state.relations[1].primary_many = pkField;
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => state.relations[0].field_many,
|
||||
() => {
|
||||
state.relations[1].junction_field = state.relations[0].field_many;
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => state.relations[1].field_many,
|
||||
() => {
|
||||
state.relations[0].junction_field = state.relations[1].field_many;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function clearLocalStore() {
|
||||
state.fieldData = {
|
||||
field: '',
|
||||
type: '',
|
||||
database: {
|
||||
default_value: undefined,
|
||||
max_length: undefined,
|
||||
is_nullable: true,
|
||||
},
|
||||
system: {
|
||||
hidden: false,
|
||||
interface: undefined,
|
||||
options: undefined,
|
||||
display: undefined,
|
||||
display_options: undefined,
|
||||
readonly: false,
|
||||
special: undefined,
|
||||
note: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
state.relations = [];
|
||||
state.newFields = [];
|
||||
}
|
||||
@@ -88,7 +88,6 @@ export const useFieldsStore = createStore({
|
||||
parseField(field: FieldRaw): Field {
|
||||
let name: string | VueI18n.TranslateResult;
|
||||
|
||||
const type = field.database === null ? 'alias' : getLocalType(field.database.type);
|
||||
const system = field.system === null ? getSystemDefault(field.collection, field.field) : field.system;
|
||||
|
||||
if (notEmpty(system.translation) && system.translation.length > 0) {
|
||||
@@ -110,7 +109,6 @@ export const useFieldsStore = createStore({
|
||||
return {
|
||||
...field,
|
||||
name,
|
||||
type,
|
||||
system,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -68,6 +68,7 @@ export type SystemField = {
|
||||
export interface FieldRaw {
|
||||
collection: string;
|
||||
field: string;
|
||||
type: typeof types[number];
|
||||
|
||||
database: DatabaseColumn | null;
|
||||
system: SystemField | null;
|
||||
|
||||
Reference in New Issue
Block a user