diff --git a/api/src/controllers/fields.ts b/api/src/controllers/fields.ts index 53b573aa18..1dd0b697e7 100644 --- a/api/src/controllers/fields.ts +++ b/api/src/controllers/fields.ts @@ -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; diff --git a/api/src/middleware/error-handler.ts b/api/src/middleware/error-handler.ts index ee285a387c..e88c62a973 100644 --- a/api/src/middleware/error-handler.ts +++ b/api/src/middleware/error-handler.ts @@ -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); diff --git a/api/src/services/collections.ts b/api/src/services/collections.ts index 751be92fcf..40acbb023e 100644 --- a/api/src/services/collections.ts +++ b/api/src/services/collections.ts @@ -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); } } diff --git a/api/src/services/fields.ts b/api/src/services/fields.ts index 3cafb703f6..30c797f62c 100644 --- a/api/src/services/fields.ts +++ b/api/src/services/fields.ts @@ -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); diff --git a/app/src/components/v-list/v-list-item-content.vue b/app/src/components/v-list/v-list-item-content.vue index 39a97218a5..0ef58f843d 100644 --- a/app/src/components/v-list/v-list-item-content.vue +++ b/app/src/components/v-list/v-list-item-content.vue @@ -7,6 +7,7 @@ @@ -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 & { diff --git a/app/src/lang/en-US/index.json b/app/src/lang/en-US/index.json index 0a7743296c..61a233e728 100644 --- a/app/src/lang/en-US/index.json +++ b/app/src/lang/en-US/index.json @@ -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", 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..2fe8c1b777 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 @@ -4,44 +4,108 @@
{{ $t('this_collection') }}
- +
{{ $t('junction_collection') }}
- + + +
{{ $t('related_collection') }}
- + + +
- + + +
- - - - + + + + + +
@@ -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 }; }, }); @@ -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; 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') }}
- + + +
- - - + + + @@ -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/components/relationship-o2m.vue b/app/src/modules/settings/routes/data-model/field-detail/components/relationship-o2m.vue index 62de1f907c..9c3ad50f6b 100644 --- a/app/src/modules/settings/routes/data-model/field-detail/components/relationship-o2m.vue +++ b/app/src/modules/settings/routes/data-model/field-detail/components/relationship-o2m.vue @@ -8,21 +8,64 @@
{{ $t('related_collection') }}
- + > + +
- - + :disabled="isExisting" + :placeholder="$t('foreign_key')" + > + + + @@ -118,13 +161,16 @@ export default defineComponent({