Skip to content

Asset Developer Notes

This document is generated from asset-related source files. It summarizes evidence-backed user behavior, implementation-facing constraints, and extracted UI structure for assets.

  • Scope: user_assets
  • Generated At: 2026-05-14T14:21:48.668Z
  • Files Scanned: assetFiles=3, apiFiles=15, backendFiles=45

Pages

  • Asset List (/assets) [confidence: high] [source: assets]
  • Create Asset (/assets/new) [confidence: high] [source: assets]
  • Asset Details (/assets/:assetId) [confidence: high] [source: assets]

Controls

  • Create Asset [button] in asset_list [confidence: high] [source: assets]
  • Open [button] in asset_list [confidence: high] [source: assets]
  • Delete [button] in asset_list [confidence: high] [source: assets]
  • Create [button] in asset_create [confidence: high] [source: assets]
  • Open Device [link] in attached_devices [confidence: high] [source: assets]
  • No assets found [message] in asset_list [confidence: high] [source: assets]
  • Not found [message] in asset_detail [confidence: high] [source: assets]

Flows

  • Create asset: Open the Assets area. -> Click Create Asset. -> Enter the asset name. -> Select the asset type. -> If required, select the parent asset. -> Create the asset. [confidence: high]
  • Open asset details: Open the Assets area. -> Locate the asset you want to inspect. -> Open the asset details page. [confidence: high]
  • Delete asset: Open the Assets area. -> Choose an asset. -> Delete the asset. -> Confirm the deletion. [confidence: high]
  • Open attached device: Open an asset details page. -> Review the attached devices table. -> Open a linked device from the table. [confidence: high]

Constraints

  • Assets are organized in a hierarchy that can include SITE, AREA, and ZONE levels. [hierarchy] [confidence: high] [source: assets]
  • Parent asset selection depends on the selected asset type. AREA requires a SITE parent, and ZONE requires an AREA parent. [dependency] [confidence: high] [source: assets]
  • An asset cannot be deleted when it has child assets. [warning] [confidence: high] [source: assets]
  • An asset cannot be deleted when devices are attached to it. [warning] [confidence: high] [source: assets]
  • The assets list shows an empty state when no assets are available. [empty_state] [confidence: high] [source: assets]
  • The asset details page can show a not found state when the asset does not exist. [empty_state] [confidence: high] [source: assets]
  • Asset creation requires core fields such as asset name and type. [validation] [confidence: high] [source: assets]

Tables

  • asset-list-table: Name [confidence: high] [source: assets]
  • attached-devices-table: Name, Type, Status [confidence: high] [source: assets]

Forms

  • create-asset-form: Asset Name, Type | sections: Asset [confidence: high] [source: assets]

Unknowns

  • None verified

c:/autoconnecto/frontend/src/features/assets/AssetCreatePage.tsx

text
"react"; import { Form, Input, Select, Button, Typography, message, Spin, } from "antd"; import { useNavigate } from "react-router-dom"; import { fetchAssets, createAsset, } from "../../api/assets.api"; const { Title } = Typography; type AssetType = "SITE" | "AREA" | "ZONE"; type Asset = { assetId: string; name: string; type: AssetType; parentId?: string; }; export default function AssetCreatePage() { const [form] = Form.useForm(); const navigate = useNavigate(); const [assets, setAssets] = useState<Asset[]>([]); const [load

c:/autoconnecto/frontend/src/features/assets/AssetDetailPage.tsx

text
td"; import { DeleteOutlined, PlusOutlined, ArrowLeftOutlined, } from "@ant-design/icons"; import { useParams, useNavigate } from "react-router-dom"; import { fetchAssets, fetchAssetAttributes, saveAssetAttributes, deleteAssetAttribute, } from "../../api/assets.api"; import { fetchDevices } from "../../api/devices.api"; function getTypeColor(type?: string) { if (type === "SITE") return "blue"; if (type === "AREA") return "green"; if (type === "ZONE") return "orange"; return "default"; } const { Title, Text } = Typography; const { TextArea } = Input;

c:/autoconnecto/frontend/src/features/assets/AssetListPage.tsx

text
Popconfirm, message, Card, Empty, } from "antd"; import { PlusOutlined } from "@ant-design/icons"; import { useNavigate } from "react-router-dom"; import { fetchAssets, deleteAsset, fetchAssetAttributes, } from "../../api/assets.api"; import { fetchDevices } from "../../api/devices.api"; const { Title, Text } = Typography; type AssetType = "SITE" | "AREA" | "ZONE"; type Asset = { assetId: string; name: string; type: AssetType; parentId?: string; }; export default function AssetsListPage() { const [assets, setAssets] = useState<Asset[]>([])

c:/autoconnecto/frontend/src/features/assets/AssetCreatePage.tsx

text
e.success("Asset created successfully"); navigate("/assets"); } catch (err: any) { message.error( err?.response?.data?.message || "Failed to create asset", ); } }; if (loading) return <Spin />; return ( <div style={{ padding: 24 }}> <Title level={2}>Create Asset</Title> <Form form={form} layout="vertical" onFinish={onFinish} > <Form.Item label="Asset Name" name="name" rules={[{ required: true }]} > <Input />

c:/autoconnecto/frontend/src/features/assets/AssetDetailPage.tsx

text
message, Popconfirm, Tag, Empty, Breadcrumb, } from "antd"; import { DeleteOutlined, PlusOutlined, ArrowLeftOutlined, } from "@ant-design/icons"; import { useParams, useNavigate } from "react-router-dom"; import { fetchAssets, fetchAssetAttributes, saveAssetAttributes, deleteAssetAttribute, } from "../../api/assets.api"; import { fetchDevices } from "../../api/devices.api"; function getTypeColor(type?: string) { if (type === "SITE") return "blue"; if (type === "AREA") return "green"; if (type === "ZONE") return "orange"; return "default"; }

