diff --git a/app/src/interfaces/translations/index.ts b/app/src/interfaces/translations/index.ts index 34c66940ec..1bcedfa8ac 100644 --- a/app/src/interfaces/translations/index.ts +++ b/app/src/interfaces/translations/index.ts @@ -6,6 +6,7 @@ export default defineInterface(({ i18n }) => ({ name: i18n.t('translations'), icon: 'replay', types: ['alias'], + relationship: 'translations', component: InterfaceTranslations, options: [ /** @todo add custom options component */ diff --git a/app/src/interfaces/types.ts b/app/src/interfaces/types.ts index 73b154cf3e..05352bd95a 100644 --- a/app/src/interfaces/types.ts +++ b/app/src/interfaces/types.ts @@ -10,7 +10,7 @@ export type InterfaceConfig = { component: Component; options: DeepPartial[] | Component; types: typeof types[number][]; - relationship?: null | 'm2o' | 'o2m' | 'm2m'; + relationship?: null | 'm2o' | 'o2m' | 'm2m' | 'translations'; hideLabel?: boolean; hideLoader?: boolean; system?: boolean; diff --git a/app/src/lang/en-US/index.json b/app/src/lang/en-US/index.json index a611f8862b..81b4014669 100644 --- a/app/src/lang/en-US/index.json +++ b/app/src/lang/en-US/index.json @@ -153,6 +153,8 @@ "field_m2o": "M2O Relationship", "field_o2m": "O2M Relationship", "field_m2m": "M2M Relationship", + "field_translations": "Translations", + "languages": "Languages", "reset_page_preferences": "Reset Page Preferences", @@ -185,6 +187,8 @@ "configure_m2o": "Configure your Many-to-One Relationship...", "configure_o2m": "Configure your One-to-Many Relationship...", "configure_m2m": "Configure your Many-to-Many Relationship...", + "configure_translations": "Configure your Translations...", + "configure_languages": "Configure your Languages...", "add_m2o_to_collection": "Add Many-to-One to \"{collection}\"", "add_o2m_to_collection": "Add One-to-Many to \"{collection}\"", @@ -297,6 +301,8 @@ "item_in": "Item {primaryKey} in {collection} | Items {primaryKey} in {collection}", "this_collection": "This Collection", "related_collection": "Related Collection", + "translations_collection": "Translations Collection", + "languages_collection": "Languages Collection", "related_values": "Related Values", diff --git a/app/src/modules/settings/routes/data-model/field-detail/components/languages.vue b/app/src/modules/settings/routes/data-model/field-detail/components/languages.vue new file mode 100644 index 0000000000..8868aef564 --- /dev/null +++ b/app/src/modules/settings/routes/data-model/field-detail/components/languages.vue @@ -0,0 +1,145 @@ + + + + + diff --git a/app/src/modules/settings/routes/data-model/field-detail/components/schema.vue b/app/src/modules/settings/routes/data-model/field-detail/components/schema.vue index 6f555f031f..a8f1752f97 100644 --- a/app/src/modules/settings/routes/data-model/field-detail/components/schema.vue +++ b/app/src/modules/settings/routes/data-model/field-detail/components/schema.vue @@ -39,7 +39,6 @@ -
{{ $t('default_value') }}
{ - return ['file', 'files', 'o2m', 'm2m', 'm2o'].includes(props.type); + return ['file', 'files', 'o2m', 'm2m', 'm2o', 'translations'].includes(props.type); }); const typePlaceholder = computed(() => { diff --git a/app/src/modules/settings/routes/data-model/field-detail/components/translations.vue b/app/src/modules/settings/routes/data-model/field-detail/components/translations.vue new file mode 100644 index 0000000000..9b42843aa8 --- /dev/null +++ b/app/src/modules/settings/routes/data-model/field-detail/components/translations.vue @@ -0,0 +1,227 @@ + + + + + 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 7a854cb132..2c52037c59 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 @@ -27,6 +27,20 @@ :type="localType" /> + + + + , + type: String as PropType<'standard' | 'file' | 'files' | 'm2o' | 'o2m' | 'm2m' | 'presentation' | 'translations'>, default: null, }, }, @@ -116,7 +134,7 @@ export default defineComponent({ const localType = computed(() => { if (props.field === '+') return props.type; - let type: 'standard' | 'file' | 'files' | 'o2m' | 'm2m' | 'm2o' | 'presentation' = 'standard'; + let type: 'standard' | 'file' | 'files' | 'o2m' | 'm2m' | 'm2o' | 'presentation' | 'translations' = 'standard'; type = getLocalTypeForField(props.collection, props.field); return type; @@ -173,6 +191,21 @@ export default defineComponent({ }); } + if (localType.value === 'translations') { + tabs.splice(1, 0, ...[ + { + text: i18n.t('translations'), + value: 'translations', + disabled: translationsDisabled(), + }, + { + text: i18n.t('languages'), + value: 'languages', + disabled: languagesDisabled(), + } + ]) + } + return tabs; }); @@ -181,10 +214,20 @@ export default defineComponent({ return { tabs, currentTab }; function relationshipDisabled() { - return ( - isEmpty(state.fieldData.field) || - (['o2m', 'm2m', 'files', 'm2o'].includes(localType.value) === false && - isEmpty(state.fieldData.type)) + return isEmpty(state.fieldData.field); + } + + function translationsDisabled() { + return isEmpty(state.fieldData.field); + } + + function languagesDisabled() { + return isEmpty(state.fieldData.field) || ( + 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_primary) ); } @@ -199,7 +242,7 @@ export default defineComponent({ ); } - if (['m2m', 'files'].includes(localType.value)) { + if (['m2m', 'files', 'translations'].includes(localType.value)) { return ( state.relations.length !== 2 || isEmpty(state.relations[0].many_collection) || @@ -294,11 +337,14 @@ export default defineComponent({ function getLocalTypeForField( collection: string, field: string - ): 'standard' | 'file' | 'files' | 'o2m' | 'm2m' | 'm2o' { + ): 'standard' | 'file' | 'files' | 'o2m' | 'm2m' | 'm2o' | 'presentation' | 'translations' { const fieldInfo = fieldsStore.getField(collection, field); const relations = relationsStore.getRelationsForField(collection, field); - if (relations.length === 0) return 'standard'; + if (relations.length === 0) { + if (fieldInfo.type === 'alias') return 'presentation'; + return 'standard'; + } if (relations.length === 1) { const relation = relations[0]; 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 1d1e3109a4..f3e1530ad2 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 @@ -27,7 +27,7 @@ export { state, availableInterfaces, availableDisplays, initLocalStore, clearLoc function initLocalStore( collection: string, field: string, - type: 'standard' | 'file' | 'files' | 'm2o' | 'o2m' | 'm2m' | 'presentation' + type: 'standard' | 'file' | 'files' | 'm2o' | 'o2m' | 'm2m' | 'presentation' | 'translations' ) { const interfaces = getInterfaces(); const displays = getDisplays(); @@ -75,6 +75,8 @@ function initLocalStore( matchesRelation = inter.relationship === 'm2o'; } else if (type === 'files') { matchesRelation = inter.relationship === 'm2m'; + } else if (type === 'translations') { + matchesRelation = inter.relationship === 'translations'; } else { matchesRelation = inter.relationship === type; } @@ -277,8 +279,6 @@ function initLocalStore( } ] } - - console.log(state.newFields); }, 50); if (!isExisting) { @@ -321,7 +321,7 @@ function initLocalStore( ) } - if (type === 'm2m' || type === 'files') { + if (type === 'm2m' || type === 'files' || type === 'translations') { delete state.fieldData.schema; delete state.fieldData.type; @@ -424,26 +424,6 @@ function initLocalStore( ]; } - watch( - () => state.fieldData.field, - () => { - state.relations[0].one_field = state.fieldData.field; - - if (collectionExists(state.fieldData.field)) { - state.relations[0].many_collection = `${state.relations[0].one_collection}_${state.relations[1].one_collection}`; - state.relations[0].many_field = `${state.relations[0].one_collection}_${state.relations[0].one_primary}`; - state.relations[1].one_collection = state.fieldData.field; - state.relations[1].one_primary = fieldsStore.getPrimaryKeyFieldForCollection(collection)?.field; - state.relations[1].many_collection = `${state.relations[0].one_collection}_${state.relations[1].one_collection}`; - state.relations[1].many_field = `${state.relations[1].one_collection}_${state.relations[1].one_primary}`; - - if (state.relations[0].many_field === state.relations[1].many_field) { - state.relations[1].many_field = `${state.relations[1].one_collection}_related_${state.relations[1].one_primary}`; - } - } - } - ); - watch( () => state.relations[0].many_collection, () => { @@ -490,34 +470,75 @@ function initLocalStore( syncNewCollectionsM2M ) - let stop: WatchStopHandle; + watch( + () => state.fieldData.field, + () => { + state.relations[0].one_field = state.fieldData.field; - watch(() => state.autoFillJunctionRelation, (startWatching) => { - if (startWatching) { - stop = watch([() => state.relations[1].one_collection, () => state.relations[1].one_primary], ([newRelatedCollection, newRelatedPrimary]: string[]) => { - if (newRelatedCollection) { - state.relations[0].many_collection = `${state.relations[0].one_collection}_${state.relations[1].one_collection}`; - state.relations[1].many_collection = `${state.relations[0].one_collection}_${state.relations[1].one_collection}`; - state.relations[0].many_field = `${state.relations[0].one_collection}_${state.relations[0].one_primary}`; - } - - if (newRelatedPrimary) { - state.relations[1].many_field = `${state.relations[1].one_collection}_${state.relations[1].one_primary}`; - } + if (collectionExists(state.fieldData.field) && type !== 'translations') { + state.relations[0].many_collection = `${state.relations[0].one_collection}_${state.relations[1].one_collection}`; + state.relations[0].many_field = `${state.relations[0].one_collection}_${state.relations[0].one_primary}`; + state.relations[1].one_collection = state.fieldData.field; + state.relations[1].one_primary = fieldsStore.getPrimaryKeyFieldForCollection(collection)?.field; + state.relations[1].many_collection = `${state.relations[0].one_collection}_${state.relations[1].one_collection}`; + state.relations[1].many_field = `${state.relations[1].one_collection}_${state.relations[1].one_primary}`; if (state.relations[0].many_field === state.relations[1].many_field) { state.relations[1].many_field = `${state.relations[1].one_collection}_related_${state.relations[1].one_primary}`; } - }); - } else { - stop?.(); + } } - }, { immediate: true }); + ); + + if (type !== 'translations') { + let stop: WatchStopHandle; + + watch(() => state.autoFillJunctionRelation, (startWatching) => { + if (startWatching) { + stop = watch([() => state.relations[1].one_collection, () => state.relations[1].one_primary], ([newRelatedCollection, newRelatedPrimary]: string[]) => { + if (newRelatedCollection) { + state.relations[0].many_collection = `${state.relations[0].one_collection}_${state.relations[1].one_collection}`; + state.relations[1].many_collection = `${state.relations[0].one_collection}_${state.relations[1].one_collection}`; + state.relations[0].many_field = `${state.relations[0].one_collection}_${state.relations[0].one_primary}`; + } + + if (newRelatedPrimary) { + state.relations[1].many_field = `${state.relations[1].one_collection}_${state.relations[1].one_primary}`; + } + + if (state.relations[0].many_field === state.relations[1].many_field) { + state.relations[1].many_field = `${state.relations[1].one_collection}_related_${state.relations[1].one_primary}`; + } + }); + } else { + stop?.(); + } + }, { immediate: true }); + } + + if (type === 'translations') { + watch(() => state.relations[0].many_collection, (newManyCollection: string) => { + state.relations[1].many_collection = newManyCollection; + }, { immediate: true }); + + state.relations[0].many_collection = `${collection}_translations`; + state.relations[0].many_field = `${collection}_${fieldsStore.getPrimaryKeyFieldForCollection(collection)?.field}`; + state.relations[1].one_collection = 'languages'; + + if (collectionExists('languages')) { + state.relations[1].one_primary = fieldsStore.getPrimaryKeyFieldForCollection('languages')?.field; + } else { + state.relations[1].one_primary = 'id'; + } + + state.relations[1].many_field = `${state.relations[1].one_collection}_${state.relations[1].one_primary}`; + } } if (type === 'presentation') { delete state.fieldData.schema; delete state.fieldData.type; + state.fieldData.meta.special = 'alias'; } diff --git a/app/src/modules/settings/routes/data-model/fields/components/fields-management.vue b/app/src/modules/settings/routes/data-model/fields/components/fields-management.vue index d4d434c6e3..971e47c2bb 100644 --- a/app/src/modules/settings/routes/data-model/fields/components/fields-management.vue +++ b/app/src/modules/settings/routes/data-model/fields/components/fields-management.vue @@ -131,6 +131,14 @@ export default defineComponent({ icon: 'import_export', text: i18n.t('m2m_relationship'), }, + { + divider: true, + }, + { + type: 'translations', + icon: 'translate', + text: i18n.t('translations') + } ]); return {