Move sortField to relationship setup (#4304)

* Add migration

* Add sort field to relation types

* Remove sortfield options in favor of relationship

* Add sort field configuration to relational setup

* Save m2a sortfield on the correct row

* Add default sort field to system data
This commit is contained in:
Rijk van Zanten
2021-02-25 19:13:36 -05:00
committed by GitHub
parent f088074d48
commit db30acbb8a
23 changed files with 611 additions and 754 deletions

View File

@@ -0,0 +1,33 @@
import Knex from 'knex';
export async function up(knex: Knex) {
await knex.schema.alterTable('directus_relations', (table) => {
table.string('sort_field');
});
const fieldsWithSort = await knex
.select('collection', 'field', 'options')
.from('directus_fields')
.whereIn('interface', ['one-to-many', 'm2a-builder', 'many-to-many']);
for (const field of fieldsWithSort) {
const options = typeof field.options === 'string' ? JSON.parse(field.options) : field.options ?? {};
if ('sortField' in options) {
await knex('directus_relations')
.update({
sort_field: options.sortField,
})
.where({
one_collection: field.collection,
one_field: field.field,
});
}
}
}
export async function down(knex: Knex) {
await knex.schema.alterTable('directus_relations', (table) => {
table.dropColumn('sort_field');
});
}

View File

@@ -8,6 +8,7 @@ defaults:
one_field: null
one_primary: null
junction_field: null
sort_field: null
data:
- many_collection: directus_users

View File

@@ -11,4 +11,7 @@ export type Relation = {
one_collection_field: string | null;
one_allowed_collections: string | null;
junction_field: string | null;
sort_field: string | null;
};

View File

@@ -12,8 +12,8 @@
:disabled="disabled"
@update:items="sortItems($event)"
@click:row="editItem"
:show-manual-sort="sortField !== null"
:manual-sort-key="sortField"
:show-manual-sort="relationInfo.sortField !== null"
:manual-sort-key="relationInfo.sortField"
>
<template #item.$thumbnail="{ item }">
<render-display
@@ -104,10 +104,6 @@ export default defineComponent({
type: String,
required: true,
},
sortField: {
type: String,
default: null,
},
value: {
type: Array as PropType<(string | number | Record<string, any>)[] | null>,
default: null,
@@ -118,23 +114,19 @@ export default defineComponent({
},
},
setup(props, { emit }) {
const { collection, field, value, primaryKey, sortField } = toRefs(props);
const { collection, field, value } = toRefs(props);
const { junction, junctionCollection, relation, relationCollection, relationInfo } = useRelation(collection, field);
const { junction, junctionCollection, relation, relationInfo } = useRelation(collection, field);
function emitter(newVal: any[] | null) {
emit('input', newVal);
}
const {
deleteItem,
getUpdatedItems,
getNewItems,
getPrimaryKeys,
getNewSelectedItems,
getJunctionItem,
getJunctionFromRelatedId,
} = useActions(value, relationInfo, emitter);
const { deleteItem, getUpdatedItems, getNewItems, getPrimaryKeys, getNewSelectedItems } = useActions(
value,
relationInfo,
emitter
);
const fields = computed(() => {
const { junctionField } = relationInfo.value;
@@ -162,7 +154,6 @@ export default defineComponent({
const { loading, error, items } = usePreview(
value,
fields,
sortField,
relationInfo,
getNewSelectedItems,
getUpdatedItems,
@@ -184,7 +175,7 @@ export default defineComponent({
const { showUpload, onUpload } = useUpload();
const { sort, sortItems, sortedItems } = useSort(sortField, fields, items, emitter);
const { sort, sortItems, sortedItems } = useSort(relationInfo, fields, items, emitter);
return {
junction,
@@ -212,7 +203,7 @@ export default defineComponent({
sort,
sortItems,
sortedItems,
downloadItem
downloadItem,
};
function downloadItem(item: any) {
@@ -255,6 +246,7 @@ export default defineComponent({
}
.download {
--v-icon-color: var(--foreground-subdued);
margin-right: 8px;
}

View File

@@ -1,6 +1,5 @@
import { defineInterface } from '../define';
import InterfaceFiles from './files.vue';
import FilesOptions from './options.vue';
export default defineInterface(({ i18n }) => ({
id: 'files',
@@ -11,6 +10,6 @@ export default defineInterface(({ i18n }) => ({
types: ['alias'],
groups: ['files'],
relational: true,
options: FilesOptions,
options: [],
recommendedDisplays: ['files'],
}));

View File

@@ -1,92 +0,0 @@
<template>
<v-notice type="warning" v-if="junctionCollection === null">
{{ $t('interfaces.one-to-many.no_collection') }}
</v-notice>
<div v-else class="form-grid">
<div class="field half">
<p class="type-label">{{ $t('sort_field') }}</p>
<interface-field
v-model="sortField"
:collection="junctionCollection"
:type-allow-list="['bigInteger', 'integer']"
allowNone
/>
</div>
</div>
</template>
<script lang="ts">
import { Field } from '@/types';
import { defineComponent, PropType, computed } from '@vue/composition-api';
import { useRelationsStore } from '@/stores/';
import { Relation, Collection } from '@/types';
import { useCollectionsStore } from '@/stores';
export default defineComponent({
props: {
collection: {
type: String,
required: true,
},
fieldData: {
type: Object as PropType<Field>,
default: null,
},
relations: {
type: Array as PropType<Relation[]>,
default: () => [],
},
value: {
type: Object as PropType<any>,
default: null,
},
newCollections: {
type: Array as PropType<Collection[]>,
default: () => [],
},
newFields: {
type: Array as PropType<Field[]>,
default: () => [],
},
},
setup(props, { emit }) {
const collectionsStore = useCollectionsStore();
const relationsStore = useRelationsStore();
const sortField = computed({
get() {
return props.value?.sortField;
},
set(newFields: string) {
emit('input', {
...(props.value || {}),
sortField: newFields,
});
},
});
const junctionCollection = computed(() => {
if (!props.fieldData || !props.relations || props.relations.length === 0) return null;
const { field } = props.fieldData;
const junctionRelation = props.relations.find(
(relation) => relation.one_collection === props.collection && relation.one_field === field
);
return junctionRelation?.many_collection || null;
});
const junctionCollectionExists = computed(() => {
return !!collectionsStore.state.collections.find(
(collection) => collection.collection === junctionCollection.value
);
});
return { sortField, junctionCollection, junctionCollectionExists };
},
});
</script>
<style lang="scss" scoped>
@import '@/styles/mixins/form-grid';
.form-grid {
@include form-grid;
}
</style>

View File

@@ -1,6 +1,5 @@
import { defineInterface } from '../define';
import InterfaceManyToAny from './m2a-builder.vue';
import Options from './options.vue';
export default defineInterface(({ i18n }) => ({
id: 'm2a-builder',
@@ -10,5 +9,5 @@ export default defineInterface(({ i18n }) => ({
relational: true,
types: ['alias'],
groups: ['m2a'],
options: Options,
options: [],
}));

View File

@@ -10,7 +10,7 @@
handle=".drag-handle"
@input="onSort"
:set-data="hideDragImage"
:disabled="!sortField"
:disabled="!o2mRelation.sort_field"
>
<div
class="m2a-row"
@@ -18,7 +18,7 @@
:key="item.$index"
@click="editExisting((value || [])[item.$index])"
>
<v-icon class="drag-handle" name="drag_handle" @click.stop v-if="sortField" />
<v-icon class="drag-handle" name="drag_handle" @click.stop v-if="o2mRelation.sort_field" />
<span class="collection">{{ collections[item[anyRelation.one_collection_field]].name }}:</span>
<span
v-if="typeof item[anyRelation.many_field] === 'number' || typeof item[anyRelation.many_field] === 'string'"
@@ -139,10 +139,6 @@ export default defineComponent({
type: [String, Number] as PropType<string | number>,
required: true,
},
sortField: {
type: String,
default: null,
},
},
setup(props, { emit }) {
const relationsStore = useRelationsStore();
@@ -304,10 +300,16 @@ export default defineComponent({
return val;
});
return [
...values.filter((val) => val[props.sortField]).sort((a, b) => a[props.sortField] - b[props.sortField]), // sort by sortField if it exists
...values.filter((val) => !val[props.sortField]).sort((a, b) => a.$index - b.$index), // sort the rest with $index
];
if (o2mRelation.value?.sort_field) {
return [
...values
.filter((val) => val[o2mRelation.value.sort_field!])
.sort((a, b) => a[o2mRelation.value.sort_field!] - b[o2mRelation.value.sort_field!]), // sort by sort field if it exists
...values.filter((val) => !val[o2mRelation.value.sort_field!]).sort((a, b) => a.$index - b.$index), // sort the rest with $index
];
} else {
return [...values.sort((a, b) => a.$index - b.$index)];
}
});
return { fetchValues, previewValues, loading, junctionRowMap, relatedItemValues };
@@ -378,7 +380,7 @@ export default defineComponent({
o2mRelation.value.many_primary,
anyRelation.value.many_field,
anyRelation.value.one_collection_field!,
props.sortField,
o2mRelation.value.sort_field,
],
},
});
@@ -530,8 +532,8 @@ export default defineComponent({
},
};
if (props.sortField) {
editsAtStart.value[props.sortField] = junctionRow[props.sortField];
if (o2mRelation.value.sort_field) {
editsAtStart.value[o2mRelation.value.sort_field] = junctionRow[o2mRelation.value.sort_field];
}
relatedPrimaryKey.value = relatedKey || '+';
@@ -571,6 +573,8 @@ export default defineComponent({
emit(
'input',
props.value.map((rawValue, index) => {
if (!o2mRelation.value.sort_field) return rawValue;
const sortedItemIndex = sortedItems.findIndex((sortedItem) => {
return sortedItem.$index === index;
});
@@ -578,12 +582,12 @@ export default defineComponent({
if (isPlainObject(rawValue)) {
return {
...rawValue,
[props.sortField]: sortedItemIndex + 1,
[o2mRelation.value.sort_field]: sortedItemIndex + 1,
};
} else {
return {
[o2mRelation.value.many_primary]: rawValue,
[props.sortField]: sortedItemIndex + 1,
[o2mRelation.value.sort_field]: sortedItemIndex + 1,
};
}
})

View File

@@ -1,96 +0,0 @@
<template>
<v-notice type="warning" v-if="junctionCollection === null">
{{ $t('interfaces.one-to-many.no_collection') }}
</v-notice>
<div v-else class="form-grid">
<div class="field half-space">
<p class="type-label">{{ $t('sort_field') }}</p>
<interface-field
v-model="sortField"
:collection="junctionCollection"
:type-allow-list="['bigInteger', 'integer']"
allowNone
/>
</div>
</div>
</template>
<script lang="ts">
import { Field } from '@/types';
import { defineComponent, PropType, computed } from '@vue/composition-api';
import { useRelationsStore } from '@/stores/';
import { Relation, Collection } from '@/types';
import { useCollectionsStore } from '@/stores';
export default defineComponent({
props: {
collection: {
type: String,
required: true,
},
fieldData: {
type: Object as PropType<Field>,
default: null,
},
relations: {
type: Array as PropType<Relation[]>,
default: () => [],
},
value: {
type: Object as PropType<any>,
default: null,
},
newCollections: {
type: Array as PropType<Collection[]>,
default: () => [],
},
newFields: {
type: Array as PropType<Field[]>,
default: () => [],
},
},
setup(props, { emit }) {
const collectionsStore = useCollectionsStore();
const relationsStore = useRelationsStore();
const sortField = computed({
get() {
return props.value?.sortField;
},
set(newFields: string) {
emit('input', {
...(props.value || {}),
sortField: newFields,
});
},
});
const junctionCollection = computed(() => {
if (!props.fieldData || !props.relations || props.relations.length === 0) return null;
const { field } = props.fieldData;
const junctionRelation = props.relations.find(
(relation) => relation.one_collection === props.collection && relation.one_field === field
);
return junctionRelation?.many_collection || null;
});
const junctionCollectionExists = computed(() => {
return !!collectionsStore.state.collections.find(
(collection) => collection.collection === junctionCollection.value
);
});
return { sortField, junctionCollection, junctionCollectionExists };
},
});
</script>
<style lang="scss" scoped>
@import '@/styles/mixins/form-grid';
.form-grid {
@include form-grid;
}
</style>

View File

@@ -13,8 +13,8 @@
@update:items="sortItems($event)"
@click:row="editItem"
:disabled="disabled"
:show-manual-sort="sortField !== null"
:manual-sort-key="sortField"
:show-manual-sort="relationInfo.sortField !== null"
:manual-sort-key="relationInfo.sortField"
>
<template v-for="header in tableHeaders" v-slot:[`item.${header.value}`]="{ item }">
<render-display
@@ -98,10 +98,6 @@ export default defineComponent({
type: String,
required: true,
},
sortField: {
type: String,
default: null,
},
fields: {
type: Array as PropType<string[]>,
default: () => [],
@@ -112,7 +108,7 @@ export default defineComponent({
},
},
setup(props, { emit }) {
const { value, collection, field, fields, sortField } = toRefs(props);
const { value, collection, field, fields } = toRefs(props);
function emitter(newVal: any[] | null) {
emit('input', newVal);
@@ -120,20 +116,15 @@ export default defineComponent({
const { junction, junctionCollection, relation, relationCollection, relationInfo } = useRelation(collection, field);
const {
deleteItem,
getUpdatedItems,
getNewItems,
getPrimaryKeys,
getNewSelectedItems,
getJunctionItem,
getJunctionFromRelatedId,
} = useActions(value, relationInfo, emitter);
const { deleteItem, getUpdatedItems, getNewItems, getPrimaryKeys, getNewSelectedItems } = useActions(
value,
relationInfo,
emitter
);
const { tableHeaders, items, loading, error } = usePreview(
value,
fields,
sortField,
relationInfo,
getNewSelectedItems,
getUpdatedItems,
@@ -153,7 +144,7 @@ export default defineComponent({
const { stageSelection, selectModalActive, selectionFilters } = useSelection(value, items, relationInfo, emitter);
const { sort, sortItems, sortedItems } = useSort(sortField, fields, items, emitter);
const { sort, sortItems, sortedItems } = useSort(relationInfo, fields, items, emitter);
return {
junction,

View File

@@ -11,15 +11,6 @@
:inject="junctionCollectionExists ? null : { fields: newFields, collections: newCollections, relations }"
/>
</div>
<div class="field half">
<p class="type-label">{{ $t('sort_field') }}</p>
<interface-field
v-model="sortField"
:collection="junctionCollection"
:type-allow-list="['bigInteger', 'integer']"
allowNone
></interface-field>
</div>
</div>
</template>
@@ -58,19 +49,6 @@ export default defineComponent({
},
setup(props, { emit }) {
const collectionsStore = useCollectionsStore();
const relationsStore = useRelationsStore();
const sortField = computed({
get() {
return props.value?.sortField;
},
set(newFields: string) {
emit('input', {
...(props.value || {}),
sortField: newFields,
});
},
});
const fields = computed({
get() {
@@ -99,7 +77,7 @@ export default defineComponent({
);
});
return { fields, sortField, junctionCollection, junctionCollectionExists };
return { fields, junctionCollection, junctionCollectionExists };
},
});
</script>

View File

@@ -9,7 +9,6 @@ import { cloneDeep, get } from 'lodash';
export default function usePreview(
value: Ref<(string | number | Record<string, any>)[] | null>,
fields: Ref<string[]>,
sortField: Ref<string | null>,
relation: Ref<RelationInfo>,
getNewSelectedItems: () => Record<string, any>[],
getUpdatedItems: () => Record<string, any>[],
@@ -159,8 +158,8 @@ export default function usePreview(
if (filteredFields.includes(junctionPkField) === false) filteredFields.push(junctionPkField);
if (filteredFields.includes(junctionField) === false) filteredFields.push(junctionField);
if (sortField.value !== null && filteredFields.includes(sortField.value) === false)
filteredFields.push(sortField.value);
if (relation.value.sortField !== null && filteredFields.includes(relation.value.sortField) === false)
filteredFields.push(relation.value.sortField);
data = await request(junctionCollection, filteredFields, junctionPkField, primaryKeys);
}

View File

@@ -7,6 +7,7 @@ export type RelationInfo = {
junctionPkField: string;
relationPkField: string;
junctionField: string;
sortField: string;
junctionCollection: string;
relationCollection: string;
};
@@ -50,6 +51,7 @@ export default function useRelation(collection: Ref<string>, field: Ref<string>)
junctionPkField: junctionPrimaryKeyField.value.field,
relationPkField: relationPrimaryKeyField.value.field,
junctionField: junction.value.junction_field as string,
sortField: junction.value.sort_field as string,
junctionCollection: junctionCollection.value.collection,
relationCollection: relationCollection.value.collection,
} as RelationInfo;

View File

@@ -1,17 +1,18 @@
import { Ref, ref, computed } from '@vue/composition-api';
import { Sort } from '@/components/v-table/types';
import { sortBy } from 'lodash';
import { RelationInfo } from './use-relation';
export default function useSort(
sortField: Ref<string | null>,
relation: Ref<RelationInfo>,
fields: Ref<string[]>,
items: Ref<Record<string, any>[]>,
emit: (newVal: any[] | null) => void
) {
const sort = ref<Sort>({ by: sortField.value || fields.value[0], desc: false });
const sort = ref<Sort>({ by: relation.value.sortField || fields.value[0], desc: false });
const sortedItems = computed(() => {
const sField = sortField.value;
const sField = relation.value.sortField;
if (sField === null || sort.value.by !== sField) return null;
const desc = sort.value.desc;
@@ -22,7 +23,7 @@ export default function useSort(
return { sort, sortItems, sortedItems };
function sortItems(newItems: Record<string, any>[]) {
const sField = sortField.value;
const sField = relation.value.sortField;
if (sField === null) return;
const itemsSorted = newItems.map((item, i) => {

View File

@@ -13,8 +13,8 @@
@update:items="sortItems($event)"
@click:row="editItem"
:disabled="disabled"
:show-manual-sort="sortField !== null"
:manual-sort-key="sortField"
:show-manual-sort="relation.sort_field !== null"
:manual-sort-key="relation.sort_field"
>
<template v-for="header in tableHeaders" v-slot:[`item.${header.value}`]="{ item }">
<render-display
@@ -100,10 +100,6 @@ export default defineComponent({
type: Array as PropType<string[]>,
default: () => [],
},
sortField: {
type: String,
default: null,
},
disabled: {
type: Boolean,
default: false,
@@ -141,16 +137,6 @@ export default defineComponent({
get,
};
function getItem(id: string | number) {
const pkField = relatedPrimaryKeyField.value.field;
if (props.value === null) return null;
return (
props.value.find(
(item) => (typeof item === 'object' && pkField in item && item[pkField] === id) || item === id
) || null
);
}
function getNewItems() {
const pkField = relatedPrimaryKeyField.value.field;
if (props.value === null) return [];
@@ -169,12 +155,6 @@ export default defineComponent({
>[];
}
function getExistingItems() {
if (props.value === null) return [];
const pkField = relatedPrimaryKeyField.value.field;
return props.value.filter((item) => typeof item === 'string' || typeof item === 'number');
}
function getPrimaryKeys() {
if (props.value === null) return [];
const pkField = relatedPrimaryKeyField.value.field;
@@ -216,13 +196,13 @@ export default defineComponent({
}
function useSort() {
const sort = ref<Sort>({ by: props.sortField || props.fields[0], desc: false });
const sort = ref<Sort>({ by: relation.value.sort_field || props.fields[0], desc: false });
function sortItems(newItems: Record<string, any>[]) {
if (props.sortField === null) return;
if (relation.value.sort_field === null) return;
const itemsSorted = newItems.map((item, i) => {
item[props.sortField] = i;
item[relation.value.sort_field] = i;
return item;
});
@@ -230,10 +210,10 @@ export default defineComponent({
}
const sortedItems = computed(() => {
if (props.sortField === null || sort.value.by !== props.sortField) return null;
if (relation.value.sort_field === null || sort.value.by !== relation.value.sort_field) return null;
const desc = sort.value.desc;
const sorted = sortBy(items.value, [props.sortField]);
const sorted = sortBy(items.value, [relation.value.sort_field]);
return desc ? sorted.reverse() : sorted;
});
@@ -267,7 +247,7 @@ export default defineComponent({
watch(
() => props.value,
async (newVal) => {
async () => {
loading.value = true;
const pkField = relatedPrimaryKeyField.value.field;
@@ -277,7 +257,8 @@ export default defineComponent({
fields.push(pkField);
}
if (props.sortField !== null && fields.includes(props.sortField) === false) fields.push(props.sortField);
if (relation.value.sort_field !== null && fields.includes(relation.value.sort_field) === false)
fields.push(relation.value.sort_field);
try {
const endpoint = relatedCollection.value.collection.startsWith('directus_')
@@ -385,8 +366,6 @@ export default defineComponent({
function stageEdits(edits: any) {
const pkField = relatedPrimaryKeyField.value.field;
const hasPrimaryKey = pkField in edits;
const newValue = (props.value || []).map((item) => {
if (typeof item === 'object' && pkField in item && pkField in edits && item[pkField] === edits[pkField]) {
return edits;
@@ -447,8 +426,6 @@ export default defineComponent({
return { stageSelection, selectModalActive, selectionFilters };
function stageSelection(newSelection: (number | string)[]) {
const pkField = relatedPrimaryKeyField.value.field;
const selection = newSelection.filter((item) => selectedPrimaryKeys.value.includes(item) === false);
const newVal = [...selection, ...(props.value || [])];

View File

@@ -11,22 +11,13 @@
:inject="relatedCollectionExists ? null : { fields: newFields, collections: newCollections, relations }"
/>
</div>
<div class="field half">
<p class="type-label">{{ $t('sort_field') }}</p>
<interface-field
v-model="sortField"
:collection="relatedCollection"
:type-allow-list="['bigInteger', 'integer']"
allowNone
></interface-field>
</div>
</div>
</template>
<script lang="ts">
import { Field, Relation, Collection } from '@/types';
import { defineComponent, PropType, computed } from '@vue/composition-api';
import { useRelationsStore, useCollectionsStore } from '@/stores/';
import { useCollectionsStore } from '@/stores/';
export default defineComponent({
props: {
@@ -57,7 +48,6 @@ export default defineComponent({
},
setup(props, { emit }) {
const collectionsStore = useCollectionsStore();
const relationsStore = useRelationsStore();
const fields = computed({
get() {
@@ -71,18 +61,6 @@ export default defineComponent({
},
});
const sortField = computed({
get() {
return props.value?.sortField;
},
set(newFields: string) {
emit('input', {
...(props.value || {}),
sortField: newFields,
});
},
});
const relatedCollection = computed(() => {
if (!props.fieldData || !props.relations || props.relations.length === 0) return null;
const { field } = props.fieldData;
@@ -98,7 +76,7 @@ export default defineComponent({
);
});
return { fields, sortField, relatedCollection, relatedCollectionExists };
return { fields, relatedCollection, relatedCollectionExists };
},
});
</script>

View File

@@ -90,6 +90,7 @@ field_presentation: Presentation & Aliases
field_file: Single File
field_files: Multiple Files
field_m2o: M2O Relationship
field_m2a: M2A Relationship
field_o2m: O2M Relationship
field_m2m: M2M Relationship
field_translations: Translations
@@ -367,6 +368,7 @@ collection_created: Collection Created
modified_on: Modified On
card_size: Card Size
sort_field: Sort Field
add_sort_field: Add Sort Field
sort: Sort
status: Status
toggle_manual_sorting: Toggle Manual Sorting

View File

@@ -192,13 +192,47 @@
<v-icon class="arrow" name="arrow_backward" />
</div>
<div class="sort-field">
<v-divider large :inline-title="false">{{ $t('sort_field') }}</v-divider>
<v-input
:class="{ matches: junctionFieldExists(relations[0].sort_field) }"
v-model="relations[0].sort_field"
:nullable="false"
:placeholder="$t('add_sort_field') + '...'"
db-safe
>
<template #append v-if="junctionCollectionExists">
<v-menu show-arrow placement="bottom-end">
<template #activator="{ toggle }">
<v-icon name="list_alt" @click="toggle" v-tooltip="$t('select_existing')" />
</template>
<v-list class="monospace">
<v-list-item
v-for="item in junctionFields"
:key="item.value"
:active="relations[0].sort_field === item.value"
:disabled="item.disabled"
@click="relations[0].sort_field = item.value"
>
<v-list-item-content>
{{ item.text }}
</v-list-item-content>
</v-list-item>
</v-list>
</v-menu>
</template>
</v-input>
</div>
<v-notice class="generated-data" v-if="generationInfo.length > 0" type="warning">
<span>
{{ $t('new_data_alert') }}
<ul>
<li v-for="(data, index) in generationInfo" :key="index">
<span class="field-name">{{ data.name }}</span>
({{ $t(data.isField ? 'new_field' : 'new_collection') }})
({{ $t(data.type === 'field' ? 'new_field' : 'new_collection') }})
</li>
</ul>
</span>
@@ -207,13 +241,12 @@
</template>
<script lang="ts">
import { defineComponent, computed, ref } from '@vue/composition-api';
import { defineComponent, computed } from '@vue/composition-api';
import { orderBy } from 'lodash';
import { useCollectionsStore, useFieldsStore } from '@/stores/';
import { Field } from '@/types';
import i18n from '@/lang';
import { state } from '../store';
import { state, generationInfo } from '../store';
export default defineComponent({
props: {
@@ -230,7 +263,7 @@ export default defineComponent({
default: false,
},
},
setup(props) {
setup() {
const collectionsStore = useCollectionsStore();
const fieldsStore = useFieldsStore();
@@ -290,38 +323,6 @@ export default defineComponent({
}));
});
const generationInfo = computed(() => {
const message: { name: string; isField: boolean }[] = [];
if (state.relations[0].many_collection) {
if (junctionCollectionExists.value === false)
message.push({ name: state.relations[0].many_collection, isField: false });
if (junctionFieldExists(state.relations[0].many_field) === false && state.relations[0].many_field !== '')
message.push({
name: state.relations[0].many_collection + '.' + state.relations[0].many_field,
isField: true,
});
if (
junctionFieldExists(state.relations[1].one_collection_field) === false &&
state.relations[1].one_collection_field !== ''
)
message.push({
name: state.relations[0].many_collection + '.' + state.relations[1].one_collection_field,
isField: true,
});
if (junctionFieldExists(state.relations[1].many_field) === false && state.relations[1].many_field !== '')
message.push({
name: state.relations[0].many_collection + '.' + state.relations[1].many_field,
isField: true,
});
}
return message;
});
return {
relations: state.relations,
autoFill,
@@ -353,10 +354,6 @@ export default defineComponent({
gap: 12px 28px;
margin-top: 48px;
.v-input.matches {
--v-input-color: var(--primary);
}
.v-icon.arrow {
--v-icon-color: var(--primary);
@@ -381,6 +378,10 @@ export default defineComponent({
}
}
.v-input.matches {
--v-input-color: var(--primary);
}
.type-label {
margin-bottom: 8px;
}
@@ -407,8 +408,8 @@ export default defineComponent({
}
.related-collections-preview {
grid-row: 2 / span 2;
grid-column: 3;
grid-row: 2 / span 2;
padding: var(--input-padding);
overflow: auto;
color: var(--foreground-subdued);
@@ -421,4 +422,13 @@ export default defineComponent({
.one-collection-field {
align-self: flex-end;
}
.sort-field {
--v-input-font-family: var(--family-monospace);
.v-divider {
margin-top: 48px;
margin-bottom: 24px;
}
}
</style>

View File

@@ -219,6 +219,40 @@
<v-icon name="arrow_forward" class="arrow" />
</div>
<div class="sort-field">
<v-divider large :inline-title="false">{{ $t('sort_field') }}</v-divider>
<v-input
:class="{ matches: junctionFieldExists(relations[0].sort_field) }"
v-model="relations[0].sort_field"
:nullable="false"
:placeholder="$t('add_sort_field') + '...'"
db-safe
>
<template #append v-if="junctionCollectionExists">
<v-menu show-arrow placement="bottom-end">
<template #activator="{ toggle }">
<v-icon name="list_alt" @click="toggle" v-tooltip="$t('select_existing')" />
</template>
<v-list class="monospace">
<v-list-item
v-for="item in junctionFields"
:key="item.value"
:active="relations[0].sort_field === item.value"
:disabled="item.disabled"
@click="relations[0].sort_field = item.value"
>
<v-list-item-content>
{{ item.text }}
</v-list-item-content>
</v-list-item>
</v-list>
</v-menu>
</template>
</v-input>
</div>
<v-notice class="generated-data" v-if="generationInfo.length > 0" type="warning">
<span>
{{ $t('new_data_alert') }}
@@ -226,7 +260,7 @@
<ul>
<li v-for="(data, index) in generationInfo" :key="index">
<span class="field-name">{{ data.name }}</span>
({{ $t(data.isField ? 'new_field' : 'new_collection') }})
({{ $t(data.type === 'field' ? 'new_field' : 'new_collection') }})
</li>
</ul>
</span>
@@ -235,13 +269,13 @@
</template>
<script lang="ts">
import { defineComponent, computed, ref } from '@vue/composition-api';
import { defineComponent, computed } from '@vue/composition-api';
import { orderBy } from 'lodash';
import { useCollectionsStore, useFieldsStore } from '@/stores/';
import { Field } from '@/types';
import i18n from '@/lang';
import { state } from '../store';
import { state, generationInfo } from '../store';
export default defineComponent({
props: {
@@ -324,44 +358,6 @@ export default defineComponent({
const { hasCorresponding, correspondingField, correspondingLabel } = useCorresponding();
const generationInfo = computed(() => {
const message: { name: string; isField: boolean }[] = [];
if (state.relations[1].one_collection !== '') {
if (relatedCollectionExists.value === false) {
message.push({ name: state.relations[1].one_collection, isField: false });
if (state.relations[1].one_primary !== '')
message.push({
name: state.relations[1].one_collection + '.' + state.relations[1].one_primary,
isField: true,
});
}
if (hasCorresponding.value === true && correspondingField.value !== null)
message.push({ name: state.relations[1].one_collection + '.' + correspondingField.value, isField: true });
}
if (state.relations[0].many_collection) {
if (junctionCollectionExists.value === false)
message.push({ name: state.relations[0].many_collection, isField: false });
if (junctionFieldExists(state.relations[0].many_field) === false && state.relations[0].many_field !== '')
message.push({
name: state.relations[0].many_collection + '.' + state.relations[0].many_field,
isField: true,
});
if (junctionFieldExists(state.relations[1].many_field) === false && state.relations[1].many_field !== '')
message.push({
name: state.relations[0].many_collection + '.' + state.relations[1].many_field,
isField: true,
});
}
return message;
});
return {
relations: state.relations,
autoFill,
@@ -379,7 +375,7 @@ export default defineComponent({
};
function junctionFieldExists(fieldKey: string) {
if (!junctionCollection.value) return false;
if (!junctionCollection.value || !fieldKey) return false;
return !!fieldsStore.getField(junctionCollection.value, fieldKey);
}
@@ -457,10 +453,6 @@ export default defineComponent({
gap: 12px 28px;
margin-top: 48px;
.v-input.matches {
--v-input-color: var(--primary);
}
.v-icon.arrow {
--v-icon-color: var(--primary);
@@ -480,12 +472,16 @@ export default defineComponent({
}
}
.v-input.matches {
--v-input-color: var(--primary);
}
.type-label {
margin-bottom: 8px;
}
.v-divider {
margin: 48px 0;
margin: 48px 0 24px;
}
.v-list {
@@ -497,7 +493,6 @@ export default defineComponent({
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px 32px;
margin-top: 48px;
.arrow {
--v-icon-color: var(--primary);
@@ -525,4 +520,13 @@ export default defineComponent({
font-family: var(--family-monospace);
}
}
.sort-field {
--v-input-font-family: var(--family-monospace);
.v-divider {
margin-top: 48px;
margin-bottom: 24px;
}
}
</style>

View File

@@ -93,7 +93,7 @@
<ul>
<li v-for="(data, index) in generationInfo" :key="index">
<span class="field-name">{{ data.name }}</span>
({{ $t(data.isField ? 'new_field' : 'new_collection') }})
({{ $t(data.type === 'field' ? 'new_field' : 'new_collection') }})
</li>
</ul>
</span>
@@ -102,15 +102,12 @@
</template>
<script lang="ts">
import { defineComponent, computed, watch } from '@vue/composition-api';
import { Relation } from '@/types';
import { Field } from '@/types';
import { defineComponent, computed } from '@vue/composition-api';
import { orderBy } from 'lodash';
import useSync from '@/composables/use-sync';
import { useCollectionsStore, useFieldsStore } from '@/stores';
import { useCollectionsStore } from '@/stores';
import i18n from '@/lang';
import { state } from '../store';
import { state, generationInfo } from '../store';
export default defineComponent({
props: {
@@ -127,9 +124,8 @@ export default defineComponent({
default: false,
},
},
setup(props, { emit }) {
setup() {
const collectionsStore = useCollectionsStore();
const fieldsStore = useFieldsStore();
const { availableCollections, systemCollections } = useRelation();
const { hasCorresponding, correspondingField, correspondingLabel } = useCorresponding();
@@ -138,30 +134,6 @@ export default defineComponent({
return !!collectionsStore.getCollection(state.relations[0].one_collection);
});
const generationInfo = computed(() => {
const message: { name: string; isField: boolean }[] = [];
if (relatedCollectionExists.value === false && state.relations[0].one_collection !== '') {
message.push({ name: state.relations[0].one_collection, isField: false });
if (state.relations[0].one_primary !== '')
message.push({
name: state.relations[0].one_collection + '.' + state.relations[0].one_primary,
isField: true,
});
}
if (
hasCorresponding.value === true &&
state.relations[0].one_collection !== '' &&
correspondingField.value !== null
) {
message.push({ name: state.relations[0].one_collection + '.' + correspondingField.value, isField: true });
}
return message;
});
return {
relations: state.relations,
availableCollections,

View File

@@ -103,6 +103,40 @@
<v-icon name="arrow_forward" class="arrow" />
</div>
<div class="sort-field">
<v-divider large :inline-title="false">{{ $t('sort_field') }}</v-divider>
<v-input
:class="{ matches: sortFieldExists }"
v-model="relations[0].sort_field"
:nullable="false"
:placeholder="$t('add_sort_field') + '...'"
db-safe
>
<template #append v-if="fields && fields.length > 0">
<v-menu show-arrow placement="bottom-end">
<template #activator="{ toggle }">
<v-icon name="list_alt" @click="toggle" v-tooltip="$t('select_existing')" />
</template>
<v-list class="monospace">
<v-list-item
v-for="item in fields"
:key="item.value"
:active="relations[0].sort_field === item.value"
:disabled="item.disabled"
@click="relations[0].sort_field = item.value"
>
<v-list-item-content>
{{ item.text }}
</v-list-item-content>
</v-list-item>
</v-list>
</v-menu>
</template>
</v-input>
</div>
<v-notice class="generated-data" v-if="generationInfo.length > 0" type="warning">
<span>
{{ $t('new_data_alert') }}
@@ -110,7 +144,7 @@
<ul>
<li v-for="(data, index) in generationInfo" :key="index">
<span class="field-name">{{ data.name }}</span>
({{ $t(data.isField ? 'new_field' : 'new_collection') }})
({{ $t(data.type === 'field' ? 'new_field' : 'new_collection') }})
</li>
</ul>
</span>
@@ -119,13 +153,12 @@
</template>
<script lang="ts">
import { defineComponent, PropType, computed } from '@vue/composition-api';
import { Relation, Field } from '@/types';
import useSync from '@/composables/use-sync';
import { defineComponent, computed } from '@vue/composition-api';
import { Field } from '@/types';
import { useFieldsStore, useCollectionsStore } from '@/stores';
import { orderBy } from 'lodash';
import i18n from '@/lang';
import { state } from '../store';
import { state, generationInfo } from '../store';
export default defineComponent({
props: {
@@ -142,7 +175,7 @@ export default defineComponent({
default: false,
},
},
setup(props, { emit }) {
setup(props) {
const collectionsStore = useCollectionsStore();
const fieldsStore = useFieldsStore();
@@ -167,22 +200,9 @@ export default defineComponent({
return !!fieldsStore.getField(state.relations[0].many_collection, state.relations[0].many_field);
});
const generationInfo = computed(() => {
const message: { name: string; isField: boolean }[] = [];
if (state.relations[0].many_collection !== '') {
if (relatedCollectionExists.value === false)
message.push({ name: state.relations[0].many_collection, isField: false });
if (relatedFieldExists.value === false && state.relations[0].many_field !== '') {
message.push({
name: state.relations[0].many_collection + '.' + state.relations[0].many_field,
isField: true,
});
}
}
return message;
const sortFieldExists = computed(() => {
if (!state?.relations?.[0].many_collection || !state?.relations?.[0].sort_field) return false;
return !!fieldsStore.getField(state.relations[0].many_collection, state.relations[0].sort_field);
});
return {
@@ -197,6 +217,7 @@ export default defineComponent({
relatedCollectionExists,
relatedFieldExists,
generationInfo,
sortFieldExists,
};
function useRelation() {
@@ -355,10 +376,6 @@ export default defineComponent({
gap: 12px 32px;
margin-top: 48px;
.v-input.matches {
--v-input-color: var(--primary);
}
.v-icon.arrow {
--v-icon-color: var(--primary);
@@ -369,6 +386,10 @@ export default defineComponent({
}
}
.v-input.matches {
--v-input-color: var(--primary);
}
.v-list {
--v-list-item-content-font-family: var(--family-monospace);
}
@@ -414,4 +435,13 @@ export default defineComponent({
font-family: var(--family-monospace);
}
}
.sort-field {
--v-input-font-family: var(--family-monospace);
.v-divider {
margin-top: 48px;
margin-bottom: 24px;
}
}
</style>

View File

@@ -19,11 +19,17 @@ const fieldsStore = useFieldsStore();
const relationsStore = useRelationsStore();
const collectionsStore = useCollectionsStore();
type GenerationInfo = {
name: string;
type: 'collection' | 'field';
};
let state: any;
let availableInterfaces: ComputedRef<InterfaceConfig[]>;
let availableDisplays: ComputedRef<DisplayConfig[]>;
let generationInfo: ComputedRef<GenerationInfo[]>;
export { state, availableInterfaces, availableDisplays, initLocalStore, clearLocalStore };
export { state, availableInterfaces, availableDisplays, generationInfo, initLocalStore, clearLocalStore };
function initLocalStore(collection: string, field: string, type: typeof localTypes[number]) {
const interfaces = getInterfaces();
@@ -85,6 +91,28 @@ function initLocalStore(collection: string, field: string, type: typeof localTyp
.sort((a, b) => (a.name > b.name ? 1 : -1));
});
generationInfo = computed(() => {
return [
...state.newCollections.map((newCollection: any) => ({
name: newCollection.collection,
type: 'collection',
})),
...state.newCollections
.map((newCollection: any) =>
newCollection.fields.map((field: any) => ({ ...field, collection: newCollection.collection }))
)
.flat()
.map((newField: any) => ({
name: `${newField.collection}.${newField.field}`,
type: 'field',
})),
...state.newFields.map((newField: any) => ({
name: `${newField.collection}.${newField.field}`,
type: 'field',
})),
];
});
const isExisting = field !== '+';
if (isExisting) {
@@ -231,59 +259,58 @@ function initLocalStore(collection: string, field: string, type: typeof localTyp
delete state.fieldData.schema;
state.fieldData.type = null;
const syncNewCollectionsO2M = throttle(() => {
const collectionName = state.relations[0].many_collection;
const fieldName = state.relations[0].many_field;
const syncNewCollectionsO2M = throttle(([collectionName, fieldName, sortField]) => {
state.newCollections = state.newCollections.filter((col: any) => ['related'].includes(col.$type) === false);
if (collectionExists(collectionName)) {
state.newCollections = [];
} else {
state.newCollections = [
{
collection: collectionName,
fields: [
{
field: 'id',
type: 'integer',
schema: {
has_auto_increment: true,
is_primary_key: true,
},
meta: {
hidden: true,
},
state.newFields = state.newFields.filter(
(field: Partial<Field> & { $type: string }) => ['manyRelated', 'sort'].includes(field.$type) === false
);
if (collectionExists(collectionName) === false) {
state.newCollections.push({
$type: 'related',
collection: collectionName,
fields: [
{
field: 'id',
type: 'integer',
schema: {
has_auto_increment: true,
is_primary_key: true,
},
],
},
];
meta: {
hidden: true,
},
},
],
});
state.relations[0].many_primary = 'id';
}
if (collectionExists(collectionName)) {
if (fieldExists(collectionName, fieldName)) {
state.newFields = [];
} else {
state.newFields = [
{
$type: 'manyRelated',
collection: collectionName,
field: fieldName,
type: fieldsStore.getPrimaryKeyFieldForCollection(collection)?.type,
schema: {},
},
];
}
} else {
state.newFields = [
{
$type: 'manyRelated',
collection: collectionName,
field: fieldName,
type: 'integer',
schema: {},
if (fieldExists(collectionName, fieldName) === false) {
state.newFields.push({
$type: 'manyRelated',
collection: collectionName,
field: fieldName,
type: collectionExists(collectionName)
? fieldsStore.getPrimaryKeyFieldForCollection(collectionName)?.type
: 'integer',
schema: {},
});
}
if (sortField && fieldExists(collectionName, sortField) === false) {
state.newFields.push({
$type: 'sort',
collection: collectionName,
field: sortField,
type: 'integer',
schema: {},
meta: {
hidden: true,
},
];
});
}
}, 50);
@@ -321,134 +348,41 @@ function initLocalStore(collection: string, field: string, type: typeof localTyp
}
);
watch([() => state.relations[0].many_collection, () => state.relations[0].many_field], syncNewCollectionsO2M);
watch(
[
() => state.relations[0].many_collection,
() => state.relations[0].many_field,
() => state.relations[0].sort_field,
],
syncNewCollectionsO2M
);
}
function useM2M() {
delete state.fieldData.schema;
state.fieldData.type = null;
const syncNewCollectionsM2M = throttle(([junctionCollection, manyCurrent, manyRelated, relatedCollection]) => {
state.newCollections = state.newCollections.filter(
(col: any) => ['junction', 'related'].includes(col.$type) === false
);
state.newFields = state.newFields.filter(
(field: Partial<Field> & { $type: string }) => ['manyCurrent', 'manyRelated'].includes(field.$type) === false
);
const syncNewCollectionsM2M = throttle(
([junctionCollection, manyCurrent, manyRelated, relatedCollection, sortField]) => {
state.newCollections = state.newCollections.filter(
(col: any) => ['junction', 'related'].includes(col.$type) === false
);
state.newFields = state.newFields.filter(
(field: Partial<Field> & { $type: string }) =>
['manyCurrent', 'manyRelated', 'sort'].includes(field.$type) === false
);
if (collectionExists(junctionCollection) === false) {
state.newCollections.push({
$type: 'junction',
collection: junctionCollection,
meta: {
hidden: true,
icon: 'import_export',
},
fields: [
{
field: 'id',
type: 'integer',
schema: {
has_auto_increment: true,
},
meta: {
hidden: true,
},
},
],
});
state.relations[0].many_primary = 'id';
state.relations[1].many_primary = 'id';
}
if (fieldExists(junctionCollection, manyCurrent) === false) {
state.newFields.push({
$type: 'manyCurrent',
collection: junctionCollection,
field: manyCurrent,
type: fieldsStore.getPrimaryKeyFieldForCollection(collection)!.type,
schema: {},
meta: {
hidden: true,
},
});
}
if (fieldExists(junctionCollection, manyRelated) === false) {
if (type === 'translations') {
state.newFields.push({
$type: 'manyRelated',
collection: junctionCollection,
field: manyRelated,
type: collectionExists(relatedCollection)
? fieldsStore.getPrimaryKeyFieldForCollection(relatedCollection)?.type
: 'string',
schema: {},
meta: {
hidden: true,
},
});
} else {
state.newFields.push({
$type: 'manyRelated',
collection: junctionCollection,
field: manyRelated,
type: collectionExists(relatedCollection)
? fieldsStore.getPrimaryKeyFieldForCollection(relatedCollection)?.type
: 'integer',
schema: {},
meta: {
hidden: true,
},
});
}
}
if (collectionExists(relatedCollection) === false) {
if (type === 'translations') {
if (collectionExists(junctionCollection) === false) {
state.newCollections.push({
$type: 'related',
collection: relatedCollection,
$type: 'junction',
collection: junctionCollection,
meta: {
icon: 'translate',
hidden: true,
icon: 'import_export',
},
fields: [
{
field: state.relations[1].one_primary,
type: 'string',
schema: {
is_primary_key: true,
},
meta: {
interface: 'text-input',
options: {
iconLeft: 'vpn_key',
},
width: 'half',
},
},
{
field: 'name',
type: 'string',
schema: {},
meta: {
interface: 'text-input',
options: {
iconLeft: 'translate',
},
width: 'half',
},
},
],
});
} else {
state.newCollections.push({
$type: 'related',
collection: relatedCollection,
fields: [
{
field: state.relations[1].one_primary,
field: 'id',
type: 'integer',
schema: {
has_auto_increment: true,
@@ -459,48 +393,165 @@ function initLocalStore(collection: string, field: string, type: typeof localTyp
},
],
});
}
}
if (type === 'translations') {
if (collectionExists(relatedCollection) === false) {
state.newRows = {
[relatedCollection]: [
{
code: 'en-US',
name: 'English',
},
{
code: 'de-DE',
name: 'German',
},
{
code: 'fr-FR',
name: 'French',
},
{
code: 'ru-RU',
name: 'Russian',
},
{
code: 'es-ES',
name: 'Spanish',
},
{
code: 'it-IT',
name: 'Italian',
},
{
code: 'pt-BR',
name: 'Portuguese',
},
],
};
} else {
state.newRows = {};
state.relations[0].many_primary = 'id';
state.relations[1].many_primary = 'id';
}
}
}, 50);
if (fieldExists(junctionCollection, manyCurrent) === false) {
state.newFields.push({
$type: 'manyCurrent',
collection: junctionCollection,
field: manyCurrent,
type: fieldsStore.getPrimaryKeyFieldForCollection(collection)!.type,
schema: {},
meta: {
hidden: true,
},
});
}
if (fieldExists(junctionCollection, manyRelated) === false) {
if (type === 'translations') {
state.newFields.push({
$type: 'manyRelated',
collection: junctionCollection,
field: manyRelated,
type: collectionExists(relatedCollection)
? fieldsStore.getPrimaryKeyFieldForCollection(relatedCollection)?.type
: 'string',
schema: {},
meta: {
hidden: true,
},
});
} else {
state.newFields.push({
$type: 'manyRelated',
collection: junctionCollection,
field: manyRelated,
type: collectionExists(relatedCollection)
? fieldsStore.getPrimaryKeyFieldForCollection(relatedCollection)?.type
: 'integer',
schema: {},
meta: {
hidden: true,
},
});
}
}
if (collectionExists(relatedCollection) === false) {
if (type === 'translations') {
state.newCollections.push({
$type: 'related',
collection: relatedCollection,
meta: {
icon: 'translate',
},
fields: [
{
field: state.relations[1].one_primary,
type: 'string',
schema: {
is_primary_key: true,
},
meta: {
interface: 'text-input',
options: {
iconLeft: 'vpn_key',
},
width: 'half',
},
},
{
field: 'name',
type: 'string',
schema: {},
meta: {
interface: 'text-input',
options: {
iconLeft: 'translate',
},
width: 'half',
},
},
],
});
} else {
state.newCollections.push({
$type: 'related',
collection: relatedCollection,
fields: [
{
field: state.relations[1].one_primary,
type: 'integer',
schema: {
has_auto_increment: true,
},
meta: {
hidden: true,
},
},
],
});
}
}
if (type === 'translations') {
if (collectionExists(relatedCollection) === false) {
state.newRows = {
[relatedCollection]: [
{
code: 'en-US',
name: 'English',
},
{
code: 'de-DE',
name: 'German',
},
{
code: 'fr-FR',
name: 'French',
},
{
code: 'ru-RU',
name: 'Russian',
},
{
code: 'es-ES',
name: 'Spanish',
},
{
code: 'it-IT',
name: 'Italian',
},
{
code: 'pt-BR',
name: 'Portuguese',
},
],
};
} else {
state.newRows = {};
}
}
if (sortField && fieldExists(junctionCollection, sortField) === false) {
state.newFields.push({
$type: 'sort',
collection: junctionCollection,
field: sortField,
type: 'integer',
schema: {},
meta: {
hidden: true,
},
});
}
},
50
);
if (!isExisting) {
state.fieldData.meta.special = [type];
@@ -567,6 +618,7 @@ function initLocalStore(collection: string, field: string, type: typeof localTyp
() => state.relations[0].many_field,
() => state.relations[1].many_field,
() => state.relations[1].one_collection,
() => state.relations[0].sort_field,
],
syncNewCollectionsM2M
);
@@ -691,83 +743,99 @@ function initLocalStore(collection: string, field: string, type: typeof localTyp
delete state.fieldData.schema;
state.fieldData.type = null;
const syncNewCollectionsM2A = throttle(([junctionCollection, manyCurrent, manyRelated, oneCollectionField]) => {
state.newCollections = state.newCollections.filter(
(col: any) => ['junction', 'related'].includes(col.$type) === false
);
const syncNewCollectionsM2A = throttle(
([junctionCollection, manyCurrent, manyRelated, oneCollectionField, sortField]) => {
state.newCollections = state.newCollections.filter(
(col: any) => ['junction', 'related'].includes(col.$type) === false
);
state.newFields = state.newFields.filter(
(field: Partial<Field> & { $type: string }) =>
['manyCurrent', 'manyRelated', 'collectionField'].includes(field.$type) === false
);
state.newFields = state.newFields.filter(
(field: Partial<Field> & { $type: string }) =>
['manyCurrent', 'manyRelated', 'collectionField', 'sort'].includes(field.$type) === false
);
if (collectionExists(junctionCollection) === false) {
state.newCollections.push({
$type: 'junction',
collection: junctionCollection,
meta: {
hidden: true,
icon: 'import_export',
},
fields: [
{
field: 'id',
type: 'integer',
schema: {
has_auto_increment: true,
},
meta: {
hidden: true,
},
if (collectionExists(junctionCollection) === false) {
state.newCollections.push({
$type: 'junction',
collection: junctionCollection,
meta: {
hidden: true,
icon: 'import_export',
},
],
});
fields: [
{
field: 'id',
type: 'integer',
schema: {
has_auto_increment: true,
},
meta: {
hidden: true,
},
},
],
});
state.relations[0].many_primary = 'id';
state.relations[1].many_primary = 'id';
}
state.relations[0].many_primary = 'id';
state.relations[1].many_primary = 'id';
}
if (fieldExists(junctionCollection, manyCurrent) === false) {
state.newFields.push({
$type: 'manyCurrent',
collection: junctionCollection,
field: manyCurrent,
type: fieldsStore.getPrimaryKeyFieldForCollection(collection)!.type,
schema: {},
meta: {
hidden: true,
},
});
}
if (fieldExists(junctionCollection, manyCurrent) === false) {
state.newFields.push({
$type: 'manyCurrent',
collection: junctionCollection,
field: manyCurrent,
type: fieldsStore.getPrimaryKeyFieldForCollection(collection)!.type,
schema: {},
meta: {
hidden: true,
},
});
}
if (fieldExists(junctionCollection, manyRelated) === false) {
state.newFields.push({
$type: 'manyRelated',
collection: junctionCollection,
field: manyRelated,
// We'll have to save the foreign key as a string, as that's the only way to safely
// be able to store the PK of multiple typed collections
type: 'string',
schema: {},
meta: {
hidden: true,
},
});
}
if (fieldExists(junctionCollection, manyRelated) === false) {
state.newFields.push({
$type: 'manyRelated',
collection: junctionCollection,
field: manyRelated,
// We'll have to save the foreign key as a string, as that's the only way to safely
// be able to store the PK of multiple typed collections
type: 'string',
schema: {},
meta: {
hidden: true,
},
});
}
if (fieldExists(junctionCollection, oneCollectionField) === false) {
state.newFields.push({
$type: 'collectionField',
collection: junctionCollection,
field: oneCollectionField,
type: 'string', // directus_collections.collection is a string
schema: {},
meta: {
hidden: true,
},
});
}
}, 50);
if (fieldExists(junctionCollection, oneCollectionField) === false) {
state.newFields.push({
$type: 'collectionField',
collection: junctionCollection,
field: oneCollectionField,
type: 'string', // directus_collections.collection is a string
schema: {},
meta: {
hidden: true,
},
});
}
if (sortField && fieldExists(junctionCollection, sortField) === false) {
state.newFields.push({
$type: 'sort',
collection: junctionCollection,
field: sortField,
type: 'integer',
schema: {},
meta: {
hidden: true,
},
});
}
},
50
);
if (!isExisting) {
state.fieldData.meta.special = [type];
@@ -825,6 +893,7 @@ function initLocalStore(collection: string, field: string, type: typeof localTyp
() => state.relations[0].many_field,
() => state.relations[1].many_field,
() => state.relations[1].one_collection_field,
() => state.relations[0].sort_field,
],
syncNewCollectionsM2A
);

View File

@@ -7,6 +7,7 @@ export type Relation = {
one_field: null | string;
one_primary: string;
junction_field: null | string;
sort_field: null | string;
one_collection_field: null | string;
one_allowed_collections: null | string[];
};