diff --git a/app/src/interfaces/many-to-many/many-to-many.vue b/app/src/interfaces/many-to-many/many-to-many.vue
index 6d5b3743af..98111cefdd 100644
--- a/app/src/interfaces/many-to-many/many-to-many.vue
+++ b/app/src/interfaces/many-to-many/many-to-many.vue
@@ -21,35 +21,32 @@
:interface="header.field.interface"
:interface-options="header.field.interfaceOptions"
:type="header.field.type"
- :collection="relationFields.junctionCollection"
+ :collection="relationInfo.junctionCollection"
:field="header.field.field"
/>
-
+
- {{ $t('create_new') }}
+ {{ $t('create_new') }}
{{ $t('add_existing') }}
+ {{ JSON.stringify(value, null, 4) }}
+
)[] | null>,
@@ -19,31 +19,32 @@ export default function useActions(
}
function getNewSelectedItems() {
- const { junctionRelation } = relation.value;
+ const { junctionField } = relation.value;
- if (value.value === null || junctionRelation === null) return [];
+ if (value.value === null || junctionField === null) return [];
return value.value.filter(
- (item) => typeof item === 'object' && junctionRelation in item && typeof item[junctionRelation] !== 'object'
+ (item) => typeof item === 'object' && junctionField in item && typeof item[junctionField] !== 'object'
) as Record[];
}
function getNewItems() {
- const { junctionRelation, relationPkField } = relation.value;
+ const { junctionField, relationPkField } = relation.value;
- if (value.value === null || junctionRelation === null) return [];
+ if (value.value === null || junctionField === null) return [];
return value.value.filter(
- (item) => has(item, junctionRelation) && has(item, [junctionRelation, relationPkField]) === false
+ (item) =>
+ typeof get(item, junctionField) === 'object' && has(item, [junctionField, relationPkField]) === false
) as Record[];
}
function getUpdatedItems() {
- const { junctionRelation, relationPkField } = relation.value;
+ const { junctionField, relationPkField } = relation.value;
- if (value.value === null || junctionRelation === null) return [];
+ if (value.value === null || junctionField === null) return [];
- return value.value.filter((item) => has(item, [junctionRelation, relationPkField])) as Record[];
+ return value.value.filter((item) => has(item, [junctionField, relationPkField])) as Record[];
}
function getExistingItems() {
@@ -57,112 +58,69 @@ export default function useActions(
if (value.value === null) return [];
- return value.value
- .map((item) => {
- if (typeof item === 'object') {
- if (junctionPkField in item) return item[junctionPkField];
- } else {
- return item;
- }
- })
- .filter((i) => i);
+ return value.value.reduce((acc: any[], item) => {
+ const deepId = get(item, [junctionPkField]) as number | string | undefined;
+
+ if (['string', 'number'].includes(typeof item)) acc.push(item);
+ else if (deepId !== undefined) acc.push(deepId);
+ return acc;
+ }, []) as (string | number)[];
}
- function getRelatedPrimaryKeys(): (string | number)[] {
+ function getRelatedPrimaryKeys() {
if (value.value === null) return [];
- const { junctionRelation, relationPkField } = relation.value;
- return value.value
- .map((junctionItem) => {
- if (
- typeof junctionItem !== 'object' ||
- junctionRelation === null ||
- junctionRelation in junctionItem === false
- )
- return undefined;
- const item = junctionItem[junctionRelation];
+ const { junctionField, relationPkField } = relation.value;
+
+ return value.value.reduce((acc: any[], item) => {
+ const relatedId = get(item, junctionField) as number | string | undefined;
+ const deepRelatedId = get(item, [junctionField, relationPkField]) as number | string | undefined;
+
+ if (relatedId !== undefined) acc.push(relatedId);
+ else if (deepRelatedId !== undefined) acc.push(deepRelatedId);
+ return acc;
+ }, []) as (string | number)[];
+ }
+
+ function deleteItem(deletingItem: Record) {
+ if (value.value === null) return;
+ const { junctionField, relationPkField, junctionPkField } = relation.value;
+
+ const junctionId = get(deletingItem, junctionPkField) as number | string | undefined;
+ const relatedId = get(deletingItem, [junctionField, relationPkField]) as number | string | undefined;
+
+ console.log(junctionId, relatedId);
+
+ const newValue = value.value.filter((item) => {
+ if (junctionId !== undefined) {
if (typeof item === 'object') {
- if (junctionRelation in item) return item[relationPkField];
+ return get(item, [junctionPkField]) !== junctionId;
} else {
- return item;
+ return item !== junctionId;
}
- })
- .filter((i) => i);
- }
-
- function deleteItem(item: Record, items: Record[]) {
- if (value.value === null) return;
- const { junctionRelation, relationPkField } = relation.value;
-
- const id = item[relationPkField] as number | string | undefined;
-
- if (id !== undefined) return deleteItemWithId(id, items);
- if (junctionRelation === null) return;
-
- const newVal = value.value.filter((junctionItem) => {
- if (typeof junctionItem !== 'object' || junctionRelation in junctionItem === false) return true;
- return junctionItem[junctionRelation] !== item;
- });
-
- if (newVal.length === 0) emit(null);
- else emit(newVal);
- }
-
- function deleteItemWithId(id: string | number, items: Record[]) {
- if (value.value === null) return;
- const { junctionRelation, relationPkField, junctionPkField } = relation.value;
-
- const junctionItem = items.find(
- (item) =>
- junctionRelation in item &&
- relationPkField in item[junctionRelation] &&
- item[junctionRelation][relationPkField] === id
- );
-
- if (junctionItem === undefined) return;
-
- // If it is a newly selected Item
- if (junctionPkField in junctionItem === false) {
- const newVal = value.value.filter((item) => {
- if (typeof item === 'object' && junctionRelation in item) {
- const jItem = item[junctionRelation];
- return typeof jItem === 'object' ? jItem[relationPkField] !== id : jItem !== id;
- }
- return true;
- });
-
- if (newVal.length === 0) emit(null);
- else emit(newVal);
- return;
- }
-
- // If it is an already existing item
- const newVal = value.value.filter((item) => {
- if (typeof item === 'object' && junctionPkField in item) {
- return junctionItem[junctionPkField] !== item[junctionPkField];
- } else {
- return junctionItem[junctionPkField] !== item;
}
- });
- if (newVal.length === 0) emit(null);
- else emit(newVal);
+ if (relatedId !== undefined) {
+ const itemRelatedId = get(item, [junctionField, relationPkField]);
+ if (['string', 'number'].includes(typeof itemRelatedId)) {
+ return itemRelatedId !== relatedId;
+ }
+
+ const junctionFieldId = get(item, [junctionField]);
+ if (['string', 'number'].includes(typeof junctionFieldId)) {
+ return junctionFieldId !== relatedId;
+ }
+ }
+
+ return isEqual(item, deletingItem) === false;
+ });
+ emit(newValue);
}
function getJunctionFromRelatedId(id: string | number, items: Record[]) {
- const { relationPkField, junctionRelation } = relation.value;
+ const { relationPkField, junctionField } = relation.value;
- return (
- items.find((item) => {
- return (
- typeof item === 'object' &&
- junctionRelation in item &&
- typeof item[junctionRelation] === 'object' &&
- relationPkField in item[junctionRelation] &&
- item[junctionRelation][relationPkField] === id
- );
- }) || null
- );
+ return items.find((item) => get(item, [junctionField, relationPkField]) === id) || null;
}
return {
@@ -175,6 +133,5 @@ export default function useActions(
getRelatedPrimaryKeys,
getJunctionFromRelatedId,
deleteItem,
- deleteItemWithId,
};
}
diff --git a/app/src/interfaces/many-to-many/use-edit.ts b/app/src/interfaces/many-to-many/use-edit.ts
index 1f0bbeed50..bdf79148c0 100644
--- a/app/src/interfaces/many-to-many/use-edit.ts
+++ b/app/src/interfaces/many-to-many/use-edit.ts
@@ -7,8 +7,7 @@ export default function useEdit(
relation: Ref,
emit: (newVal: any[] | null) => void
) {
- // Primary key of the item we're currently editing. If null, the edit modal should be
- // closed
+ const editModalActive = ref(false);
const currentlyEditing = ref(null);
const relatedPrimaryKey = ref(null);
@@ -16,15 +15,16 @@ export default function useEdit(
const editsAtStart = ref>({});
function editItem(item: any) {
- const { relationPkField, junctionRelation, junctionPkField } = relation.value;
+ const { relationPkField, junctionField, junctionPkField } = relation.value;
+ editModalActive.value = true;
editsAtStart.value = item;
- relatedPrimaryKey.value = get(item, [junctionRelation, relationPkField], null);
currentlyEditing.value = get(item, [junctionPkField], null);
+ relatedPrimaryKey.value = get(item, [junctionField, relationPkField], null);
}
function stageEdits(edits: any) {
- const { relationPkField, junctionRelation, junctionPkField } = relation.value;
+ const { relationPkField, junctionField, junctionPkField } = relation.value;
const newValue = (value.value || []).map((item) => {
if (currentlyEditing.value !== null) {
@@ -40,8 +40,8 @@ export default function useEdit(
if (relatedPrimaryKey.value != null) {
const id = relatedPrimaryKey.value;
- if (get(item, [junctionRelation], null) === id) return edits;
- if (get(item, [junctionRelation, relationPkField], null) === id) return edits;
+ if (get(item, [junctionField], null) === id) return edits;
+ if (get(item, [junctionField, relationPkField], null) === id) return edits;
}
if (isEqual(editsAtStart.value, item)) {
@@ -57,13 +57,16 @@ export default function useEdit(
if (newValue.length === 0) emit(null);
else emit(newValue);
+
+ cancelEdit();
}
function cancelEdit() {
+ editModalActive.value = false;
editsAtStart.value = {};
currentlyEditing.value = null;
relatedPrimaryKey.value = null;
}
- return { currentlyEditing, editItem, editsAtStart, stageEdits, cancelEdit, relatedPrimaryKey };
+ return { currentlyEditing, editItem, editsAtStart, stageEdits, cancelEdit, relatedPrimaryKey, editModalActive };
}
diff --git a/app/src/interfaces/many-to-many/use-preview.ts b/app/src/interfaces/many-to-many/use-preview.ts
index 9f88d23768..140a26046d 100644
--- a/app/src/interfaces/many-to-many/use-preview.ts
+++ b/app/src/interfaces/many-to-many/use-preview.ts
@@ -4,7 +4,7 @@ import { RelationInfo } from './use-relation';
import { useFieldsStore } from '@/stores/';
import { Field, Collection } from '@/types';
import api from '@/api';
-import { cloneDeep } from 'lodash';
+import { cloneDeep, get } from 'lodash';
export default function usePreview(
value: Ref<(string | number | Record)[] | null>,
@@ -25,14 +25,13 @@ export default function usePreview(
const error = ref(null);
function getRelatedFields(fields: string[]) {
- const { junctionRelation } = relation.value;
+ const { junctionField } = relation.value;
- return fields
- .map((field) => {
- const sections = field.split('.');
- if (junctionRelation === sections[0] && sections.length === 2) return sections[1];
- })
- .filter((i) => i);
+ return fields.reduce((acc: string[], field) => {
+ const sections = field.split('.');
+ if (junctionField === sections[0] && sections.length === 2) acc.push(sections[1]);
+ return acc;
+ }, []);
}
function getJunctionFields() {
@@ -48,12 +47,17 @@ export default function usePreview(
}
loading.value = true;
- const { junctionRelation, relationPkField, junctionPkField } = relation.value;
- if (junctionRelation === null) return;
+ const { junctionField, relationPkField, junctionPkField, relationCollection } = relation.value;
+ if (junctionField === null) return;
// Load the junction items so we have access to the id's in the related collection
const junctionItems = await loadRelatedIds();
- const relatedPrimaryKeys = junctionItems.map((junction) => junction[junctionRelation]);
+
+ const relatedPrimaryKeys = junctionItems.reduce((acc, junction) => {
+ const id = get(junction, junctionField);
+ if (id !== null) acc.push(id);
+ return acc;
+ }, []) as (string | number)[];
const filteredFields = [...(fields.value.length > 0 ? getRelatedFields(fields.value) : getDefaultFields())];
@@ -63,35 +67,36 @@ export default function usePreview(
let responseData: Record[] = [];
if (relatedPrimaryKeys.length > 0) {
- const endpoint = relation.value.relationCollection.startsWith('directus_')
- ? `/${relation.value.relationCollection.substring(9)}`
- : `/items/${relation.value.relationCollection}`;
-
- const response = await api.get(endpoint, {
- params: {
- fields: filteredFields,
- [`filter[${relationPkField}][_in]`]: relatedPrimaryKeys.join(','),
- },
- });
- responseData = response?.data.data as Record[];
+ responseData = await request(
+ relationCollection,
+ filteredFields,
+ relationPkField,
+ relatedPrimaryKeys
+ );
}
+ console.log('responseData1', responseData);
// Insert the related items into the junction items
- const existingItems = responseData.map((data) => {
- const id = data[relationPkField];
- const junction = junctionItems.find((junction) => junction[junctionRelation] === id);
- if (junction === undefined) return;
+ responseData = responseData.map((data) => {
+ const id = get(data, relationPkField);
+ const junction = junctionItems.find((junction) => junction[junctionField] === id);
+
+ if (junction === undefined || id === undefined) return;
const newJunction = cloneDeep(junction);
- newJunction[junctionRelation] = data;
+ newJunction[junctionField] = data;
return newJunction;
}) as Record[];
const updatedItems = getUpdatedItems();
const newItems = getNewItems();
+ console.log('updatedItems', updatedItems);
+ console.log('newItems', newItems);
+ console.log('responseData2', responseData);
+
// Replace existing items with it's updated counterparts
- const newVal = existingItems
+ responseData = responseData
.map((item) => {
const updatedItem = updatedItems.find(
(updated) => updated[junctionPkField] === item[junctionPkField]
@@ -100,7 +105,9 @@ export default function usePreview(
return item;
})
.concat(...newItems);
- items.value = newVal;
+
+ console.log('responseData3', responseData);
+ items.value = responseData;
} catch (err) {
error.value = err;
} finally {
@@ -111,33 +118,24 @@ export default function usePreview(
);
async function loadRelatedIds() {
- const { junctionPkField, junctionRelation, relationPkField } = relation.value;
+ const { junctionPkField, junctionField, relationPkField, junctionCollection } = relation.value;
try {
let data: Record[] = [];
const primaryKeys = getPrimaryKeys();
+ console.log('PKS', primaryKeys);
if (primaryKeys.length > 0) {
const filteredFields = getJunctionFields();
if (filteredFields.includes(junctionPkField) === false) filteredFields.push(junctionPkField);
- if (filteredFields.includes(junctionRelation) === false) filteredFields.push(junctionRelation);
+ if (filteredFields.includes(junctionField) === false) filteredFields.push(junctionField);
- const endpoint = relation.value.junctionCollection.startsWith('directus_')
- ? `/${relation.value.junctionCollection.substring(9)}`
- : `/items/${relation.value.junctionCollection}`;
-
- const response = await api.get(endpoint, {
- params: {
- fields: filteredFields,
- [`filter[${junctionPkField}][_in]`]: getPrimaryKeys().join(','),
- },
- });
- data = response?.data.data as Record[];
+ data = await request(junctionCollection, filteredFields, junctionPkField, primaryKeys);
}
const updatedItems = getUpdatedItems().map((item) => ({
- [junctionRelation]: item[junctionRelation][relationPkField],
+ [junctionField]: item[junctionField][relationPkField],
}));
// Add all items that already had the id of it's related item
@@ -148,16 +146,35 @@ export default function usePreview(
return [];
}
+ async function request(
+ collection: string,
+ fields: string[] | null,
+ filteredField: string,
+ primaryKeys: (string | number)[] | null
+ ) {
+ if (fields === null || fields.length === 0 || primaryKeys === null || primaryKeys.length === 0) return [];
+
+ const endpoint = collection.startsWith('directus_') ? `/${collection.substring(9)}` : `/items/${collection}`;
+
+ const response = await api.get(endpoint, {
+ params: {
+ fields: fields,
+ [`filter[${filteredField}][_in]`]: primaryKeys.join(','),
+ },
+ });
+ return response?.data.data as Record[];
+ }
+
// Seeing we don't care about saving those tableHeaders, we can reset it whenever the
// fields prop changes (most likely when we're navigating to a different o2m context)
watch(
() => fields.value,
() => {
- const { junctionRelation, junctionCollection } = relation.value;
+ const { junctionField, junctionCollection } = relation.value;
tableHeaders.value = (fields.value.length > 0
? fields.value
- : getDefaultFields().map((field) => `${junctionRelation}.${field}`)
+ : getDefaultFields().map((field) => `${junctionField}.${field}`)
)
.map((fieldKey) => {
let field = fieldsStore.getField(junctionCollection, fieldKey);
diff --git a/app/src/interfaces/many-to-many/use-relation.ts b/app/src/interfaces/many-to-many/use-relation.ts
index 9b16c4b02d..c2f679aee2 100644
--- a/app/src/interfaces/many-to-many/use-relation.ts
+++ b/app/src/interfaces/many-to-many/use-relation.ts
@@ -6,7 +6,7 @@ import { Relation } from '@/types';
export type RelationInfo = {
junctionPkField: string;
relationPkField: string;
- junctionRelation: string;
+ junctionField: string;
junctionCollection: string;
relationCollection: string;
};
@@ -38,11 +38,11 @@ export default function useRelation(collection: Ref, field: Ref)
const { primaryKeyField: junctionPrimaryKeyField } = useCollection(junctionCollection.value.collection);
const { primaryKeyField: relationPrimaryKeyField } = useCollection(relationCollection.value.collection);
- const relationFields = computed(() => {
+ const relationInfo = computed(() => {
return {
junctionPkField: junctionPrimaryKeyField.value.field,
relationPkField: relationPrimaryKeyField.value.field,
- junctionRelation: junction.value.junction_field as string,
+ junctionField: junction.value.junction_field as string,
junctionCollection: junctionCollection.value.collection,
relationCollection: relationCollection.value.collection,
} as RelationInfo;
@@ -53,7 +53,7 @@ export default function useRelation(collection: Ref, field: Ref)
junctionCollection,
relation,
relationCollection,
- relationFields,
+ relationInfo,
junctionPrimaryKeyField,
relationPrimaryKeyField,
};
diff --git a/app/src/interfaces/many-to-many/use-selection.ts b/app/src/interfaces/many-to-many/use-selection.ts
index 1eff19c858..19f2330dd3 100644
--- a/app/src/interfaces/many-to-many/use-selection.ts
+++ b/app/src/interfaces/many-to-many/use-selection.ts
@@ -14,11 +14,13 @@ export default function useSelection(
const selectedPrimaryKeys = computed(() => {
if (items.value === null) return [];
- const { relationPkField, junctionRelation } = relation.value;
+ const { relationPkField, junctionField } = relation.value;
- const selectedKeys: (number | string)[] = items.value
- .map((currentItem) => get(currentItem, [junctionRelation, relationPkField]))
- .filter((i) => i);
+ const selectedKeys = items.value.reduce((acc, current) => {
+ const key = get(current, [junctionField, relationPkField]);
+ if (key !== undefined) acc.push(key);
+ return acc;
+ }, []) as (number | string)[];
return selectedKeys;
});
@@ -40,7 +42,12 @@ export default function useSelection(
});
function stageSelection(newSelection: (number | string)[]) {
- const selection = newSelection.filter((item) => selectedPrimaryKeys.value.includes(item) === false);
+ const { junctionField } = relation.value;
+
+ const selection = newSelection.reduce((acc, item) => {
+ if (selectedPrimaryKeys.value.includes(item) === false) acc.push({ [junctionField]: item });
+ return acc;
+ }, new Array());
const newVal = [...selection, ...(value.value || [])];
if (newVal.length === 0) emit(null);