Files
Inventory/docs/superpowers/plans/2026-04-02-machine-context-fields-frontend.md
2026-04-03 09:25:07 +02:00

13 KiB

Machine Context Custom Fields — Frontend Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking. Parallel plan: This is the frontend half. The backend plan is at 2026-04-02-machine-context-fields-backend.md. Both can run in parallel on separate worktrees — they share no files. Frontend tests requiring the API will need the backend done first.

Goal: Add machineContextOnly toggle in structure editors, filter context fields from standalone pages, and display/edit them in the machine detail view.

Architecture: Structure editors get a checkbox per field. The machine-detail transform propagates contextCustomFields/contextCustomFieldValues from the API link response onto the component/piece objects. Standalone entity views filter these out. Machine view displays them in a separate "Champs contextuels" section using the existing CustomFieldDisplay component, saving via upsert with the link ID.

Tech Stack: Nuxt 4, Vue 3 Composition API, TypeScript, DaisyUI 5

Spec: docs/superpowers/specs/2026-04-02-machine-context-custom-fields-design.md


File Map

Modify

  • frontend/app/shared/types/inventory.ts — add machineContextOnly to custom field types
  • frontend/app/components/PieceModelStructureEditor.vue — add checkbox toggle
  • frontend/app/composables/usePieceStructureEditorLogic.ts — add default in createEmptyField()
  • frontend/app/components/StructureNodeEditor.vue — add checkbox toggle
  • frontend/app/composables/useStructureNodeCrud.ts — add default in addCustomField()
  • frontend/app/composables/useEntityCustomFields.ts — filter out machineContextOnly fields
  • frontend/app/composables/useMachineDetailCustomFields.ts — propagate context fields, filter from normal merge
  • frontend/app/components/ComponentItem.vue — display context custom fields section
  • frontend/app/components/PieceItem.vue — display context custom fields section

Task 1: Types — add machineContextOnly

Files:

  • Modify: frontend/app/shared/types/inventory.ts

  • Step 1: Add machineContextOnly to ComponentModelCustomField

In the ComponentModelCustomField interface (around line 14), add:

machineContextOnly?: boolean
  • Step 2: Add machineContextOnly to PieceModelCustomField

In the PieceModelCustomField interface (around line 65), add:

machineContextOnly?: boolean
  • Step 3: Commit
cd frontend && git add app/shared/types/inventory.ts
git commit -m "feat(custom-fields) : add machineContextOnly to custom field types"

Task 2: Structure editors — add toggle

Files:

  • Modify: frontend/app/components/PieceModelStructureEditor.vue:122-125

  • Modify: frontend/app/composables/usePieceStructureEditorLogic.ts:283-290

  • Modify: frontend/app/components/StructureNodeEditor.vue:121-125

  • Modify: frontend/app/composables/useStructureNodeCrud.ts:49-62

  • Step 1: Add toggle in PieceModelStructureEditor.vue

After the "Obligatoire" checkbox block (line 125, after </div> closing the required checkbox), add:

              <div class="flex items-center gap-2 text-xs">
                <input v-model="field.machineContextOnly" type="checkbox" class="checkbox checkbox-xs">
                Contexte machine uniquement
              </div>
  • Step 2: Update usePieceStructureEditorLogic.ts — 3 functions

a) createEmptyField (line 283) — add machineContextOnly: false to the returned object:

const createEmptyField = (orderIndex: number): EditorField => ({
    uid: createUid('field'),
    name: '',
    type: 'text',
    required: false,
    optionsText: '',
    machineContextOnly: false,
    orderIndex,
})

b) toEditorField (line 78-91) — add machineContextOnly to the returned object, after the orderIndex line (line 90):

machineContextOnly: Boolean(input?.machineContextOnly),

c) buildPayload (line 160-165) — add machineContextOnly to the payload object after orderIndex (line 164):

machineContextOnly: Boolean(field.machineContextOnly),
  • Step 3: Add toggle in StructureNodeEditor.vue

