WMS Plugin Example
Complete working example of a Warehouse Management plugin for Framework M.
Structure
wms-plugin/
├── package.json # Plugin metadata
├── tsconfig.json
└── src/
├── plugin.config.ts # Plugin manifest
├── pages/
│ ├── Dashboard.tsx # WMS Dashboard
│ └── Inventory.tsx # Inventory list
└── services/
└── InventoryService.ts
Setup
This example is a reference implementation. To create your own:
m new:app wms --with-frontend
Then copy the patterns shown below into your generated plugin.
Files
package.json
{
"name": "@wms/frontend",
"version": "0.1.0",
"private": true,
"framework-m": {
"plugin": "./src/plugin.config.ts",
"type": "frontend-module"
},
"dependencies": {
"@framework-m/desk": "^0.1.0",
"@framework-m/plugin-sdk": "^0.1.0"
}
}
src/plugin.config.ts
import type { FrameworkMPlugin } from "@framework-m/plugin-sdk";
const plugin: FrameworkMPlugin = {
name: "wms",
version: "0.1.0",
menu: [
{
name: "wms.dashboard",
label: "WMS Dashboard",
route: "/wms/dashboard",
icon: "warehouse",
module: "WMS",
category: "Overview",
order: 1,
},
{
name: "wms.inventory",
label: "Inventory",
route: "/wms/inventory",
icon: "package",
module: "WMS",
category: "Operations",
order: 2,
},
{
name: "wms.receiving",
label: "Receiving",
route: "/wms/receiving",
icon: "truck",
module: "WMS",
category: "Operations",
order: 3,
},
],
routes: [
{
path: "/wms/dashboard",
element: () => import("./pages/Dashboard"),
},
{
path: "/wms/inventory",
element: () => import("./pages/Inventory"),
},
],
services: {
inventoryService: () => import("./services/InventoryService"),
},
};
export default plugin;
src/pages/Dashboard.tsx
export default function WmsDashboard() {
return (
<div style={{ padding: "2rem" }}>
<h1>Warehouse Management</h1>
<div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: "1rem", marginTop: "1rem" }}>
<div className="card">
<h3>📦 142</h3>
<p>Items in Stock</p>
</div>
<div className="card">
<h3>🚚 8</h3>
<p>Pending Receipts</p>
</div>
<div className="card">
<h3>📋 23</h3>
<p>Open Pick Lists</p>
</div>
</div>
</div>
);
}
src/pages/Inventory.tsx
import { AutoTable, useDocTypeMeta } from "@framework-m/desk";
export default function Inventory() {
const { schema, isLoading } = useDocTypeMeta("InventoryItem");
if (isLoading) return <div>Loading...</div>;
return (
<div style={{ padding: "2rem" }}>
<h1>Inventory</h1>
<AutoTable
resource="InventoryItem"
schema={schema}
onRowClick={(row) => console.log("Selected:", row)}
/>
</div>
);
}
src/services/InventoryService.ts
export interface InventoryService {
getStockLevel(itemId: string): Promise<number>;
getLowStockItems(): Promise<string[]>;
}
const inventoryService: InventoryService = {
async getStockLevel(itemId: string) {
const res = await fetch(`/api/v1/InventoryItem/${itemId}`);
const data = await res.json();
return data.stock_qty ?? 0;
},
async getLowStockItems() {
const res = await fetch("/api/v1/InventoryItem?filters=[['stock_qty','<',10]]");
const data = await res.json();
return data.data?.map((d: { name: string }) => d.name) ?? [];
},
};
export default inventoryService;