Add m2m config

This commit is contained in:
rijkvanzanten
2020-07-23 18:19:07 -04:00
parent f004f72244
commit f05277eb3a
10 changed files with 220 additions and 12 deletions

View File

@@ -36,6 +36,7 @@ export default function useFormFields(fields: Ref<Field[]>) {
special: null,
translation: null,
width: 'full',
note: null,
};
}

View File

@@ -51,6 +51,7 @@
"not_available_for_type": "Not Available for this Type",
"configure_m2o": "Configure your Many-to-One Relationship...",
"configure_m2m": "Configure your Many-to-Many Relationship...",
"include_seconds": "Include Seconds",

View File

@@ -43,7 +43,7 @@ export default defineComponent({
const _field = useSync(props, 'fieldData', emit);
const availabledisplays = computed(() =>
displays.filter((display) => {
const matchesType = display.types.includes(props.fieldData.database.type);
const matchesType = display.types.includes(props.fieldData.database?.type || 'alias');
const matchesRelation = true;
// if (props.type === 'standard') {

View File

@@ -43,7 +43,7 @@ export default defineComponent({
const _field = useSync(props, 'fieldData', emit);
const availableInterfaces = computed(() =>
interfaces.filter((inter) => {
const matchesType = inter.types.includes(props.fieldData.database.type);
const matchesType = inter.types.includes(props.fieldData.database?.type || 'alias');
let matchesRelation = false;
if (props.type === 'standard') {

View File

@@ -1,9 +1,143 @@
<template>
<div>M2M</div>
<div>
<h2 class="type-title">{{ $t('configure_m2m') }}</h2>
<div class="grid">
<div class="field">
<div class="type-label">{{ $t('this_collection') }}</div>
<v-input disabled :value="collection" />
</div>
<div class="field">
<div class="type-label">{{ $t('junction_collection') }}</div>
<v-select :items="collectionItems" v-model="junctionCollection" />
</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"
/>
</div>
<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-icon name="arrow_forward" />
<v-icon name="arrow_backward" />
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from '@vue/composition-api';
import { defineComponent, computed, PropType } from '@vue/composition-api';
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';
export default defineComponent({});
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();
const availableCollections = computed(() => {
return orderBy(
collectionsStore.state.collections.filter((collection) => {
return (
collection.collection.startsWith('directus_') === false &&
collection.collection !== props.collection
);
}),
['collection'],
['asc']
);
});
const collectionItems = computed(() =>
availableCollections.value.map((collection) => ({
text: collection.collection,
value: collection.collection,
}))
);
const junctionCollection = computed({
get() {
return _relations.value[0].collection_many;
},
set(collection: string) {
_relations.value[0].collection_many = collection;
_relations.value[1].collection_many = collection;
},
});
const junctionFields = computed(() => {
if (!junctionCollection.value) return [];
return fieldsStore.getFieldsForCollection(junctionCollection.value).map((field: Field) => ({
text: field.field,
value: field.field,
}));
});
return { _relations, collectionItems, junctionCollection, junctionFields };
},
});
</script>
<style lang="scss" scoped>
.grid {
--v-select-font-family: var(--family-monospace);
--v-input-font-family: var(--family-monospace);
position: relative;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin-top: 48px;
.v-icon {
--v-icon-color: var(--foreground-subdued);
position: absolute;
transform: translateX(-50%);
&:first-of-type {
bottom: 85px;
left: 32.8%;
}
&:last-of-type {
bottom: 14px;
left: 67%;
}
}
}
.type-label {
margin-bottom: 8px;
}
</style>

View File

@@ -100,6 +100,9 @@ export default defineComponent({
<style lang="scss" scoped>
.grid {
--v-select-font-family: var(--family-monospace);
--v-input-font-family: var(--family-monospace);
position: relative;
display: grid;
grid-template-columns: repeat(2, 1fr);

View File

@@ -10,7 +10,9 @@
<div class="field">
<div class="label type-label">{{ $t('type') }}</div>
<v-input v-if="!_field.database" :value="$t('alias')" disabled />
<v-select
v-else
:disabled="typeDisabled"
:value="_field.database.type"
@input="setType"
@@ -20,11 +22,11 @@
<div class="field full">
<div class="label type-label">{{ $t('note') }}</div>
<v-input v-model="_field.database.comment" :placeholder="$t('add_note')" />
<v-input v-model="_field.system.comment" :placeholder="$t('add_note')" />
</div>
<!-- @todo base default value field type on selected type -->
<div class="field">
<div class="field" v-if="_field.database">
<div class="label type-label">{{ $t('default_value') }}</div>
<v-input
class="monospace"
@@ -33,7 +35,7 @@
/>
</div>
<div class="field">
<div class="field" v-if="_field.database">
<div class="label type-label">{{ $t('length') }}</div>
<v-input
type="number"
@@ -43,7 +45,7 @@
/>
</div>
<div class="field">
<div class="field" v-if="_field.database">
<div class="label type-label">{{ $t('allow_null') }}</div>
<v-checkbox v-model="_field.database.is_nullable" :label="$t('allow_null_label')" block />
</div>

View File

@@ -10,6 +10,7 @@
:field-data.sync="fieldData"
:type="type"
/>
<setup-relationship
:collection="collection"
v-if="currentTab[0] === 'relationship'"
@@ -17,12 +18,14 @@
:relations.sync="relations"
:type="type"
/>
<setup-interface
:collection="collection"
v-if="currentTab[0] === 'interface'"
:field-data.sync="fieldData"
:type="type"
/>
<setup-display
:collection="collection"
v-if="currentTab[0] === 'display'"
@@ -115,7 +118,7 @@ export default defineComponent({
},
];
if (['o2m', 'm2o', 'm2m'].includes(props.type)) {
if (['o2m', 'm2o', 'm2m', 'files'].includes(props.type)) {
tabs.splice(1, 0, {
text: i18n.t('relationship'),
value: 'relationship',
@@ -131,7 +134,10 @@ export default defineComponent({
return { tabs, currentTab };
function relationshipDisabled() {
return isEmpty(fieldData.field) || isEmpty(fieldData.database.type);
return (
isEmpty(fieldData.field) ||
(['o2m', 'm2m', 'files'].includes(props.type) === false && isEmpty(fieldData.database.type))
);
}
function interfaceDisplayDisabled() {
@@ -162,7 +168,6 @@ export default defineComponent({
default_value: undefined,
max_length: undefined,
is_nullable: true,
comment: undefined,
},
system: {
hidden: false,
@@ -172,6 +177,7 @@ export default defineComponent({
display_options: undefined,
readonly: false,
special: undefined,
note: undefined,
},
});
@@ -217,6 +223,63 @@ export default defineComponent({
);
}
if (props.type === 'm2m' || props.type === 'files') {
delete fieldData.database;
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 };
}
@@ -225,6 +288,7 @@ export default defineComponent({
try {
await api.post(`/fields/${props.collection}`, fieldData);
await api.post(`/relations`, relations.value);
} catch (error) {
console.error(error);
} finally {

View File

@@ -33,6 +33,7 @@ const fakeFilesField: Field = {
readonly: true,
width: 'full',
group: null,
note: null,
},
};
@@ -54,6 +55,7 @@ function getSystemDefault(collection: string, field: string): Field['system'] {
special: null,
translation: null,
width: 'full',
note: null,
};
}

View File

@@ -62,6 +62,7 @@ export type SystemField = {
special: string | null;
translation: null | Translation[];
width: Width | null;
note: string | null;
};
export interface FieldRaw {