Assets - Read assets from the asset table record view (de18263e-…), keyed by _pk_value; rows open by assetRowId and the list sorts newest first. - Update INIT fields to the builder: Asset Details (was Category), Asset State/Priority/Classification options, warranty fields, Brand, ID, Location, and the images field (field_53); Asset Owner is field_51. - Upload photos and an Invoice document before the activity runs, and send the returned file objects. The Invoice field id is a placeholder until the field exists in the builder. - Show every Add Asset field as a list and details column, with option labels, and group related columns into rich cells. - Use the INIT activity's detail view template (a573754f-…). - Drop Assigned by, Condition and Type from the reassign, return and lost forms; add Lost manager name options. Employees - Department is a select from DEPARTMENTS; more employee types; status removed from the forms; rich columns in the employee list. Shared - apiFetch sends FormData bodies as multipart. - Default workflow host is dev-apps.getzino.in. - displayLabel() for readable field labels; Raw response panels removed. - useRecordView accepts an initial sort. - Temporary dev proxy in vite.config.js for rv-screens lookups. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
60 lines
2.3 KiB
JavaScript
60 lines
2.3 KiB
JavaScript
import { normLabel } from './detailView'
|
|
|
|
/**
|
|
* Rich columns: several related record view columns shown as one cell — a
|
|
* bold title line and a smaller line under it (e.g. name over employee code).
|
|
*
|
|
* A group is
|
|
* { key, label, avatar?, title: [label…], subtitle: [label | { label, prefix }…] }
|
|
* where each label names a view column by its output_label or field_key
|
|
* ('first_name', 'Asset Brand'…, compared loosely). Title parts are joined
|
|
* with a space, subtitle parts with ' · '; `prefix` goes before a value
|
|
* ("Ends 12 Oct 2026"). `avatar` shows the title's initials.
|
|
*
|
|
* The member columns are replaced by the rich column, placed where its first
|
|
* member was. A group none of whose columns the view has is skipped; columns
|
|
* no group takes stay as they are. Rows are not changed — the cell reads each
|
|
* member's own value.
|
|
*/
|
|
export function withRichColumns(res, groups) {
|
|
const fields = res?.config?.fields ?? []
|
|
const matches = (col, label) =>
|
|
[col.output_label, col.field_key].some((l) => l && normLabel(l) === normLabel(label))
|
|
const taken = new Set()
|
|
const take = (spec) => {
|
|
const { label, prefix } = typeof spec === 'string' ? { label: spec } : spec
|
|
const col = fields.find((c) => !taken.has(c) && matches(c, label))
|
|
if (!col) return []
|
|
taken.add(col)
|
|
return [{ ...col, prefix }]
|
|
}
|
|
|
|
const richAt = new Map()
|
|
for (const group of groups) {
|
|
const title = group.title.flatMap(take)
|
|
const subtitle = (group.subtitle ?? []).flatMap(take)
|
|
const members = [...title, ...subtitle]
|
|
if (!members.length) continue
|
|
// Sorting by the column sorts by its first title field (or first member).
|
|
const sortBy = title[0] ?? members[0]
|
|
const first = fields.findIndex((c) => members.some((m) => m.field_key === c.field_key))
|
|
richAt.set(first, {
|
|
type: 'rich',
|
|
field_key: `rich_${group.key}`,
|
|
output_label: group.label,
|
|
data_type: 'rich',
|
|
avatar: Boolean(group.avatar),
|
|
title,
|
|
subtitle,
|
|
sort_key: sortBy.field_key,
|
|
sortable: sortBy.sortable !== false && sortBy.type !== 'derived',
|
|
})
|
|
}
|
|
|
|
const next = fields.flatMap((c, i) => {
|
|
if (richAt.has(i)) return [richAt.get(i)]
|
|
return taken.has(c) ? [] : [c]
|
|
})
|
|
return { ...res, config: { ...res?.config, fields: next } }
|
|
}
|