c:/autoconnecto/frontend/src/features/assets/AssetListPage.tsx

text
</div> </div> <Button type="primary" icon={<PlusOutlined />} onClick={() => navigate("/assets/new")} > Create Asset </Button> </div> <Card bodyStyle={{ padding: 0 }}> <Table rowKey="assetId" columns={columns} dataSource={hierarchyData} pagination={false} locale={{ emptyText: <Empty description="No assets found" />, }} /> </Card> </> ); }

c:/autoconnecto/frontend/src/features/assets/AssetListPage.tsx

text
Actions", width: 170, render: (_: any, r: Asset) => ( <Space> <Button size="small" onClick={() => navigate(`/assets/${r.assetId}`)}> Open </Button> <Popconfirm title="Delete this asset?" onConfirm={() => handleDelete(r)} > <Button danger size="small"> Delete </Button> </Popconfirm> </Space> ), }, ]; if (loading) { return ( <div style={{ minHeight: 240, display: "fle

c:/autoconnecto/frontend/src/features/assets/AssetListPage.tsx

text
<Space> <Button size="small" onClick={() => navigate(`/assets/${r.assetId}`)}> Open </Button> <Popconfirm title="Delete this asset?" onConfirm={() => handleDelete(r)} > <Button danger size="small"> Delete </Button> </Popconfirm> </Space> ), }, ]; if (loading) { return ( <div style={{ minHeight: 240, display: "flex", alignItems: "center", justifyContent: "center

c:/autoconnecto/frontend/src/features/assets/AssetCreatePage.tsx

text
))} </Select> </Form.Item> ); }} </Form.Item> <Button type="primary" htmlType="submit" > Create </Button> </Form> </div> ); }

c:/autoconnecto/frontend/src/features/assets/AssetDetailPage.tsx

text
ces" />, }} columns={[ { title: "Name", render: (_: any, r: any) => ( <a onClick={() => navigate(`/devices/${r.deviceId}`)}> {r.name} </a> ), }, { title: "Type", dataIndex: "type", }, { title: "Status", dataIndex: "status", }, ]} /> </Card> <Card title="Attributes" style={{ marginTop: 24

c:/autoconnecto/frontend/src/features/assets/AssetListPage.tsx

text
rowKey="assetId" columns={columns} dataSource={hierarchyData} pagination={false} locale={{ emptyText: <Empty description="No assets found" />, }} /> </Card> </> ); }

c:/autoconnecto/frontend/src/features/assets/AssetDetailPage.tsx

