diff --git a/app/src/modules/settings/routes/data-model/field-detail/components/relationship-m2m.vue b/app/src/modules/settings/routes/data-model/field-detail/components/relationship-m2m.vue
index e02040b447..1e82e1106e 100644
--- a/app/src/modules/settings/routes/data-model/field-detail/components/relationship-m2m.vue
+++ b/app/src/modules/settings/routes/data-model/field-detail/components/relationship-m2m.vue
@@ -13,6 +13,7 @@
v-model="junctionCollection"
:placeholder="$t('select_one')"
:disabled="isExisting"
+ allow-other
/>
@@ -111,7 +112,7 @@ export default defineComponent({
value: field.field,
disabled:
state.relations[0].many_field === field.field ||
- field.schema?.is_primary_key ||
+ field.schema?.is_primary_key ||
state.relations[1].many_field === field.field,
}));
});
diff --git a/app/src/modules/settings/routes/data-model/field-detail/components/relationship-m2o.vue b/app/src/modules/settings/routes/data-model/field-detail/components/relationship-m2o.vue
index b894e02238..b4ca1aaad5 100644
--- a/app/src/modules/settings/routes/data-model/field-detail/components/relationship-m2o.vue
+++ b/app/src/modules/settings/routes/data-model/field-detail/components/relationship-m2o.vue
@@ -8,16 +8,32 @@
{{ $t('related_collection') }}
-
+
+
+
+
+
+
+
+
+
+
+ {{ item.text }}
+
+
+
+
+
+
-
-
-
+
+
+
@@ -31,7 +47,7 @@
{{ $t('corresponding_field_name') }}
-
+
@@ -66,17 +82,21 @@ export default defineComponent({
const collectionsStore = useCollectionsStore();
const fieldsStore = useFieldsStore();
- const { items, relatedPrimary } = useRelation();
+ const { items } = useRelation();
const { hasCorresponding, correspondingField, correspondingLabel } = useCorresponding();
+ const isNewCollection = computed(() => {
+ return collectionsStore.getCollection(state.relations[0].one_collection) === null;
+ });
+
return {
relations: state.relations,
items,
- relatedPrimary,
hasCorresponding,
correspondingField,
correspondingLabel,
fieldData: state.fieldData,
+ isNewCollection,
};
function useRelation() {
@@ -97,13 +117,7 @@ export default defineComponent({
}))
);
- const relatedPrimary = computed(() => {
- return state.relations[0].one_collection
- ? fieldsStore.getPrimaryKeyFieldForCollection(state.relations[0].one_collection)?.field
- : null;
- });
-
- return { items, relatedPrimary };
+ return { items };
}
function useCorresponding() {
@@ -175,7 +189,7 @@ export default defineComponent({
gap: 20px 32px;
margin-top: 48px;
- .v-icon {
+ .arrow {
--v-icon-color: var(--foreground-subdued);
position: absolute;
@@ -185,6 +199,10 @@ export default defineComponent({
}
}
+.v-list {
+ --v-list-item-content-font-family: var(--family-monospace);
+}
+
.v-divider {
margin: 48px 0;
}
diff --git a/app/src/modules/settings/routes/data-model/field-detail/field-detail.vue b/app/src/modules/settings/routes/data-model/field-detail/field-detail.vue
index e3ea5445b6..b6c5bc1a40 100644
--- a/app/src/modules/settings/routes/data-model/field-detail/field-detail.vue
+++ b/app/src/modules/settings/routes/data-model/field-detail/field-detail.vue
@@ -66,8 +66,8 @@ import SetupDisplay from './components/display.vue';
import { i18n } from '@/lang';
import { isEmpty } from 'lodash';
import api from '@/api';
-import { Relation } from '@/types';
-import { useFieldsStore, useRelationsStore } from '@/stores/';
+import { Relation, Collection } from '@/types';
+import { useFieldsStore, useRelationsStore, useCollectionsStore } from '@/stores/';
import { Field } from '@/types';
import router from '@/router';
import useCollection from '@/composables/use-collection';
@@ -99,6 +99,7 @@ export default defineComponent({
},
},
setup(props) {
+ const collectionsStore = useCollectionsStore();
const fieldsStore = useFieldsStore();
const relationsStore = useRelationsStore();
@@ -190,7 +191,8 @@ export default defineComponent({
state.relations.length === 0 ||
isEmpty(state.relations[0].many_collection) ||
isEmpty(state.relations[0].many_field) ||
- isEmpty(state.relations[0].one_collection)
+ isEmpty(state.relations[0].one_collection) ||
+ isEmpty(state.relations[0].one_primary)
);
}
@@ -224,6 +226,12 @@ export default defineComponent({
await api.post(`/fields/${props.collection}`, state.fieldData);
}
+ await Promise.all(
+ state.newCollections.map((newCollection: Partial) => {
+ return api.post(`/collections`, newCollection);
+ })
+ );
+
await Promise.all(
state.newFields.map((newField: Partial) => {
return api.post(`/fields/${newField.collection}`, newField);
@@ -240,6 +248,7 @@ export default defineComponent({
})
);
+ await collectionsStore.hydrate();
await fieldsStore.hydrate();
await relationsStore.hydrate();
diff --git a/app/src/modules/settings/routes/data-model/field-detail/store.ts b/app/src/modules/settings/routes/data-model/field-detail/store.ts
index e3b31e02c2..ff8898e863 100644
--- a/app/src/modules/settings/routes/data-model/field-detail/store.ts
+++ b/app/src/modules/settings/routes/data-model/field-detail/store.ts
@@ -5,9 +5,9 @@
* It's reset every time the modal opens and shouldn't be used outside of the field-detail flow.
*/
-import { useFieldsStore, useRelationsStore } from '@/stores/';
+import { useFieldsStore, useRelationsStore, useCollectionsStore } from '@/stores/';
import { reactive, watch, computed, ComputedRef } from '@vue/composition-api';
-import { clone } from 'lodash';
+import { clone, throttle } from 'lodash';
import { getInterfaces } from '@/interfaces';
import { getDisplays } from '@/displays';
import { InterfaceConfig } from '@/interfaces/types';
@@ -27,6 +27,7 @@ function initLocalStore(
field: string,
type: 'standard' | 'file' | 'files' | 'm2o' | 'o2m' | 'm2m' | 'presentation'
) {
+ const collectionsStore = useCollectionsStore();
const interfaces = getInterfaces();
const displays = getDisplays();
@@ -51,6 +52,7 @@ function initLocalStore(
},
},
relations: [],
+ newCollections: [],
newFields: [],
});
@@ -141,7 +143,33 @@ function initLocalStore(
}
if (type === 'm2o') {
- if (!isExisting) {
+ const syncNewCollectionsM2O = throttle(() => {
+ const collectionName = state.relations[0].one_collection;
+
+ if (collectionExists(collectionName)) {
+ state.newCollections = [];
+ } else {
+ state.newCollections = [
+ {
+ collection: collectionName,
+ fields: [
+ {
+ field: state.relations[0].one_primary,
+ type: 'integer',
+ schema: {
+ has_auto_increment: true,
+ },
+ system: {
+ interface: 'text-input',
+ }
+ }
+ ]
+ }
+ ];
+ }
+ }, 50);
+
+ if (isExisting === false) {
state.relations = [
{
many_collection: collection,
@@ -165,9 +193,13 @@ function initLocalStore(
watch(
() => state.relations[0].one_collection,
() => {
- const field = fieldsStore.getPrimaryKeyFieldForCollection(state.relations[0].one_collection);
- state.fieldData.type = field.type;
- state.relations[0].one_primary = field.field;
+ if (collectionExists(state.relations[0].one_collection)) {
+ const field = fieldsStore.getPrimaryKeyFieldForCollection(state.relations[0].one_collection);
+ state.fieldData.type = field.type;
+ state.relations[0].one_primary = field.field;
+ } else {
+ state.fieldData.type = 'integer';
+ }
}
);
@@ -180,6 +212,8 @@ function initLocalStore(
}
}
);
+
+ watch([() => state.relations[0].one_collection, () => state.relations[0].one_primary], syncNewCollectionsM2O);
}
if (type === 'o2m') {
@@ -322,6 +356,10 @@ function initLocalStore(
}
);
}
+
+ function collectionExists(collection: string) {
+ return collectionsStore.getCollection(collection) !== null;
+ }
}
function clearLocalStore() {