After the "Obligatoire" checkbox closing </div> (line 123) and before the <textarea> for select options (line 124), add:

                <div class="flex items-center gap-2 text-xs">
                  <input v-model="field.machineContextOnly" type="checkbox" class="checkbox checkbox-xs">
                  Contexte machine uniquement
                </div>
  • Step 4: Update addCustomField in useStructureNodeCrud.ts

In addCustomField (line 49), add machineContextOnly: false to the pushed object (line 53-60):

fields.push({
    name: '',
    type: 'text',
    required: false,
    optionsText: '',
    options: [],
    machineContextOnly: false,
    orderIndex: nextIndex,
})
  • Step 5: Run lint + typecheck
cd frontend && npm run lint:fix && npx nuxi typecheck

Expected: 0 errors.

  • Step 6: Commit
cd frontend && git add .
git commit -m "feat(custom-fields) : add machineContextOnly toggle in structure editors"

Task 3: Filter context fields on standalone pages + machine-detail transform

Files:

  • Modify: frontend/app/composables/useEntityCustomFields.ts:42-49

  • Modify: frontend/app/composables/useMachineDetailCustomFields.ts:141-154,241-256

  • Step 1: Filter machineContextOnly from displayedCustomFields in useEntityCustomFields.ts

Update the displayedCustomFields computed (line 42):

const displayedCustomFields = computed(() =>
  dedupeMergedFields(
    mergeFieldDefinitionsWithValues(
      definitionSources.value,
      entity().customFieldValues,
    ),
  ).filter((field: any) => !field.machineContextOnly && !field.customField?.machineContextOnly),
)
  • Step 2: Filter machineContextOnly from normal customFields in machine-detail transform and propagate context data

In frontend/app/composables/useMachineDetailCustomFields.ts:

For pieces — In transformCustomFields (line 70), the returned object is built at line 141. Replace the customFields, line (line 143) with:

customFields: customFields.filter((f: AnyRecord) => !f.machineContextOnly && !f.customField?.machineContextOnly),
contextCustomFields: piece.contextCustomFields ?? [],
contextCustomFieldValues: piece.contextCustomFieldValues ?? [],

For components — In transformComponentCustomFields (line 158), the returned object is built at line 241. Replace the customFields, line (line 243) with:

customFields: customFields.filter((f: AnyRecord) => !f.machineContextOnly && !f.customField?.machineContextOnly),
contextCustomFields: component.contextCustomFields ?? [],
contextCustomFieldValues: component.contextCustomFieldValues ?? [],
  • Step 3: Run lint + typecheck
cd frontend && npm run lint:fix && npx nuxi typecheck

Expected: 0 errors.

  • Step 4: Commit
cd frontend && git add .
git commit -m "feat(custom-fields) : filter machineContextOnly from standalone and machine-detail views"

Task 4: Display context fields in machine view — ComponentItem.vue

Files:

  • Modify: frontend/app/components/ComponentItem.vue

Context fields are on the component object (set by the transform in Task 3), not as separate props.

  • Step 1: Add template section

After the existing CustomFieldDisplay block (line 195), add:

        <!-- Context custom fields (machine-specific) -->
        <div v-if="mergedContextFields.length" class="mt-4">
          <h4 class="text-xs font-semibold text-base-content/70 mb-2">
            Champs contextuels
          </h4>
          <CustomFieldDisplay
            :fields="mergedContextFields"
            :is-edit-mode="isEditMode"
            :columns="2"
            @field-blur="updateContextCustomField"
          />
        </div>
  • Step 2: Add imports and script logic

IMPORTANT: ComponentItem.vue uses <script setup> WITHOUT lang="ts". Do NOT use TypeScript annotations (: any, : string, etc.) in any code added to this file.

Add these imports (they are NOT already present in the component):

import { mergeFieldDefinitionsWithValues, dedupeMergedFields } from '~/shared/utils/entityCustomFieldLogic'
import { useCustomFields } from '~/composables/useCustomFields'
import { useToast } from '~/composables/useToast'

Add after the existing useEntityCustomFields block (around line 348):

