mirror of
https://github.com/directus/directus.git
synced 2026-02-08 07:35:03 -05:00
Merge pull request #289 from directus/generate-related
Generate related
This commit is contained in:
@@ -18,7 +18,7 @@ router.get(
|
||||
|
||||
res.locals.payload = { data: fields || null };
|
||||
return next();
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
router.get(
|
||||
@@ -30,7 +30,7 @@ router.get(
|
||||
|
||||
res.locals.payload = { data: fields || null };
|
||||
return next();
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
router.get(
|
||||
@@ -46,7 +46,7 @@ router.get(
|
||||
|
||||
res.locals.payload = { data: field || null };
|
||||
return next();
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
const newFieldSchema = Joi.object({
|
||||
@@ -67,6 +67,9 @@ router.post(
|
||||
'/:collection',
|
||||
validateCollection,
|
||||
asyncHandler(async (req, res, next) => {
|
||||
if (!req.body.schema && !req.body.meta)
|
||||
throw new InvalidPayloadException(`"schema" or "meta" is required`);
|
||||
|
||||
const service = new FieldsService({ accountability: req.accountability });
|
||||
|
||||
const { error } = newFieldSchema.validate(req.body);
|
||||
@@ -83,7 +86,7 @@ router.post(
|
||||
|
||||
res.locals.payload = { data: createdField || null };
|
||||
return next();
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
router.patch(
|
||||
@@ -107,7 +110,7 @@ router.patch(
|
||||
|
||||
res.locals.payload = { data: results || null };
|
||||
return next();
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
router.patch(
|
||||
@@ -126,7 +129,7 @@ router.patch(
|
||||
|
||||
res.locals.payload = { data: updatedField || null };
|
||||
return next();
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
router.delete(
|
||||
@@ -136,7 +139,7 @@ router.delete(
|
||||
const service = new FieldsService({ accountability: req.accountability });
|
||||
await service.deleteField(req.params.collection, req.params.field);
|
||||
return next();
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -27,14 +27,14 @@ const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
|
||||
}
|
||||
|
||||
for (const err of errors) {
|
||||
if (err instanceof BaseException) {
|
||||
if (env.NODE_ENV === 'development') {
|
||||
err.extensions = {
|
||||
...(err.extensions || {}),
|
||||
stack: err.stack,
|
||||
};
|
||||
}
|
||||
if (env.NODE_ENV === 'development') {
|
||||
err.extensions = {
|
||||
...(err.extensions || {}),
|
||||
stack: err.stack,
|
||||
};
|
||||
}
|
||||
|
||||
if (err instanceof BaseException) {
|
||||
logger.debug(err);
|
||||
|
||||
res.status(err.status);
|
||||
|
||||
@@ -290,7 +290,7 @@ export default class CollectionsService {
|
||||
} else {
|
||||
await this.knex('directus_relations')
|
||||
.update({ one_field: null })
|
||||
.where({ one_collection: collection, field: relation.one_field });
|
||||
.where({ one_collection: collection, one_field: relation.one_field });
|
||||
await fieldsService.deleteField(relation.many_collection, relation.many_field);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +157,9 @@ export default class FieldsService {
|
||||
.where({ collection, field })
|
||||
.first();
|
||||
|
||||
fieldInfo = (await this.payloadService.processValues('read', fieldInfo)) as FieldMeta[];
|
||||
if (fieldInfo) {
|
||||
fieldInfo = (await this.payloadService.processValues('read', fieldInfo)) as FieldMeta[];
|
||||
}
|
||||
|
||||
try {
|
||||
column = await schemaInspector.columnInfo(collection, field);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<style>
|
||||
body {
|
||||
--v-list-item-content-padding: 9px 0;
|
||||
--v-list-item-content-font-family: var(--family-sans-serif);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -21,6 +22,7 @@ body {
|
||||
align-self: center;
|
||||
padding: var(--v-list-item-content-padding);
|
||||
overflow: hidden;
|
||||
font-family: var(--v-list-item-content-font-family);
|
||||
|
||||
.v-list.three-line &,
|
||||
.v-list-item.three-line & {
|
||||
|
||||
@@ -232,6 +232,7 @@
|
||||
"hide_on_browse": "Hide on Browse",
|
||||
"unique": "Unique",
|
||||
"primary_key": "Primary Key",
|
||||
"foreign_key": "Foreign Key",
|
||||
"validation_regex": "Validation RegEx",
|
||||
"validation_message": "Validation Message",
|
||||
"directus_type": "Directus Type",
|
||||
|
||||
@@ -4,44 +4,108 @@
|
||||
<div class="grid">
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('this_collection') }}</div>
|
||||
<v-input disabled :value="collection" />
|
||||
<v-input disabled :value="relations[0].one_collection" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('junction_collection') }}</div>
|
||||
<v-select
|
||||
:items="collectionItems"
|
||||
v-model="junctionCollection"
|
||||
:placeholder="$t('select_one')"
|
||||
:disabled="isExisting"
|
||||
/>
|
||||
<v-input v-model="junctionCollection" :placeholder="$t('collection')" :disabled="isExisting" db-safe>
|
||||
<template #append>
|
||||
<v-menu show-arrow placement="bottom-end">
|
||||
<template #activator="{ toggle }">
|
||||
<v-icon name="box" @click="toggle" v-tooltip="$t('select_existing')" />
|
||||
</template>
|
||||
|
||||
<v-list dense class="monospace">
|
||||
<v-list-item
|
||||
v-for="item in collectionItems"
|
||||
:key="item.value"
|
||||
:active="relations[0].many_collection === item.value"
|
||||
@click="relations[0].many_collection = item.value"
|
||||
>
|
||||
<v-list-item-content>
|
||||
{{ item.text }}
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-input>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('related_collection') }}</div>
|
||||
<v-select
|
||||
:disabled="type === 'files' || isExisting"
|
||||
:items="collectionItems"
|
||||
v-model="relations[1].one_collection"
|
||||
:placeholder="$t('select_one')"
|
||||
/>
|
||||
<v-input v-model="relations[1].one_collection" :placeholder="$t('collection')" :disabled="type === 'files' || isExisting" db-safe>
|
||||
<template #append>
|
||||
<v-menu show-arrow placement="bottom-end">
|
||||
<template #activator="{ toggle }">
|
||||
<v-icon name="box" @click="toggle" v-tooltip="$t('select_existing')" />
|
||||
</template>
|
||||
|
||||
<v-list dense class="monospace">
|
||||
<v-list-item
|
||||
v-for="item in collectionItems"
|
||||
:key="item.value"
|
||||
:active="relations[1].one_collection === item.value"
|
||||
@click="relations[1].one_collection = item.value"
|
||||
>
|
||||
<v-list-item-content>
|
||||
{{ item.text }}
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-input>
|
||||
</div>
|
||||
<v-input disabled :value="relations[0].one_primary" />
|
||||
<v-select
|
||||
:disabled="!junctionCollection || isExisting"
|
||||
:items="junctionFields"
|
||||
v-model="relations[0].many_field"
|
||||
:placeholder="$t('select_one')"
|
||||
/>
|
||||
<v-input v-model="relations[0].many_field" :placeholder="$t('foreign_key')" :disabled="isExisting" db-safe>
|
||||
<template #append v-if="junctionCollectionExists">
|
||||
<v-menu show-arrow placement="bottom-end">
|
||||
<template #activator="{ toggle }">
|
||||
<v-icon name="box" @click="toggle" v-tooltip="$t('select_existing')" />
|
||||
</template>
|
||||
|
||||
<v-list dense class="monospace">
|
||||
<v-list-item
|
||||
v-for="item in junctionFields"
|
||||
:key="item.value"
|
||||
:active="relations[0].many_field === item.value"
|
||||
@click="relations[0].many_field = item.value"
|
||||
>
|
||||
<v-list-item-content>
|
||||
{{ item.text }}
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-input>
|
||||
<div class="spacer" />
|
||||
<div class="spacer" />
|
||||
<v-select
|
||||
:disabled="!junctionCollection || isExisting"
|
||||
:items="junctionFields"
|
||||
v-model="relations[1].many_field"
|
||||
:placeholder="$t('select_one')"
|
||||
/>
|
||||
<v-input disabled :value="relations[1].one_primary" />
|
||||
<v-icon name="arrow_forward" />
|
||||
<v-icon name="arrow_backward" />
|
||||
<v-input v-model="relations[1].many_field" :placeholder="$t('foreign_key')" :disabled="isExisting" db-safe>
|
||||
<template #append v-if="junctionCollectionExists">
|
||||
<v-menu show-arrow placement="bottom-end">
|
||||
<template #activator="{ toggle }">
|
||||
<v-icon name="box" @click="toggle" v-tooltip="$t('select_existing')" />
|
||||
</template>
|
||||
|
||||
<v-list dense class="monospace">
|
||||
<v-list-item
|
||||
v-for="item in junctionFields"
|
||||
:key="item.value"
|
||||
:active="relations[1].many_field === item.value"
|
||||
@click="relations[1].many_field = item.value"
|
||||
>
|
||||
<v-list-item-content>
|
||||
{{ item.text }}
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-input>
|
||||
<v-input db-safe :disabled="relatedCollectionExists" v-model="relations[1].one_primary" :placeholder="$t('primary_key')" />
|
||||
<v-icon class="arrow" name="arrow_forward" />
|
||||
<v-icon class="arrow" name="arrow_backward" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -103,6 +167,14 @@ export default defineComponent({
|
||||
},
|
||||
});
|
||||
|
||||
const junctionCollectionExists = computed(() => {
|
||||
return collectionsStore.getCollection(junctionCollection.value) !== null;
|
||||
});
|
||||
|
||||
const relatedCollectionExists = computed(() => {
|
||||
return collectionsStore.getCollection(state.relations[1].one_collection) !== null;
|
||||
});
|
||||
|
||||
const junctionFields = computed(() => {
|
||||
if (!junctionCollection.value) return [];
|
||||
|
||||
@@ -111,12 +183,12 @@ 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,
|
||||
}));
|
||||
});
|
||||
|
||||
return { relations: state.relations, collectionItems, junctionCollection, junctionFields };
|
||||
return { relations: state.relations, collectionItems, junctionCollection, junctionFields, junctionCollectionExists, relatedCollectionExists };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -132,11 +204,12 @@ export default defineComponent({
|
||||
gap: 20px;
|
||||
margin-top: 48px;
|
||||
|
||||
.v-icon {
|
||||
.v-icon.arrow {
|
||||
--v-icon-color: var(--foreground-subdued);
|
||||
|
||||
position: absolute;
|
||||
transform: translateX(-50%);
|
||||
pointer-events: none;
|
||||
|
||||
&:first-of-type {
|
||||
bottom: 85px;
|
||||
|
||||
@@ -8,16 +8,32 @@
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('related_collection') }}</div>
|
||||
<v-select
|
||||
:placeholder="$t('select_one')"
|
||||
:items="items"
|
||||
v-model="relations[0].one_collection"
|
||||
:disabled="isExisting"
|
||||
/>
|
||||
<v-input db-safe key="related-collection" v-model="relations[0].one_collection" :disabled="isExisting" :placeholder="$t('collection')">
|
||||
<template #append>
|
||||
<v-menu show-arrow placement="bottom-end">
|
||||
<template #activator="{ toggle }">
|
||||
<v-icon name="box" @click="toggle" v-tooltip="$t('select_existing')" />
|
||||
</template>
|
||||
|
||||
<v-list dense class="monospace">
|
||||
<v-list-item
|
||||
v-for="item in items"
|
||||
:key="item.value"
|
||||
:active="relations[0].one_collection === item.value"
|
||||
@click="relations[0].one_collection = item.value"
|
||||
>
|
||||
<v-list-item-content>
|
||||
{{ item.text }}
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-input>
|
||||
</div>
|
||||
<v-input disabled :value="fieldData.field" />
|
||||
<v-input disabled :value="relatedPrimary" />
|
||||
<v-icon name="arrow_back" />
|
||||
<v-input disabled :value="relations[0].many_field" />
|
||||
<v-input db-safe :disabled="isNewCollection === false" v-model="relations[0].one_primary" :placeholder="$t('primary_key')" />
|
||||
<v-icon class="arrow" name="arrow_back" />
|
||||
</div>
|
||||
|
||||
<v-divider v-if="!isExisting" />
|
||||
@@ -31,7 +47,7 @@
|
||||
<div class="type-label">{{ $t('corresponding_field_name') }}</div>
|
||||
<v-input :disabled="hasCorresponding === false" v-model="correspondingField" db-safe />
|
||||
</div>
|
||||
<v-icon name="arrow_forward" />
|
||||
<v-icon name="arrow_forward" class="arrow" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -8,21 +8,64 @@
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="type-label">{{ $t('related_collection') }}</div>
|
||||
<v-select
|
||||
:placeholder="$t('select_one')"
|
||||
:items="items"
|
||||
v-model="collectionMany"
|
||||
<v-input
|
||||
db-safe
|
||||
:placeholder="$t('collection')"
|
||||
v-model="relations[0].many_collection"
|
||||
:disabled="isExisting"
|
||||
/>
|
||||
>
|
||||
<template #append>
|
||||
<v-menu show-arrow placement="bottom-end">
|
||||
<template #activator="{ toggle }">
|
||||
<v-icon name="box" @click="toggle" v-tooltip="$t('select_existing')" />
|
||||
</template>
|
||||
|
||||
<v-list dense class="monospace">
|
||||
<v-list-item
|
||||
v-for="item in items"
|
||||
:key="item.value"
|
||||
:active="relations[0].many_collection === item.value"
|
||||
@click="relations[0].many_collection = item.value"
|
||||
>
|
||||
<v-list-item-content>
|
||||
{{ item.text }}
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-input>
|
||||
</div>
|
||||
<v-input disabled :value="currentCollectionPrimaryKey.field" />
|
||||
<v-select
|
||||
<v-input
|
||||
db-safe
|
||||
v-model="relations[0].many_field"
|
||||
:disabled="!relations[0].many_collection || isExisting"
|
||||
:items="fields"
|
||||
:placeholder="!relations[0].many_collection ? $t('select_one') : $t('select_one')"
|
||||
/>
|
||||
<v-icon name="arrow_forward" />
|
||||
:disabled="isExisting"
|
||||
:placeholder="$t('foreign_key')"
|
||||
>
|
||||
<template #append v-if="fields && fields.length > 0">
|
||||
<v-menu show-arrow placement="bottom-end">
|
||||
<template #activator="{ toggle }">
|
||||
<v-icon name="box" @click="toggle" v-tooltip="$t('select_existing')" />
|
||||
</template>
|
||||
|
||||
<v-list dense class="monospace">
|
||||
<v-list-item
|
||||
v-for="field in fields"
|
||||
:key="field.value"
|
||||
:active="relations[0].many_field === field.value"
|
||||
@click="relations[0].many_field = field.value"
|
||||
:disabled="field.disabled"
|
||||
>
|
||||
<v-list-item-content>
|
||||
{{ field.text }}
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-input>
|
||||
<v-icon class="arrow" name="arrow_forward" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -118,13 +161,16 @@ 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, minmax(0, 1fr));
|
||||
gap: 20px 32px;
|
||||
margin-top: 48px;
|
||||
|
||||
.v-icon {
|
||||
.v-icon.arrow {
|
||||
--v-icon-color: var(--foreground-subdued);
|
||||
|
||||
position: absolute;
|
||||
@@ -134,6 +180,10 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
.v-list {
|
||||
--v-list-item-content-font-family: var(--family-monospace);
|
||||
}
|
||||
|
||||
.type-label {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -202,7 +204,8 @@ export default defineComponent({
|
||||
isEmpty(state.relations[0].one_field) ||
|
||||
isEmpty(state.relations[1].many_collection) ||
|
||||
isEmpty(state.relations[1].many_field) ||
|
||||
isEmpty(state.relations[1].one_collection)
|
||||
isEmpty(state.relations[1].one_collection) ||
|
||||
isEmpty(state.relations[1].one_primary)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -217,6 +220,8 @@ export default defineComponent({
|
||||
async function saveField() {
|
||||
saving.value = true;
|
||||
|
||||
console.log(state);
|
||||
|
||||
try {
|
||||
if (props.field !== '+') {
|
||||
await api.patch(`/fields/${props.collection}/${props.field}`, state.fieldData);
|
||||
@@ -225,7 +230,15 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
state.newFields.map((newField: Partial<Field>) => {
|
||||
state.newCollections.map((newCollection: Partial<Collection> & { $type: string }) => {
|
||||
delete newCollection.$type;
|
||||
return api.post(`/collections`, newCollection);
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all(
|
||||
state.newFields.map((newField: Partial<Field> & { $type: string }) => {
|
||||
delete newField.$type;
|
||||
return api.post(`/fields/${newField.collection}`, newField);
|
||||
})
|
||||
);
|
||||
@@ -240,6 +253,7 @@ export default defineComponent({
|
||||
})
|
||||
);
|
||||
|
||||
await collectionsStore.hydrate();
|
||||
await fieldsStore.hydrate();
|
||||
await relationsStore.hydrate();
|
||||
|
||||
|
||||
@@ -5,16 +5,18 @@
|
||||
* 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';
|
||||
import { DisplayConfig } from '@/displays/types';
|
||||
import { Field } from '@/types';
|
||||
|
||||
const fieldsStore = useFieldsStore();
|
||||
const relationsStore = useRelationsStore();
|
||||
const collectionsStore = useCollectionsStore();
|
||||
|
||||
let state: any;
|
||||
let availableInterfaces: ComputedRef<InterfaceConfig[]>;
|
||||
@@ -51,6 +53,7 @@ function initLocalStore(
|
||||
},
|
||||
},
|
||||
relations: [],
|
||||
newCollections: [],
|
||||
newFields: [],
|
||||
});
|
||||
|
||||
@@ -141,7 +144,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: {
|
||||
hidden: true,
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
}, 50);
|
||||
|
||||
if (isExisting === false) {
|
||||
state.relations = [
|
||||
{
|
||||
many_collection: collection,
|
||||
@@ -165,9 +194,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,12 +213,65 @@ function initLocalStore(
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch([() => state.relations[0].one_collection, () => state.relations[0].one_primary], syncNewCollectionsM2O);
|
||||
}
|
||||
|
||||
if (type === 'o2m') {
|
||||
delete state.fieldData.schema;
|
||||
delete state.fieldData.type;
|
||||
|
||||
const syncNewCollectionsO2M = throttle(() => {
|
||||
const collectionName = state.relations[0].many_collection;
|
||||
const fieldName = state.relations[0].many_field;
|
||||
|
||||
if (collectionExists(collectionName)) {
|
||||
state.newCollections = [];
|
||||
} else {
|
||||
state.newCollections = [
|
||||
{
|
||||
collection: collectionName,
|
||||
fields: [
|
||||
{
|
||||
field: 'id',
|
||||
type: 'integer',
|
||||
schema: {
|
||||
has_auto_increment: true,
|
||||
},
|
||||
system: {
|
||||
hidden: true,
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
if (collectionExists(collectionName)) {
|
||||
if (fieldExists(collectionName, fieldName)) {
|
||||
state.newFields = [];
|
||||
} else {
|
||||
state.newFields = [
|
||||
{
|
||||
collection: collectionName,
|
||||
field: fieldName,
|
||||
type: fieldsStore.getPrimaryKeyFieldForCollection(collection)?.type,
|
||||
schema: {},
|
||||
}
|
||||
]
|
||||
}
|
||||
} else {
|
||||
state.newFields = [
|
||||
{
|
||||
collection: collectionName,
|
||||
field: fieldName,
|
||||
type: 'integer',
|
||||
schema: {},
|
||||
}
|
||||
]
|
||||
}
|
||||
}, 50);
|
||||
|
||||
if (!isExisting) {
|
||||
state.fieldData.meta.special = 'o2m';
|
||||
|
||||
@@ -212,17 +298,95 @@ function initLocalStore(
|
||||
watch(
|
||||
() => state.relations[0].many_collection,
|
||||
() => {
|
||||
state.relations[0].many_primary = fieldsStore.getPrimaryKeyFieldForCollection(
|
||||
state.relations[0].many_collection
|
||||
).field;
|
||||
if (collectionExists(state.relations[0].many_collection)) {
|
||||
state.relations[0].many_primary = fieldsStore.getPrimaryKeyFieldForCollection(
|
||||
state.relations[0].many_collection
|
||||
).field;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
[() => state.relations[0].many_collection, () => state.relations[0].many_field],
|
||||
syncNewCollectionsO2M
|
||||
)
|
||||
}
|
||||
|
||||
if (type === 'm2m' || type === 'files') {
|
||||
delete state.fieldData.schema;
|
||||
delete state.fieldData.type;
|
||||
|
||||
const syncNewCollectionsM2M = throttle(([junctionCollection, manyCurrent, manyRelated, relatedCollection]) => {
|
||||
state.newCollections = state.newCollections.filter((col: any) => ['junction', 'related'].includes(col.$type) === false);
|
||||
state.newFields = state.newFields.filter((field: Partial<Field> & { $type: string }) => ['manyCurrent', 'manyRelated'].includes(field.$type) === false);
|
||||
|
||||
if (collectionExists(junctionCollection) === false) {
|
||||
state.newCollections.push({
|
||||
$type: 'junction',
|
||||
collection: junctionCollection,
|
||||
fields: [
|
||||
{
|
||||
field: 'id',
|
||||
type: 'integer',
|
||||
schema: {
|
||||
has_auto_increment: true,
|
||||
},
|
||||
meta: {
|
||||
hidden: true,
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
if (fieldExists(junctionCollection, manyCurrent) === false) {
|
||||
state.newFields.push({
|
||||
$type: 'manyCurrent',
|
||||
collection: junctionCollection,
|
||||
field: manyCurrent,
|
||||
type: collectionExists(junctionCollection) ? fieldsStore.getPrimaryKeyFieldForCollection(junctionCollection)?.type : 'integer',
|
||||
schema: {},
|
||||
meta: {
|
||||
hidden: true,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (fieldExists(junctionCollection, manyRelated) === false) {
|
||||
state.newFields.push({
|
||||
$type: 'manyRelated',
|
||||
collection: junctionCollection,
|
||||
field: manyRelated,
|
||||
type: collectionExists(relatedCollection) ? fieldsStore.getPrimaryKeyFieldForCollection(relatedCollection)?.type : 'integer',
|
||||
schema: {},
|
||||
meta: {
|
||||
hidden: true,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (collectionExists(relatedCollection) === false) {
|
||||
state.newCollections.push({
|
||||
$type: 'related',
|
||||
collection: relatedCollection,
|
||||
fields: [
|
||||
{
|
||||
field: state.relations[1].one_primary,
|
||||
type: 'integer',
|
||||
schema: {
|
||||
has_auto_increment: true,
|
||||
},
|
||||
meta: {
|
||||
hidden: true,
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
console.log(state.newCollections, state.newFields);
|
||||
}, 50);
|
||||
|
||||
if (!isExisting) {
|
||||
state.fieldData.meta.special = 'm2m';
|
||||
|
||||
@@ -256,9 +420,11 @@ function initLocalStore(
|
||||
watch(
|
||||
() => state.relations[0].many_collection,
|
||||
() => {
|
||||
const pkField = fieldsStore.getPrimaryKeyFieldForCollection(state.relations[0].many_collection)?.field;
|
||||
state.relations[0].many_primary = pkField;
|
||||
state.relations[1].many_primary = pkField;
|
||||
if (collectionExists(state.relations[0].many_collection)) {
|
||||
const pkField = fieldsStore.getPrimaryKeyFieldForCollection(state.relations[0].many_collection)?.field;
|
||||
state.relations[0].many_primary = pkField;
|
||||
state.relations[1].many_primary = pkField;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -279,11 +445,23 @@ function initLocalStore(
|
||||
watch(
|
||||
() => state.relations[1].one_collection,
|
||||
() => {
|
||||
state.relations[1].one_primary = fieldsStore.getPrimaryKeyFieldForCollection(
|
||||
state.relations[1].one_collection
|
||||
)?.field;
|
||||
if (collectionExists(state.relations[1].one_collection)) {
|
||||
state.relations[1].one_primary = fieldsStore.getPrimaryKeyFieldForCollection(
|
||||
state.relations[1].one_collection
|
||||
)?.field;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
[
|
||||
() => state.relations[0].many_collection,
|
||||
() => state.relations[0].many_field,
|
||||
() => state.relations[1].many_field,
|
||||
() => state.relations[1].one_collection,
|
||||
],
|
||||
syncNewCollectionsM2M
|
||||
)
|
||||
}
|
||||
|
||||
if (type === 'presentation') {
|
||||
@@ -322,6 +500,14 @@ function initLocalStore(
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function collectionExists(collection: string) {
|
||||
return collectionsStore.getCollection(collection) !== null;
|
||||
}
|
||||
|
||||
function fieldExists(collection: string, field: string) {
|
||||
return collectionExists(collection) && fieldsStore.getField(collection, field) !== null;
|
||||
}
|
||||
}
|
||||
|
||||
function clearLocalStore() {
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
<v-list-item
|
||||
@click="deleteActive = true"
|
||||
class="danger"
|
||||
:disabled="field.schema && field.schema.is_primary_key === true"
|
||||
:disabled="field.schema && field.schema.is_primary_key === true || false"
|
||||
>
|
||||
<v-list-item-icon><v-icon name="delete" outline /></v-list-item-icon>
|
||||
<v-list-item-content>
|
||||
@@ -128,10 +128,10 @@
|
||||
|
||||
<v-dialog v-model="deleteActive">
|
||||
<v-card>
|
||||
<v-card-title>Are you sure you want to delete this field?</v-card-title>
|
||||
<v-card-title>{{ $t('delete_field_are_you_sure', { field: field.field }) }}</v-card-title>
|
||||
<v-card-actions>
|
||||
<v-button @click="deleteActive = false" secondary>Cancel</v-button>
|
||||
<v-button :loading="deleting" @click="deleteField">Delete</v-button>
|
||||
<v-button @click="deleteActive = false" secondary>{{ $t('cancel') }}</v-button>
|
||||
<v-button :loading="deleting" @click="deleteField">{{ $t('delete') }}</v-button>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<private-view :title="collectionInfo.name">
|
||||
<private-view :title="collectionInfo && collectionInfo.name">
|
||||
<template #headline>{{ $t('settings_data_model') }}</template>
|
||||
<template #title-outer:prepend>
|
||||
<v-button class="header-icon" rounded icon exact to="/settings/data-model">
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 475;
|
||||
z-index: 850;
|
||||
display: none;
|
||||
max-width: 260px;
|
||||
padding: 4px 8px;
|
||||
|
||||
Reference in New Issue
Block a user