From df3f6edf83ea9ae1246205bac27722697a31f253 Mon Sep 17 00:00:00 2001 From: Rijk van Zanten Date: Thu, 4 Jun 2020 18:28:12 -0400 Subject: [PATCH] Many to Many (#675) * Start on files inteface * Return related m2m relation in store * Add useRelations to files * Fetch current items in files * Allow dot notation in table row * Render tableHeaders * Add file display * Register file display * Remove unused css * Rough in select modal * Start on saving selected items * Finish selecting existing item * Auto fix line width of file display * Rename var to be clearer * Rework to allow editing junctino row * Add support for junction fields in modal detail * Stage changes to existing items correctly * Show previously made edits in modal detail * Blammo bunch of changes, lets do m2m first * Stage newly created items * Stage newly created items * Add editing of newly added records * Stage new edits of existing items * Allow updating existing rows * Stage selection of newly selected items * Add fetching preview for related item * Allow for updating staged selected existing items * Fix nested m2o in m2m * Finish deep fetch of multi-related item * Finish deselecting of rows * Add a little note to future self --- .../v-table/table-row/table-row.vue | 5 +- src/displays/file/file.vue | 92 +++ src/displays/file/index.ts | 12 + src/displays/image/image.vue | 5 +- src/displays/index.ts | 2 + src/interfaces/file/file.vue | 1 - src/interfaces/index.ts | 2 + src/interfaces/many-to-many/index.ts | 10 + src/interfaces/many-to-many/many-to-many.vue | 716 ++++++++++++++++++ src/interfaces/many-to-one/many-to-one.vue | 7 +- src/interfaces/one-to-many/one-to-many.vue | 6 +- src/lang/en-US/index.json | 2 + src/stores/relations/relations.ts | 22 +- .../adjust-fields-for-displays.ts | 5 +- .../components/modal-detail/modal-detail.vue | 116 ++- 15 files changed, 990 insertions(+), 13 deletions(-) create mode 100644 src/displays/file/file.vue create mode 100644 src/displays/file/index.ts create mode 100644 src/interfaces/many-to-many/index.ts create mode 100644 src/interfaces/many-to-many/many-to-many.vue diff --git a/src/components/v-table/table-row/table-row.vue b/src/components/v-table/table-row/table-row.vue index f9be6670c8..e017e86a9f 100644 --- a/src/components/v-table/table-row/table-row.vue +++ b/src/components/v-table/table-row/table-row.vue @@ -15,7 +15,7 @@ - {{ item[header.value] }} + {{ get(item, header.value) }} @@ -27,6 +27,7 @@ + + diff --git a/src/displays/file/index.ts b/src/displays/file/index.ts new file mode 100644 index 0000000000..44a25fc2fd --- /dev/null +++ b/src/displays/file/index.ts @@ -0,0 +1,12 @@ +import { defineDisplay } from '@/displays/define'; +import DisplayFile from './file.vue'; + +export default defineDisplay(({ i18n }) => ({ + id: 'file', + name: i18n.t('file'), + types: ['file'], + icon: 'insert_photo', + handler: DisplayFile, + options: [], + fields: ['data', 'type', 'title'], +})); diff --git a/src/displays/image/image.vue b/src/displays/image/image.vue index e9efa5eaed..22d8dd41d5 100644 --- a/src/displays/image/image.vue +++ b/src/displays/image/image.vue @@ -1,13 +1,15 @@ + + diff --git a/src/interfaces/many-to-one/many-to-one.vue b/src/interfaces/many-to-one/many-to-one.vue index 829fe13f90..4515bcbad5 100644 --- a/src/interfaces/many-to-one/many-to-one.vue +++ b/src/interfaces/many-to-one/many-to-one.vue @@ -71,7 +71,11 @@ @click="setCurrent(item)" > - + @@ -185,6 +189,7 @@ export default defineComponent({ edits, stageEdits, editModalActive, + relatedPrimaryKeyField, }; function useCurrent() { diff --git a/src/interfaces/one-to-many/one-to-many.vue b/src/interfaces/one-to-many/one-to-many.vue index 3df79901cb..7a0cf04ff9 100644 --- a/src/interfaces/one-to-many/one-to-many.vue +++ b/src/interfaces/one-to-many/one-to-many.vue @@ -239,7 +239,7 @@ export default defineComponent({ const pkField = relatedPrimaryKeyField.value.field; - const updatedItems = items.value + const itemsWithChangesApplied = items.value .map((item: any) => { const changeForThisItem = changes.find((change) => change[pkField] === item[pkField]); @@ -265,7 +265,7 @@ export default defineComponent({ const selectedPrimaryKeys = changes .filter((change) => typeof change === 'string' || typeof change === 'number') .filter((primaryKey) => { - const isAlsoUpdate = updatedItems.some((update) => update[pkField] === primaryKey); + const isAlsoUpdate = itemsWithChangesApplied.some((update) => update[pkField] === primaryKey); return isAlsoUpdate === false; }); @@ -297,7 +297,7 @@ export default defineComponent({ } } - items.value = [...updatedItems, ...newlyAddedItems, ...selectedItems]; + items.value = [...itemsWithChangesApplied, ...newlyAddedItems, ...selectedItems]; loading.value = false; } } diff --git a/src/lang/en-US/index.json b/src/lang/en-US/index.json index 292d4434cf..d963492a17 100644 --- a/src/lang/en-US/index.json +++ b/src/lang/en-US/index.json @@ -81,6 +81,8 @@ "settings_update_success": "Settings updated", "settings_update_failed": "Updating settings failed", + "title": "Title", + "revision_delta_created": "Created", "revision_delta_created_externally": "Created Externally", "revision_delta_updated": "Updated {count} Fields", diff --git a/src/stores/relations/relations.ts b/src/stores/relations/relations.ts index cee2b2bc97..9efb0e6cb1 100644 --- a/src/stores/relations/relations.ts +++ b/src/stores/relations/relations.ts @@ -56,9 +56,29 @@ export const useRelationsStore = createStore({ ] as Relation[]; } - return this.getRelationsForCollection(collection).filter((relation: Relation) => { + const relations = this.getRelationsForCollection(collection).filter((relation: Relation) => { return relation.field_many === field || relation.field_one === field; }); + + if (relations.length > 0) { + const isM2M = relations[0].junction_field !== null; + + // If the relation matching the field has a junction field, it's a m2m. In that case, + // we also want to return the secondary relationship (from the jt to the related) + // so any ui elements (interfaces) can utilize the full relationship + if (isM2M) { + const secondaryRelation = this.state.relations.find((relation) => { + return ( + relation.collection_many === relations[0].collection_many && + relation.field_many === relations[0].junction_field + ); + }); + + if (secondaryRelation) relations.push(secondaryRelation); + } + } + + return relations; }, }, }); diff --git a/src/utils/adjust-fields-for-displays/adjust-fields-for-displays.ts b/src/utils/adjust-fields-for-displays/adjust-fields-for-displays.ts index ffc7b33b84..d7520325f2 100644 --- a/src/utils/adjust-fields-for-displays/adjust-fields-for-displays.ts +++ b/src/utils/adjust-fields-for-displays/adjust-fields-for-displays.ts @@ -14,6 +14,7 @@ export default function adjustFieldsForDisplays(fields: readonly string[], paren const display = displays.find((d) => d.id === field.display); + if (!display) return fieldKey; if (!display?.fields) return fieldKey; if (Array.isArray(display.fields)) { @@ -23,8 +24,8 @@ export default function adjustFieldsForDisplays(fields: readonly string[], paren if (typeof display.fields === 'function') { return display .fields(field.display_options, { - collection: parentCollection, - field: fieldKey, + collection: field.collection, + field: field.field, type: field.type, }) .map((relatedFieldKey: string) => `${fieldKey}.${relatedFieldKey}`); diff --git a/src/views/private/components/modal-detail/modal-detail.vue b/src/views/private/components/modal-detail/modal-detail.vue index 37ea95efbb..0bb987dea7 100644 --- a/src/views/private/components/modal-detail/modal-detail.vue +++ b/src/views/private/components/modal-detail/modal-detail.vue @@ -8,6 +8,18 @@ v-model="_edits" /> + +