const { upsertCustomFieldValue } = useCustomFields()
const { showSuccess, showError } = useToast()

const mergedContextFields = computed(() => {
  const definitions = props.component?.contextCustomFields ?? []
  const values = props.component?.contextCustomFieldValues ?? []
  if (!definitions.length && !values.length) return []
  return dedupeMergedFields(
    mergeFieldDefinitionsWithValues(definitions, values),
  )
})

const updateContextCustomField = async (field) => {
  const linkId = props.component?.linkId
  if (!linkId || !field) return

  const customFieldId = field.customFieldId || field.customField?.id
  if (!customFieldId) return

  const result = await upsertCustomFieldValue(
    customFieldId,
    'machineComponentLink',
    linkId,
    field.value ?? '',
  )

  if (result.success) {
    showSuccess(`Champ contextuel "${field.name || field.customField?.name}" mis à jour`)
  } else {
    showError(`Erreur lors de la mise à jour du champ contextuel`)
  }
}
  • Step 3: Run lint + typecheck
cd frontend && npm run lint:fix && npx nuxi typecheck
  • Step 4: Commit
cd frontend && git add app/components/ComponentItem.vue
git commit -m "feat(custom-fields) : display context custom fields in ComponentItem"

Task 5: Display context fields in machine view — PieceItem.vue

Files:

  • Modify: frontend/app/components/PieceItem.vue

  • Step 1: Add template section

After the existing CustomFieldDisplay block (line 236), add:

        <!-- Context custom fields (machine-specific) -->
        <div v-if="mergedContextFields.length" class="mt-4">
          <h4 class="text-xs font-semibold text-base-content/70 mb-2">
            Champs contextuels
          </h4>
          <CustomFieldDisplay
            :fields="mergedContextFields"
            :is-edit-mode="isEditMode"
            :columns="2"
            @field-blur="updateContextCustomField"
          />
        </div>
  • Step 2: Add imports and script logic

IMPORTANT: PieceItem.vue uses <script setup> WITHOUT lang="ts". Do NOT use TypeScript annotations in any code added to this file.

Add these imports (they are NOT already present in the component):

import { mergeFieldDefinitionsWithValues, dedupeMergedFields } from '~/shared/utils/entityCustomFieldLogic'
import { useCustomFields } from '~/composables/useCustomFields'
import { useToast } from '~/composables/useToast'

Add after the existing useEntityCustomFields block (around line 366):

const { upsertCustomFieldValue } = useCustomFields()
const { showSuccess, showError } = useToast()

const mergedContextFields = computed(() => {
  const definitions = props.piece?.contextCustomFields ?? []
  const values = props.piece?.contextCustomFieldValues ?? []
  if (!definitions.length && !values.length) return []
  return dedupeMergedFields(
    mergeFieldDefinitionsWithValues(definitions, values),
  )
})

const updateContextCustomField = async (field) => {
  const linkId = props.piece?.linkId
  if (!linkId || !field) return

  const customFieldId = field.customFieldId || field.customField?.id
  if (!customFieldId) return

  const result = await upsertCustomFieldValue(
    customFieldId,
    'machinePieceLink',
    linkId,
    field.value ?? '',
  )

  if (result.success) {
    showSuccess(`Champ contextuel "${field.name || field.customField?.name}" mis à jour`)
  } else {
    showError(`Erreur lors de la mise à jour du champ contextuel`)
  }
}
  • Step 3: Run lint + typecheck
cd frontend && npm run lint:fix && npx nuxi typecheck
  • Step 4: Commit
cd frontend && git add app/components/PieceItem.vue
git commit -m "feat(custom-fields) : display context custom fields in PieceItem"

Task 6: Frontend build verification

  • Step 1: Run full build
cd frontend && npm run build

Expected: Build succeeds with no errors.

  • Step 2: Final commit — update submodule pointer (from main repo)
cd /home/matthieu/dev_malio/Inventory
git add frontend
git commit -m "chore : update frontend submodule for context custom fields"