feat: workflow-backed asset lists with employee names and assigned-by
- Read assets, assignments and returns from their asset workflow record views - Show employee names instead of raw lookup ids in the activity lists, and fall back to the assignee's department when none was picked - Merge derived activity columns into the view's matching columns instead of showing them twice - Offer active employees for "Assigned by" and default it to the signed-in user - Edit activity uploads photos and pre-fills saved ones Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
d9f614857a
commit
1004ad4895
File diff suppressed because it is too large
Load Diff
@ -25,11 +25,25 @@ export const RECORD_VIEWS = {
|
|||||||
rowKey: '_pk_value',
|
rowKey: '_pk_value',
|
||||||
},
|
},
|
||||||
assets: {
|
assets: {
|
||||||
// Reads the asset table (rows keyed by id / _pk_value). It has no
|
// The "ddddd" view on the asset workflow: every row is an instance and
|
||||||
// instance_id or workflow state; the old workflow view c91be68b-… is gone.
|
// carries its instance_id, which Edit and the other activities need. Its
|
||||||
templateUid: 'de18263e-dc82-4d4d-98a2-99607104373b',
|
// columns are the "Asset details" (Edit) activity's fields (field_2,
|
||||||
screen: 'Asset_management',
|
// field_3, field_13…). It replaced the asset table view de18263e-….
|
||||||
rowKey: '_pk_value',
|
templateUid: 'cdba05a2-a993-4de0-9364-c6ea048bd775',
|
||||||
|
screen: 'ddddd',
|
||||||
|
rowKey: 'instance_id',
|
||||||
|
},
|
||||||
|
assetAssignments: {
|
||||||
|
// The asset workflow view the Assignment list reads; its rows carry the
|
||||||
|
// instance_id the ASSIGNED and Reassigned activities run on.
|
||||||
|
templateUid: '08438179-a28f-4d92-b340-97c3028cece2',
|
||||||
|
rowKey: 'instance_id',
|
||||||
|
},
|
||||||
|
assetReturns: {
|
||||||
|
// The asset workflow view the Return list reads; its rows carry the
|
||||||
|
// instance_id the RETURNED activity runs on.
|
||||||
|
templateUid: '45ea19d6-8729-438e-8c4e-488a9d1ffddb',
|
||||||
|
rowKey: 'instance_id',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { useRef } from 'react'
|
import { useRef } from 'react'
|
||||||
import { Field, SelectInput, TextArea, TextInput } from '../ui'
|
import { Field, SelectInput, TextArea, TextInput } from '../ui'
|
||||||
import { displayLabel } from '../../utils/format'
|
import { displayLabel } from '../../utils/format'
|
||||||
|
import { fileUrl } from './FieldValue'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders one Zino activity field per builder definition, matching its
|
* Renders one Zino activity field per builder definition, matching its
|
||||||
@ -232,11 +233,15 @@ function startUpload(file, field, upload, done) {
|
|||||||
return pending
|
return pending
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A photo's thumbnail with its remove button, dimmed while it uploads. */
|
/**
|
||||||
|
* A photo's thumbnail with its remove button, dimmed while it uploads. A photo
|
||||||
|
* already saved on the record (e.g. pre-filled on edit) has no local preview,
|
||||||
|
* so it shows from the stored file.
|
||||||
|
*/
|
||||||
function Thumb({ photo, alt, className, onRemove, disabled, removeLabel }) {
|
function Thumb({ photo, alt, className, onRemove, disabled, removeLabel }) {
|
||||||
return (
|
return (
|
||||||
<div className={`image-slot__thumb${photo.uploading ? ' image-slot__thumb--uploading' : ''}`}>
|
<div className={`image-slot__thumb${photo.uploading ? ' image-slot__thumb--uploading' : ''}`}>
|
||||||
<img className={className} src={photo.preview} alt={alt} />
|
<img className={className} src={photo.preview ?? fileUrl(photo)} alt={alt} />
|
||||||
{photo.uploading && <span className="image-slot__status">Uploading…</span>}
|
{photo.uploading && <span className="image-slot__status">Uploading…</span>}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@ -12,6 +12,13 @@ import {
|
|||||||
import { emptyValues, validateActivity } from '../../api/activityForm'
|
import { emptyValues, validateActivity } from '../../api/activityForm'
|
||||||
import { useAuth } from '../../store/AuthContext'
|
import { useAuth } from '../../store/AuthContext'
|
||||||
|
|
||||||
|
// Asset Owner sits after Condition, as in the builder.
|
||||||
|
const OWNER_AFTER = ASSET_FIELDS.findIndex((f) => f.id === 'field_47') + 1
|
||||||
|
const FIELDS_BEFORE_OWNER = ASSET_FIELDS.slice(0, OWNER_AFTER)
|
||||||
|
const FIELDS_AFTER_OWNER = ASSET_FIELDS.slice(OWNER_AFTER)
|
||||||
|
|
||||||
|
const upload = (fieldId, file) => uploadAssetFile(ASSET_INIT_ACTIVITY_ID, fieldId, file)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "Add Asset" dialog. Fields are rendered straight from the Zino builder
|
* "Add Asset" dialog. Fields are rendered straight from the Zino builder
|
||||||
* definition in api/assets.js, so the form always matches the activity.
|
* definition in api/assets.js, so the form always matches the activity.
|
||||||
@ -129,12 +136,12 @@ export default function AddAssetModal({ open, onClose, onCreated }) {
|
|||||||
|
|
||||||
<form id="add-asset-form" className="form__grid" onSubmit={handleSubmit} noValidate>
|
<form id="add-asset-form" className="form__grid" onSubmit={handleSubmit} noValidate>
|
||||||
<ActivityFields
|
<ActivityFields
|
||||||
fields={ASSET_FIELDS}
|
fields={FIELDS_BEFORE_OWNER}
|
||||||
values={values}
|
values={values}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
upload={(fieldId, file) => uploadAssetFile(ASSET_INIT_ACTIVITY_ID, fieldId, file)}
|
upload={upload}
|
||||||
/>
|
/>
|
||||||
<Field label="Asset Owner">
|
<Field label="Asset Owner">
|
||||||
{(id) => (
|
{(id) => (
|
||||||
@ -154,6 +161,14 @@ export default function AddAssetModal({ open, onClose, onCreated }) {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
|
<ActivityFields
|
||||||
|
fields={FIELDS_AFTER_OWNER}
|
||||||
|
values={values}
|
||||||
|
errors={errors}
|
||||||
|
onChange={handleChange}
|
||||||
|
disabled={submitting}
|
||||||
|
upload={upload}
|
||||||
|
/>
|
||||||
</form>
|
</form>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -24,6 +24,8 @@ const LISTS = {
|
|||||||
title: 'Asset Assignment',
|
title: 'Asset Assignment',
|
||||||
subtitle: 'Which employee has which asset, read live from the asset workflow.',
|
subtitle: 'Which employee has which asset, read live from the asset workflow.',
|
||||||
fetcher: fetchAssignmentListPage,
|
fetcher: fetchAssignmentListPage,
|
||||||
|
// Its own record view, not the asset list's.
|
||||||
|
view: RECORD_VIEWS.assetAssignments,
|
||||||
filters: ASSIGNMENT_FILTERS,
|
filters: ASSIGNMENT_FILTERS,
|
||||||
empty: {
|
empty: {
|
||||||
assigned: 'No assets are assigned right now.',
|
assigned: 'No assets are assigned right now.',
|
||||||
@ -34,6 +36,8 @@ const LISTS = {
|
|||||||
title: 'Asset Return',
|
title: 'Asset Return',
|
||||||
subtitle: 'Assets waiting to come back, and the ones already returned.',
|
subtitle: 'Assets waiting to come back, and the ones already returned.',
|
||||||
fetcher: fetchReturnListPage,
|
fetcher: fetchReturnListPage,
|
||||||
|
// Its own record view, not the asset list's.
|
||||||
|
view: RECORD_VIEWS.assetReturns,
|
||||||
filters: RETURN_FILTERS,
|
filters: RETURN_FILTERS,
|
||||||
empty: {
|
empty: {
|
||||||
assigned: 'No assets are out with employees right now.',
|
assigned: 'No assets are out with employees right now.',
|
||||||
@ -59,8 +63,9 @@ const LISTS = {
|
|||||||
*/
|
*/
|
||||||
export default function AssetActivityList({ kind }) {
|
export default function AssetActivityList({ kind }) {
|
||||||
const list = LISTS[kind]
|
const list = LISTS[kind]
|
||||||
|
const source = list.view ?? VIEW
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const view = useRecordView(VIEW.templateUid, {
|
const view = useRecordView(source.templateUid, {
|
||||||
fetcher: list.fetcher,
|
fetcher: list.fetcher,
|
||||||
filter: list.filters[0].value,
|
filter: list.filters[0].value,
|
||||||
})
|
})
|
||||||
@ -72,7 +77,7 @@ export default function AssetActivityList({ kind }) {
|
|||||||
<Card flush>
|
<Card flush>
|
||||||
<RecordView
|
<RecordView
|
||||||
{...view}
|
{...view}
|
||||||
rowKey={list.rowKey ?? VIEW.rowKey}
|
rowKey={list.rowKey ?? source.rowKey}
|
||||||
emptyText={list.empty[view.query.filter] ?? 'No assets yet.'}
|
emptyText={list.empty[view.query.filter] ?? 'No assets yet.'}
|
||||||
onRowClick={(row) => navigate(`/assets/${encodeURIComponent(assetRowId(row))}`)}
|
onRowClick={(row) => navigate(`/assets/${encodeURIComponent(assetRowId(row))}`)}
|
||||||
toolbar={
|
toolbar={
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { Button, Field, Icon, Modal, SelectInput } from '../../components/ui'
|
|||||||
import ActivityFields from '../../components/activity/ActivityFields'
|
import ActivityFields from '../../components/activity/ActivityFields'
|
||||||
import {
|
import {
|
||||||
ASSET_ASSIGN_FIELDS,
|
ASSET_ASSIGN_FIELDS,
|
||||||
|
ASSET_EDIT_ACTIVITY_ID,
|
||||||
ASSET_EDIT_FIELDS,
|
ASSET_EDIT_FIELDS,
|
||||||
ASSET_LOST_FIELDS,
|
ASSET_LOST_FIELDS,
|
||||||
ASSET_REASSIGN_FIELDS,
|
ASSET_REASSIGN_FIELDS,
|
||||||
@ -58,7 +59,7 @@ const ACTIVITIES = {
|
|||||||
return: {
|
return: {
|
||||||
title: 'Return Asset',
|
title: 'Return Asset',
|
||||||
subtitle: 'Runs the RETURNED activity in the asset workflow.',
|
subtitle: 'Runs the RETURNED activity in the asset workflow.',
|
||||||
// Photo uploads are tagged with this activity's id.
|
// Return photos are uploaded tagged with this activity's id.
|
||||||
activityId: ASSET_RETURN_ACTIVITY_ID,
|
activityId: ASSET_RETURN_ACTIVITY_ID,
|
||||||
fields: ASSET_RETURN_FIELDS,
|
fields: ASSET_RETURN_FIELDS,
|
||||||
employeeLabel: 'Returned by',
|
employeeLabel: 'Returned by',
|
||||||
@ -85,9 +86,12 @@ const ACTIVITIES = {
|
|||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
title: 'Edit Asset',
|
title: 'Edit Asset',
|
||||||
subtitle: 'Runs the Edit activity in the asset workflow. The asset keeps its state.',
|
subtitle: 'Runs the Asset details activity in the asset workflow. The asset keeps its state.',
|
||||||
|
// Photo uploads are tagged with this activity's id.
|
||||||
|
activityId: ASSET_EDIT_ACTIVITY_ID,
|
||||||
fields: ASSET_EDIT_FIELDS,
|
fields: ASSET_EDIT_FIELDS,
|
||||||
employee: false,
|
employeeLabel: 'Asset Owner',
|
||||||
|
employeeRequired: false,
|
||||||
submit: 'Save Changes',
|
submit: 'Save Changes',
|
||||||
submitting: 'Saving…',
|
submitting: 'Saving…',
|
||||||
failed: 'Could not save the changes. Please try again.',
|
failed: 'Could not save the changes. Please try again.',
|
||||||
@ -118,12 +122,14 @@ const ACTIVITIES = {
|
|||||||
/**
|
/**
|
||||||
* Dialog for one asset activity. `kind` is a key of ACTIVITIES;
|
* Dialog for one asset activity. `kind` is a key of ACTIVITIES;
|
||||||
* `defaultEmployeeId` preselects an employee (e.g. the assignee on return);
|
* `defaultEmployeeId` preselects an employee (e.g. the assignee on return);
|
||||||
* `initialValues` pre-fills the fields (e.g. the asset's details on edit).
|
* `initialValues` pre-fills the fields (e.g. the asset's details on edit);
|
||||||
|
* `instanceProblem` says why there is no `instanceId`, when there is none.
|
||||||
*/
|
*/
|
||||||
export default function AssetActivityModal({
|
export default function AssetActivityModal({
|
||||||
kind,
|
kind,
|
||||||
open,
|
open,
|
||||||
instanceId,
|
instanceId,
|
||||||
|
instanceProblem,
|
||||||
defaultEmployeeId,
|
defaultEmployeeId,
|
||||||
initialValues,
|
initialValues,
|
||||||
onClose,
|
onClose,
|
||||||
@ -133,7 +139,7 @@ export default function AssetActivityModal({
|
|||||||
const hasEmployee = activity.employee !== false
|
const hasEmployee = activity.employee !== false
|
||||||
const employeeRequired = hasEmployee && activity.employeeRequired !== false
|
const employeeRequired = hasEmployee && activity.employeeRequired !== false
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { logout } = useAuth()
|
const { logout, user } = useAuth()
|
||||||
|
|
||||||
const [employees, setEmployees] = useState([])
|
const [employees, setEmployees] = useState([])
|
||||||
const [loadingEmployees, setLoadingEmployees] = useState(false)
|
const [loadingEmployees, setLoadingEmployees] = useState(false)
|
||||||
@ -159,7 +165,17 @@ export default function AssetActivityModal({
|
|||||||
let cancelled = false
|
let cancelled = false
|
||||||
setLoadingEmployees(true)
|
setLoadingEmployees(true)
|
||||||
fetchAssignableEmployees()
|
fetchAssignableEmployees()
|
||||||
.then((list) => !cancelled && setEmployees(list))
|
.then((list) => {
|
||||||
|
if (cancelled) return
|
||||||
|
setEmployees(list)
|
||||||
|
// A field that names an employee (Assigned by) starts as the signed-in user.
|
||||||
|
const me = list.find(
|
||||||
|
(emp) => (user?.email && emp.email === user.email) || (user?.name && emp.name === user.name),
|
||||||
|
)
|
||||||
|
if (!me) return
|
||||||
|
const mine = activity.fields.filter((f) => f.employeeOptions).map((f) => f.id)
|
||||||
|
setValues((v) => ({ ...v, ...Object.fromEntries(mine.filter((id) => !v[id]).map((id) => [id, me.name])) }))
|
||||||
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (cancelled) return
|
if (cancelled) return
|
||||||
if (err?.status === 401) {
|
if (err?.status === 401) {
|
||||||
@ -173,7 +189,7 @@ export default function AssetActivityModal({
|
|||||||
return () => {
|
return () => {
|
||||||
cancelled = true
|
cancelled = true
|
||||||
}
|
}
|
||||||
}, [open, activity, hasEmployee, defaultEmployeeId, logout, navigate])
|
}, [open, activity, hasEmployee, defaultEmployeeId, logout, navigate, user])
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
if (submitting) return
|
if (submitting) return
|
||||||
@ -189,7 +205,11 @@ export default function AssetActivityModal({
|
|||||||
async function handleSubmit(e) {
|
async function handleSubmit(e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
if (instanceId == null || instanceId === '') {
|
if (instanceId == null || instanceId === '') {
|
||||||
setServerError('This asset has no instance id, so the activity cannot run.')
|
// Sending the table id instead fails with NOT_FOUND (see ASSET_WORKFLOW_VIEW_UID).
|
||||||
|
setServerError(
|
||||||
|
"This asset's workflow instance could not be found, so the activity cannot run. " +
|
||||||
|
(instanceProblem || 'Check ASSET_WORKFLOW_VIEW_UID in src/api/assets.js.'),
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const employee = employees.find((emp) => String(emp.id) === employeeId) ?? null
|
const employee = employees.find((emp) => String(emp.id) === employeeId) ?? null
|
||||||
@ -220,6 +240,10 @@ export default function AssetActivityModal({
|
|||||||
label: emp.code ? `${emp.name} (${emp.code})` : emp.name,
|
label: emp.code ? `${emp.name} (${emp.code})` : emp.name,
|
||||||
}))
|
}))
|
||||||
const formId = `${kind}-asset-form`
|
const formId = `${kind}-asset-form`
|
||||||
|
// Fields with no builder options that pick an employee by name (Assigned by).
|
||||||
|
const fields = activity.fields.map((f) =>
|
||||||
|
f.employeeOptions ? { ...f, options: employees.map((emp) => ({ label: emp.name, value: emp.name })) } : f,
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@ -278,7 +302,7 @@ export default function AssetActivityModal({
|
|||||||
</Field>
|
</Field>
|
||||||
)}
|
)}
|
||||||
<ActivityFields
|
<ActivityFields
|
||||||
fields={activity.fields}
|
fields={fields}
|
||||||
values={values}
|
values={values}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import {
|
|||||||
import FieldValue, { formatMeta } from '../../components/activity/FieldValue'
|
import FieldValue, { formatMeta } from '../../components/activity/FieldValue'
|
||||||
import { displayLabel } from '../../utils/format'
|
import { displayLabel } from '../../utils/format'
|
||||||
import Timeline from '../../components/common/Timeline'
|
import Timeline from '../../components/common/Timeline'
|
||||||
import { instanceState } from '../../api/activityForm'
|
import { instanceState, isEndState } from '../../api/activityForm'
|
||||||
import {
|
import {
|
||||||
ASSET_ASSIGN_FIELDS,
|
ASSET_ASSIGN_FIELDS,
|
||||||
ASSET_LOST_FIELDS,
|
ASSET_LOST_FIELDS,
|
||||||
@ -37,6 +37,7 @@ import {
|
|||||||
isAssetState,
|
isAssetState,
|
||||||
lookupName,
|
lookupName,
|
||||||
optionLabel,
|
optionLabel,
|
||||||
|
rowFieldValue,
|
||||||
withAssetFormColumns,
|
withAssetFormColumns,
|
||||||
} from '../../api/assets'
|
} from '../../api/assets'
|
||||||
import { useAuth } from '../../store/AuthContext'
|
import { useAuth } from '../../store/AuthContext'
|
||||||
@ -49,7 +50,7 @@ const META_KEY = /^(instance_id|current_state_name|(created|updated|modified)_\w
|
|||||||
* Asset details: the asset's INIT values, its latest assignment and return,
|
* Asset details: the asset's INIT values, its latest assignment and return,
|
||||||
* and its workflow state. There is no asset detail view template, so the record comes
|
* and its workflow state. There is no asset detail view template, so the record comes
|
||||||
* from the asset record view (see fetchAssetInstance).
|
* from the asset record view (see fetchAssetInstance).
|
||||||
* Route: /assets/:assetId — the asset's workflow instance id.
|
* Route: /assets/:assetId — the asset's table id (not its workflow instance id).
|
||||||
*/
|
*/
|
||||||
export default function AssetDetails() {
|
export default function AssetDetails() {
|
||||||
const { assetId } = useParams()
|
const { assetId } = useParams()
|
||||||
@ -167,13 +168,14 @@ export default function AssetDetails() {
|
|||||||
const isAssigned = isAssetState(row, ASSET_STATE.ASSIGNED)
|
const isAssigned = isAssetState(row, ASSET_STATE.ASSIGNED)
|
||||||
const isAvailable = isAssetState(row, ASSET_STATE.AVAILABLE)
|
const isAvailable = isAssetState(row, ASSET_STATE.AVAILABLE)
|
||||||
const isUnderRepair = isAssetState(row, ASSET_STATE.UNDER_REPAIR)
|
const isUnderRepair = isAssetState(row, ASSET_STATE.UNDER_REPAIR)
|
||||||
// ASSIGNED and Send for Repair run in Available; Reassigned and RETURNED in
|
// ASSIGNED and Send for Repair run in Available; Reassigned in Assigned; Lost
|
||||||
// Assigned; Lost in either; Repair Completed in Under Repair; Edit in all
|
// in either; Repair Completed in Under Repair; Edit in all three. RETURNED
|
||||||
// three. If the view sends no state, let the runtime decide rather than
|
// runs in any state but Closed, and the runtime says if the state doesn't
|
||||||
|
// allow it. If the view sends no state, let the runtime decide rather than
|
||||||
// hiding the buttons.
|
// hiding the buttons.
|
||||||
const canAssign = !state || isAvailable
|
const canAssign = !state || isAvailable
|
||||||
const canReassign = !state || isAssigned
|
const canReassign = !state || isAssigned
|
||||||
const canReturn = !state || isAssigned
|
const canReturn = !isAssetState(row, ASSET_STATE.CLOSED) && !isEndState(row)
|
||||||
const canReportLost = !state || isAvailable || isAssigned
|
const canReportLost = !state || isAvailable || isAssigned
|
||||||
const canSendForRepair = !state || isAvailable
|
const canSendForRepair = !state || isAvailable
|
||||||
const canCompleteRepair = !state || isUnderRepair
|
const canCompleteRepair = !state || isUnderRepair
|
||||||
@ -182,8 +184,18 @@ export default function AssetDetails() {
|
|||||||
// Assigned or reassigned, whichever is newer, is who has it now.
|
// Assigned or reassigned, whichever is newer, is who has it now.
|
||||||
const current = currentAssignment(row)
|
const current = currentAssignment(row)
|
||||||
const employee = current.name
|
const employee = current.name
|
||||||
|
// Photos show like the asset's own: captioned by slot (Front, Back…) and
|
||||||
|
// found by field id or by the view's column for them. Other values go by id
|
||||||
|
// only — several activities share labels like "Remarks".
|
||||||
const toFields = (defs) =>
|
const toFields = (defs) =>
|
||||||
defs.map((f) => ({ key: f.id, label: f.label, type: f.type, value: row[f.id] }))
|
defs.map((f) => ({
|
||||||
|
key: f.id,
|
||||||
|
label: f.label,
|
||||||
|
type: f.type,
|
||||||
|
slots: f.slots,
|
||||||
|
otherSlot: f.otherSlot,
|
||||||
|
value: f.type === 'image' ? rowFieldValue(f, row) : row[f.id],
|
||||||
|
}))
|
||||||
const assignmentFields = [
|
const assignmentFields = [
|
||||||
{ key: ASSIGN_EMPLOYEE_FIELD, label: 'Employee data', type: 'text', value: lookupName(row, ASSIGN_EMPLOYEE_FIELD) },
|
{ key: ASSIGN_EMPLOYEE_FIELD, label: 'Employee data', type: 'text', value: lookupName(row, ASSIGN_EMPLOYEE_FIELD) },
|
||||||
...toFields(ASSET_ASSIGN_FIELDS),
|
...toFields(ASSET_ASSIGN_FIELDS),
|
||||||
@ -388,10 +400,17 @@ export default function AssetDetails() {
|
|||||||
<AssetActivityModal
|
<AssetActivityModal
|
||||||
kind={activity}
|
kind={activity}
|
||||||
open
|
open
|
||||||
instanceId={assetId}
|
// The workflow instance id, not the table id in the URL (see fetchAssetInstance).
|
||||||
// On return or loss, the person who has it now is the likely one.
|
instanceId={row.instance_id}
|
||||||
|
instanceProblem={data.instanceProblem}
|
||||||
|
// On return or loss, the person who has it now is the likely one; on
|
||||||
|
// edit, the asset's current owner.
|
||||||
defaultEmployeeId={
|
defaultEmployeeId={
|
||||||
(activity === 'return' || activity === 'lost') && isAssigned ? current.id : null
|
activity === 'edit'
|
||||||
|
? data.ownerId
|
||||||
|
: (activity === 'return' || activity === 'lost') && isAssigned
|
||||||
|
? current.id
|
||||||
|
: null
|
||||||
}
|
}
|
||||||
initialValues={activity === 'edit' ? assetEditValues(row) : undefined}
|
initialValues={activity === 'edit' ? assetEditValues(row) : undefined}
|
||||||
onClose={() => setActivity(null)}
|
onClose={() => setActivity(null)}
|
||||||
|
|||||||
@ -13,10 +13,11 @@ export default function AssetList() {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
// Every field the Add Asset form fills gets a column, even if the view lacks
|
// Every field the Add Asset form fills gets a column, even if the view lacks
|
||||||
// one, and Asset Owner shows the employee's name. Newest first, so a just
|
// one, and Asset Owner shows the employee's name. Newest first, so a just
|
||||||
// created asset is on page 1 (the view itself lists oldest first).
|
// created asset is on page 1 (the view itself lists oldest first). idgen_2 is
|
||||||
|
// the view's Asset id column (ID-00001…).
|
||||||
const view = useRecordView(VIEW.templateUid, {
|
const view = useRecordView(VIEW.templateUid, {
|
||||||
transform: withAssetListDisplay,
|
transform: withAssetListDisplay,
|
||||||
sortBy: 'id',
|
sortBy: 'idgen_2',
|
||||||
sortDir: 'desc',
|
sortDir: 'desc',
|
||||||
})
|
})
|
||||||
const [adding, setAdding] = useState(false)
|
const [adding, setAdding] = useState(false)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user