From 13fa2ed4b6667ee2eaa22a4afc9782307575381b Mon Sep 17 00:00:00 2001 From: rijkvanzanten Date: Fri, 24 Jul 2020 10:00:01 -0400 Subject: [PATCH] Add m2o corresponding --- src/components/v-select/v-select.vue | 3 + src/interfaces/file/index.ts | 3 +- src/interfaces/files/index.ts | 1 + src/lang/en-US/index.json | 3 + .../components/relationship-m2m.vue | 2 + .../components/relationship-m2o.vue | 128 ++++++++++++++---- .../field-detail/components/relationship.vue | 11 +- .../field-detail/components/schema.vue | 13 +- .../data-model/field-detail/field-detail.vue | 42 +++++- 9 files changed, 170 insertions(+), 36 deletions(-) diff --git a/src/components/v-select/v-select.vue b/src/components/v-select/v-select.vue index bdd1c2962d..dc70a102ec 100644 --- a/src/components/v-select/v-select.vue +++ b/src/components/v-select/v-select.vue @@ -46,6 +46,7 @@ v-for="item in _items" :key="item.value" :active="multiple ? (value || []).includes(item.value) : value === item.value" + :disabled="item.disabled" @click="multiple ? null : $emit('input', item.value)" > @@ -117,6 +118,7 @@ import { useCustomSelection, useCustomSelectionMultiple } from '@/composables/us type Item = { text: string; value: string; + disabled?: boolean; }; type ItemsRaw = (string | any)[]; @@ -207,6 +209,7 @@ export default defineComponent({ return { text: item[props.itemText], value: item[props.itemValue], + disabled: item.disabled, }; }); diff --git a/src/interfaces/file/index.ts b/src/interfaces/file/index.ts index 473dceb75b..11ae9629ea 100644 --- a/src/interfaces/file/index.ts +++ b/src/interfaces/file/index.ts @@ -6,6 +6,7 @@ export default defineInterface(({ i18n }) => ({ name: i18n.t('file'), icon: 'note_add', component: InterfaceFile, - types: ['string'], + types: ['uuid'], + relationship: 'm2o', options: [], })); diff --git a/src/interfaces/files/index.ts b/src/interfaces/files/index.ts index 2480e0fc9a..3a9a9cc7b0 100644 --- a/src/interfaces/files/index.ts +++ b/src/interfaces/files/index.ts @@ -7,5 +7,6 @@ export default defineInterface(({ i18n }) => ({ icon: 'note_add', component: InterfaceFiles, types: ['alias'], + relationship: 'm2m', options: [], })); diff --git a/src/lang/en-US/index.json b/src/lang/en-US/index.json index d43ec1d106..003342a731 100644 --- a/src/lang/en-US/index.json +++ b/src/lang/en-US/index.json @@ -53,6 +53,9 @@ "configure_m2o": "Configure your Many-to-One Relationship...", "configure_m2m": "Configure your Many-to-Many Relationship...", + "choose_a_type": "Choose a Type...", + "determined_by_relationship": "Determined by Relationship", + "include_seconds": "Include Seconds", "add_note": "Add a helpful note for users...", diff --git a/src/modules/settings/routes/data-model/field-detail/components/relationship-m2m.vue b/src/modules/settings/routes/data-model/field-detail/components/relationship-m2m.vue index 8bca9e4395..a9ab64f0c3 100644 --- a/src/modules/settings/routes/data-model/field-detail/components/relationship-m2m.vue +++ b/src/modules/settings/routes/data-model/field-detail/components/relationship-m2m.vue @@ -100,6 +100,8 @@ export default defineComponent({ return fieldsStore.getFieldsForCollection(junctionCollection.value).map((field: Field) => ({ text: field.field, value: field.field, + disabled: + _relations.value[0].field_many === field.field || _relations.value[1].field_many === field.field, })); }); diff --git a/src/modules/settings/routes/data-model/field-detail/components/relationship-m2o.vue b/src/modules/settings/routes/data-model/field-detail/components/relationship-m2o.vue index 2ce16f2328..15ff9902b1 100644 --- a/src/modules/settings/routes/data-model/field-detail/components/relationship-m2o.vue +++ b/src/modules/settings/routes/data-model/field-detail/components/relationship-m2o.vue @@ -24,11 +24,11 @@
{{ $t('create_corresponding_field') }}
- +
{{ $t('corresponding_field_name') }}
- +
@@ -36,8 +36,9 @@ diff --git a/src/modules/settings/routes/data-model/field-detail/components/relationship.vue b/src/modules/settings/routes/data-model/field-detail/components/relationship.vue index 7f78372731..1e7cf01d58 100644 --- a/src/modules/settings/routes/data-model/field-detail/components/relationship.vue +++ b/src/modules/settings/routes/data-model/field-detail/components/relationship.vue @@ -3,6 +3,7 @@ :collection="collection" :field-data="fieldData" :relations.sync="_relations" + :new-fields.sync="_newFields" :type="type" v-if="type === 'm2o' || type === 'file'" /> @@ -10,6 +11,7 @@ :collection="collection" :field-data="fieldData" :relations.sync="_relations" + :new-fields.sync="_newFields" :type="type" v-else-if="type === 'o2m'" /> @@ -17,6 +19,7 @@ :collection="collection" :field-data="fieldData" :relations.sync="_relations" + :new-fields.sync="_newFields" :type="type" v-else-if="type === 'm2m' || type === 'files'" /> @@ -25,6 +28,7 @@ diff --git a/src/modules/settings/routes/data-model/field-detail/components/schema.vue b/src/modules/settings/routes/data-model/field-detail/components/schema.vue index e186a47dbc..e7e3a00307 100644 --- a/src/modules/settings/routes/data-model/field-detail/components/schema.vue +++ b/src/modules/settings/routes/data-model/field-detail/components/schema.vue @@ -17,6 +17,7 @@ :value="_field.database.type" @input="setType" :items="typesWithLabels" + :placeholder="typePlaceholder" /> @@ -101,10 +102,18 @@ export default defineComponent({ ); const typeDisabled = computed(() => { - return ['file', 'files', 'o2m', 'm2m'].includes(props.type); + return ['file', 'files', 'o2m', 'm2m', 'm2o'].includes(props.type); }); - return { _field, typesWithLabels, setType, typeDisabled }; + const typePlaceholder = computed(() => { + if (props.type === 'm2o') { + return i18n.t('determined_by_relationship'); + } + + return i18n.t('choose_a_type'); + }); + + return { _field, typesWithLabels, setType, typeDisabled, typePlaceholder }; function setType(value: typeof types[number]) { if (value === 'uuid') { diff --git a/src/modules/settings/routes/data-model/field-detail/field-detail.vue b/src/modules/settings/routes/data-model/field-detail/field-detail.vue index b0c98644cd..e8315eaa93 100644 --- a/src/modules/settings/routes/data-model/field-detail/field-detail.vue +++ b/src/modules/settings/routes/data-model/field-detail/field-detail.vue @@ -16,6 +16,7 @@ v-if="currentTab[0] === 'relationship'" :field-data.sync="fieldData" :relations.sync="relations" + :new-fields.sync="newFields" :type="type" /> @@ -58,6 +59,7 @@ import { isEmpty } from 'lodash'; import api from '@/api'; import { Relation } from '@/stores/relations/types'; import { useFieldsStore } from '@/stores/fields'; +import { Field } from '@/stores/fields/types'; export default defineComponent({ components: { @@ -92,11 +94,11 @@ export default defineComponent({ const fieldsStore = useFieldsStore(); const { tabs, currentTab } = useTabs(); - const { fieldData, relations } = useData(); + const { fieldData, relations, newFields } = useData(); const saving = ref(false); - return { active, tabs, currentTab, fieldData, saveField, saving, relations }; + return { active, tabs, currentTab, fieldData, saveField, saving, relations, newFields }; function useTabs() { const tabs = computed(() => { @@ -136,7 +138,7 @@ export default defineComponent({ function relationshipDisabled() { return ( isEmpty(fieldData.field) || - (['o2m', 'm2m', 'files'].includes(props.type) === false && isEmpty(fieldData.database.type)) + (['o2m', 'm2m', 'files', 'm2o'].includes(props.type) === false && isEmpty(fieldData.database.type)) ); } @@ -151,8 +153,15 @@ export default defineComponent({ } if (['m2m', 'files'].includes(props.type)) { - /** @todo extend with all values */ - return relations.value.length !== 2; + 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) + ); } return isEmpty(fieldData.field) || isEmpty(fieldData.database.type); @@ -183,6 +192,10 @@ export default defineComponent({ const relations = ref[]>([]); + // 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[]>([]); + if (props.type === 'file') { fieldData.database.type = 'uuid'; @@ -221,6 +234,16 @@ export default defineComponent({ 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; + } + ); } if (props.type === 'm2m' || props.type === 'files') { @@ -280,7 +303,7 @@ export default defineComponent({ ); } - return { fieldData, relations }; + return { fieldData, relations, newFields }; } async function saveField() { @@ -288,6 +311,13 @@ export default defineComponent({ try { await api.post(`/fields/${props.collection}`, fieldData); + + await Promise.all( + newFields.value.map((newField) => { + return api.post(`/fields/${newField.collection}`, newField); + }) + ); + await api.post(`/relations`, relations.value); } catch (error) { console.error(error);