text
"flex", alignItems: "center", justifyContent: "center", }} > <Spin /> </div> ); } if (!asset) { return <div>Not found</div>; } return ( <> <Space direction="vertical" size={8} style={{ width: "100%" }}> <Space> <Button icon={<ArrowLeftOutlined />} onClick={() => navigate("/assets")} > Back to Assets </Button> </Space> <Breadcrumb items={[ { title: (

c:/autoconnecto/frontend/src/features/assets/AssetCreatePage.tsx

text
<Title level={2}>Create Asset</Title> <Form form={form} layout="vertical" onFinish={onFinish} > <Form.Item label="Asset Name" name="name" rules={[{ required: true }]} > <Input /> </Form.Item> <Form.Item label="Type" name="type" rules={[{ required: true }]} > <Select onChange={() => form.setFieldsValue({ parentId: undefined, }) } >

c:/autoconnecto/frontend/src/features/assets/AssetCreatePage.tsx

text
</Form.Item> <Form.Item shouldUpdate> {({ getFieldValue }) => { const type = getFieldValue("type"); if (!type || type === "SITE") return null; const validParents = type === "AREA" ? assets.filter( (a) => a.type === "SITE", ) : assets.filter( (a) => a.type === "AREA", ); return ( <Form.Item label={ type ===

c:/autoconnecto/frontend/src/features/assets/AssetListPage.tsx

text
"Failed to load assets"); } finally { setLoading(false); } }; const buildHierarchy = (list: Asset[]): Asset[] => { const sites = list.filter((a) => a.type === "SITE"); const areas = list.filter((a) => a.type === "AREA"); const zones = list.filter((a) => a.type === "ZONE"); const result: Asset[] = []; sites.forEach((site) => { result.push(site); areas .filter((area) => area.parentId === site.assetId) .forEach((area) => { result.push(area); zones .filter((zone) => zone.par

c:/autoconnecto/frontend/src/features/assets/AssetCreatePage.tsx

text
const type = getFieldValue("type"); if (!type || type === "SITE") return null; const validParents = type === "AREA" ? assets.filter( (a) => a.type === "SITE", ) : assets.filter( (a) => a.type === "AREA", ); return ( <Form.Item label={ type === "AREA" ? "Select SITE" : "Select AREA"

c:/autoconnecto/frontend/src/features/assets/AssetListPage.tsx

text
setId: string) => assets.some((a) => a.parentId === assetId); const handleDelete = async (asset: Asset) => { if (hasChildren(asset.assetId)) { message.error("Cannot delete asset with child assets"); return; } if (countDevices(asset.assetId) > 0) { message.error("Cannot delete asset with attached devices"); return; } try { await deleteAsset(asset.assetId); message.success("Asset deleted"); loadAll(); } catch (err: any) { message.error(err?.response?.data?.message || "Delete failed"); } };

c:/autoconnecto/frontend/src/features/assets/AssetListPage.tsx

text
Children(asset.assetId)) { message.error("Cannot delete asset with child assets"); return; } if (countDevices(asset.assetId) > 0) { message.error("Cannot delete asset with attached devices"); return; } try { await deleteAsset(asset.assetId); message.success("Asset deleted"); loadAll(); } catch (err: any) { message.error(err?.response?.data?.message || "Delete failed"); } }; const hierarchyData = useMemo(() => buildHierarchy(assets), [assets]); const summary = useMemo(() => { const totalA

c:/autoconnecto/frontend/src/features/assets/AssetCreatePage.tsx

text
form={form} layout="vertical" onFinish={onFinish} > <Form.Item label="Asset Name" name="name" rules={[{ required: true }]} > <Input /> </Form.Item> <Form.Item label="Type" name="type" rules={[{ required: true }]} > <Select onChange={() => form.setFieldsValue({ parentId: undefined, }) } > <Select.Option value="SITE"> S

c:/autoconnecto/frontend/src/features/assets/AssetListPage.tsx

text
alDevices, totalSites, totalAreas, totalZones, totalAttributes, }; }, [assets, devices, attributeCounts]); const columns = [ { title: "Name", render: (_: any, r: Asset) => { const indent = r.type === "AREA" ? 24 : r.type === "ZONE" ? 48 : 0; const icon = r.type === "SITE" ? "🏭" : r.type === "AREA" ? "🏢" : "📍"; return ( <div style={{ paddingLeft: indent }}> <div style={{ fontWeight: 500 }}> {icon} {r.name} </div> <Text type="se

c:/autoconnecto/frontend/src/features/assets/AssetDetailPage.tsx

text
pagination={false} locale={{ emptyText: <Empty description="No attached devices" />, }} columns={[ { title: "Name", render: (_: any, r: any) => ( <a onClick={() => navigate(`/devices/${r.deviceId}`)}> {r.name} </a> ), }, { title: "Type", dataIndex: "type", }, { title: "Status", dataIndex: "status", },

c:/autoconnecto/frontend/src/features/assets/AssetCreatePage.tsx

text
tyle={{ padding: 24 }}> <Title level={2}>Create Asset</Title> <Form form={form} layout="vertical" onFinish={onFinish} > <Form.Item label="Asset Name" name="name" rules={[{ required: true }]} > <Input /> </Form.Item> <Form.Item label="Type" name="type" rules={[{ required: true }]} > <Select onChange={() => form.setFieldsValue({ parentId: undefined, })

support@autoconnecto.in · founder@autoconnecto.in · +91 92121 00555 · app.autoconnecto.in