|
9 | 9 | :interface-type="'tags'" |
10 | 10 | :interface-options="interfaceOptions" |
11 | 11 | :is-editable="isFieldEditableComputed" |
| 12 | + :permission-denied="permissionDenied" |
12 | 13 | :is-relational="false" |
13 | 14 | :auto-save="false" |
14 | 15 | :saving="saving" |
|
55 | 56 | :interface-type="getInterfaceType() || undefined" |
56 | 57 | :interface-options="interfaceOptions" |
57 | 58 | :is-editable="isFieldEditableComputed" |
| 59 | + :permission-denied="permissionDenied" |
58 | 60 | :is-relational="false" |
59 | 61 | :auto-save="false" |
60 | 62 | :language-code-field="props.languageCodeField" |
@@ -166,10 +168,13 @@ import ColorCell from './CellRenderers/ColorCell.vue'; |
166 | 168 | import TagCell from './TagCell.vue'; |
167 | 169 | import { isFieldEditable, getFieldEditWarning, getFieldSupportLevel } from '../utils/fieldSupport'; |
168 | 170 | import { pickHeuristic } from '../utils/displayHeuristics'; |
| 171 | +import { resolveTranslationValue } from '../utils/resolveTranslationValue'; |
| 172 | +import { usePermissions } from '../composables/usePermissions'; |
169 | 173 |
|
170 | 174 | const { useFieldsStore, useRelationsStore } = useStores(); |
171 | 175 | const fieldsStore = useFieldsStore(); |
172 | 176 | const relationsStore = useRelationsStore(); |
| 177 | +const permissions = usePermissions(); |
173 | 178 |
|
174 | 179 | const props = defineProps<{ |
175 | 180 | item: Item; |
@@ -252,39 +257,20 @@ const displayValue = computed(() => { |
252 | 257 | return props.edits; |
253 | 258 | } |
254 | 259 |
|
255 | | - // Special handling for translations fields |
| 260 | + // Translation sub-fields go through the centralised helper so a sibling |
| 261 | + // re-render during a popover open cannot leak a whole translation row |
| 262 | + // through as "[object Object]". |
256 | 263 | if (actualFieldKey.value.includes('translations.')) { |
257 | | - const translationField = actualFieldKey.value.split('.').slice(1).join('.'); |
258 | | -
|
259 | | - // Check if translations exist and is an array |
260 | | - if (Array.isArray(props.item.translations) && props.item.translations.length > 0) { |
261 | | - // Use the language from field key (if specified) or the selected language |
262 | | - const targetLanguage = fieldLanguage.value; |
263 | | -
|
264 | | - if (targetLanguage) { |
265 | | - const languageField = props.languageCodeField || 'languages_code'; |
266 | | - const translation = props.item.translations.find( |
267 | | - (t: any) => t[languageField] === targetLanguage |
268 | | - ); |
269 | | -
|
270 | | - // Return the specific field value if translation exists |
271 | | - if (translation) { |
272 | | - return translation[translationField] || null; |
273 | | - } |
274 | | - } |
275 | | -
|
276 | | - // No translation for this language |
277 | | - return null; |
278 | | - } |
279 | | -
|
280 | | - // No translations available at all |
281 | | - return null; |
| 264 | + return resolveTranslationValue( |
| 265 | + props.item, |
| 266 | + actualFieldKey.value, |
| 267 | + fieldLanguage.value ?? null, |
| 268 | + props.languageCodeField || 'languages_code' |
| 269 | + ); |
282 | 270 | } |
283 | 271 |
|
284 | | - // Handle relational fields with display templates. |
285 | | - // Resolved priority: override → field-display → heuristic → none. |
286 | | - // Issue #48: layout-level override (columnDisplays) takes priority over the |
287 | | - // field-settings display. |
| 272 | + // Display-template resolution priority: column-display override → |
| 273 | + // field's own display template → relational heuristic → none. |
288 | 274 | const storageKey = props.fieldKey.includes(':') ? props.fieldKey.split(':')[0] : props.fieldKey; |
289 | 275 | const override = props.columnDisplays?.[storageKey]; |
290 | 276 | const fieldTemplate = |
@@ -425,16 +411,40 @@ const resolvedDisplay = computed<ResolvedDisplay>(() => { |
425 | 411 | const isFieldEditableComputed = computed(() => { |
426 | 412 | if (!props.editMode) return false; |
427 | 413 |
|
428 | | - // Special case: translation fields should be editable if the base type is supported |
| 414 | + // Permission check first — denies independent of field-support |
| 415 | + const collection = props.field?.collection || props.item?.collection; |
| 416 | + if (!collection) return false; |
| 417 | +
|
429 | 418 | if (actualFieldKey.value.startsWith('translations.')) { |
430 | | - // For now, allow editing of translation fields if edit mode is on |
431 | | - // The actual field support check will be done in the InlineEditPopover |
432 | | - return true; |
| 419 | + // Translation sub-field: resolve the junction collection and check update permission. |
| 420 | + // `collection` may already be the junction (when called from a translation cell whose |
| 421 | + // field metadata.collection points at the junction) or the parent collection. Try the |
| 422 | + // parent → junction lookup first; fall back to treating `collection` as the junction. |
| 423 | + const subField = actualFieldKey.value.split('.').slice(1).join('.'); |
| 424 | + const parentRels = relationsStore.getRelationsForField(collection, 'translations'); |
| 425 | + const transCollection = parentRels?.[0]?.collection || collection; |
| 426 | + if (!permissions.canUpdate(transCollection, subField)) return false; |
| 427 | + } else { |
| 428 | + if (!permissions.canUpdate(collection, actualFieldKey.value)) return false; |
433 | 429 | } |
434 | 430 |
|
435 | | - // Use the field support utility which already handles tags and other partial support fields |
436 | | - const editable = isFieldEditable(props.field, actualFieldKey.value); |
437 | | - return editable; |
| 431 | + // Field-support check (unchanged) |
| 432 | + if (actualFieldKey.value.startsWith('translations.')) return true; |
| 433 | + return isFieldEditable(props.field, actualFieldKey.value); |
| 434 | +}); |
| 435 | +
|
| 436 | +const permissionDenied = computed(() => { |
| 437 | + if (!props.editMode) return false; |
| 438 | + const collection = props.field?.collection || props.item?.collection; |
| 439 | + if (!collection) return false; |
| 440 | +
|
| 441 | + if (actualFieldKey.value.startsWith('translations.')) { |
| 442 | + const subField = actualFieldKey.value.split('.').slice(1).join('.'); |
| 443 | + const parentRels = relationsStore.getRelationsForField(collection, 'translations'); |
| 444 | + const transCollection = parentRels?.[0]?.collection || collection; |
| 445 | + return !permissions.canUpdate(transCollection, subField); |
| 446 | + } |
| 447 | + return !permissions.canUpdate(collection, actualFieldKey.value); |
438 | 448 | }); |
439 | 449 |
|
440 | 450 | // Get field edit warning message |
|
0 commit comments