Phase 06: Frontend & UI
Duration: 1 week (parallel with backend phases) Goal: Build user interfaces for Legacy ERP WMS operations using Refine.dev
Update (Jan 2026): Developers have chosen Refine.dev as it maps 1:1 with Framework M’s auto-generated APIs. Only ~100 lines of code needed for data provider mapping.
1. UI Strategy: Refine.dev
Section titled “1. UI Strategy: Refine.dev”Why Refine.dev
Section titled “Why Refine.dev”| Advantage | Benefit for Legacy ERP |
|---|---|
| 1:1 API mapping | Framework M generates OpenAPI, Refine consumes it |
| Headless | Choose any UI library (Ant Design, Material UI, etc.) |
| CRUD out of box | List, Create, Edit, Show pages auto-generated |
| Auth integration | Works with JWT/Session auth |
| Real-time | WebSocket support for live updates |
Data Provider (~100 lines)
Section titled “Data Provider (~100 lines)”import { DataProvider } from "@refinedev/core";
const API_URL = import.meta.env.VITE_API_URL;
export const dataProvider: DataProvider = { getList: async ({ resource, pagination, filters, sorters }) => { const { current = 1, pageSize = 10 } = pagination ?? {}; const params = new URLSearchParams({ page: String(current), page_size: String(pageSize), });
// Add filters filters?.forEach(filter => { params.append(`filter[${filter.field}]`, String(filter.value)); });
const response = await fetch(`${API_URL}/api/v1/${resource}?${params}`, { headers: { Authorization: `Bearer ${localStorage.getItem("token")}` }, }); const data = await response.json();
return { data: data.items, total: data.total, }; },
getOne: async ({ resource, id }) => { const response = await fetch(`${API_URL}/api/v1/${resource}/${id}`, { headers: { Authorization: `Bearer ${localStorage.getItem("token")}` }, }); return { data: await response.json() }; },
create: async ({ resource, variables }) => { const response = await fetch(`${API_URL}/api/v1/${resource}`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${localStorage.getItem("token")}`, }, body: JSON.stringify(variables), }); return { data: await response.json() }; },
update: async ({ resource, id, variables }) => { const response = await fetch(`${API_URL}/api/v1/${resource}/${id}`, { method: "PUT", headers: { "Content-Type": "application/json", Authorization: `Bearer ${localStorage.getItem("token")}`, }, body: JSON.stringify(variables), }); return { data: await response.json() }; },
deleteOne: async ({ resource, id }) => { await fetch(`${API_URL}/api/v1/${resource}/${id}`, { method: "DELETE", headers: { Authorization: `Bearer ${localStorage.getItem("token")}` }, }); return { data: { id } }; },
getApiUrl: () => API_URL,};2. Project Setup
Section titled “2. Project Setup”2.1 Create Refine App
Section titled “2.1 Create Refine App”npm create refine-app@latest legacy_erp-wms-ui -- \ --ui antd \ --router react-router \ --auth custom2.2 Project Structure
Section titled “2.2 Project Structure”legacy_erp-wms-ui/├── src/│ ├── providers/│ │ ├── frameworkMDataProvider.ts # ~100 lines│ │ └── authProvider.ts│ ├── pages/│ │ ├── items/│ │ ├── depots/│ │ ├── urcs/│ │ └── stock-entries/│ ├── components/│ └── App.tsx├── package.json└── vite.config.ts2.3 Tasks
Section titled “2.3 Tasks”- Create Refine project with Ant Design
- Implement Framework M data provider
- Implement auth provider (JWT)
- Configure resources
3. Resources Configuration
Section titled “3. Resources Configuration”import { Refine } from "@refinedev/core";import { dataProvider } from "./providers/frameworkMDataProvider";import { authProvider } from "./providers/authProvider";
function App() { return ( <Refine dataProvider={dataProvider} authProvider={authProvider} resources={[ { name: "item", list: "/items", create: "/items/create", edit: "/items/:id/edit", show: "/items/:id", meta: { label: "Items", icon: <ShoppingOutlined /> }, }, { name: "depot", list: "/depots", create: "/depots/create", edit: "/depots/:id/edit", meta: { label: "Depots", icon: <HomeOutlined /> }, }, { name: "urc", list: "/urcs", create: "/urcs/create", edit: "/urcs/:id/edit", meta: { label: "URCs", icon: <ShopOutlined /> }, }, { name: "stock_entry", list: "/stock-entries", create: "/stock-entries/create", show: "/stock-entries/:id", meta: { label: "Stock Entries", icon: <SwapOutlined /> }, }, { name: "material_request", list: "/material-requests", create: "/material-requests/create", show: "/material-requests/:id", meta: { label: "Material Requests", icon: <FileTextOutlined /> }, }, ]} /> );}4. User Roles & Portals
Section titled “4. User Roles & Portals”Portal Types
Section titled “Portal Types”| Portal | Users | Resources |
|---|---|---|
| HQ Admin | Legacy ERP HO Staff | All resources, reports |
| Depot | Depot Staff | Inventory, GRN, Delivery |
| URC | URC Managers | Orders, Stock view |
Role-based Resource Visibility
Section titled “Role-based Resource Visibility”// Filter resources based on user roleconst getResourcesForRole = (role: string) => { const allResources = [...];
if (role === "depot_staff") { return allResources.filter(r => ["stock_entry", "material_request", "item"].includes(r.name) ); } // ...};5. Key Pages
Section titled “5. Key Pages”5.1 Item List (Auto-generated with customization)
Section titled “5.1 Item List (Auto-generated with customization)”import { List, Table, useTable } from "@refinedev/antd";
export const ItemList = () => { const { tableProps } = useTable();
return ( <List> <Table {...tableProps} rowKey="id"> <Table.Column dataIndex="item_code" title="Code" /> <Table.Column dataIndex="item_name" title="Name" /> <Table.Column dataIndex="item_group" title="Group" /> <Table.Column dataIndex="legacy_erp_price" title="Legacy ERP Price" /> <Table.Column dataIndex="is_active" title="Active" /> </Table> </List> );};5.2 Stock Entry Form
Section titled “5.2 Stock Entry Form”import { Create, useForm, useSelect } from "@refinedev/antd";import { Form, Input, Select, Table } from "antd";
export const StockEntryCreate = () => { const { formProps, saveButtonProps } = useForm(); const { selectProps: warehouseProps } = useSelect({ resource: "warehouse" });
return ( <Create saveButtonProps={saveButtonProps}> <Form {...formProps} layout="vertical"> <Form.Item label="Entry Type" name="entry_type" rules={[{ required: true }]}> <Select options={[ { value: "Receipt", label: "Receipt" }, { value: "Issue", label: "Issue" }, { value: "Transfer", label: "Transfer" }, ]} /> </Form.Item> <Form.Item label="Warehouse" name="warehouse"> <Select {...warehouseProps} /> </Form.Item> {/* Items table */} </Form> </Create> );};6. Authentication
Section titled “6. Authentication”Auth Provider
Section titled “Auth Provider”import { AuthProvider } from "@refinedev/core";
export const authProvider: AuthProvider = { login: async ({ email, password }) => { const response = await fetch(`${API_URL}/api/v1/auth/login`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email, password }), });
if (response.ok) { const { access_token, user } = await response.json(); localStorage.setItem("token", access_token); localStorage.setItem("user", JSON.stringify(user)); return { success: true, redirectTo: "/" }; }
return { success: false, error: { message: "Invalid credentials" } }; },
logout: async () => { localStorage.removeItem("token"); localStorage.removeItem("user"); return { success: true, redirectTo: "/login" }; },
check: async () => { const token = localStorage.getItem("token"); return { authenticated: !!token }; },
getIdentity: async () => { const user = localStorage.getItem("user"); return user ? JSON.parse(user) : null; },};7. Deployment
Section titled “7. Deployment”Build for MeghRaj
Section titled “Build for MeghRaj”FROM node:20-alpine AS builderWORKDIR /appCOPY package*.json ./RUN npm ciCOPY . .RUN npm run build
FROM nginx:alpineCOPY --from=builder /app/dist /usr/share/nginx/htmlCOPY nginx.conf /etc/nginx/nginx.confEXPOSE 808. Verification Checklist
Section titled “8. Verification Checklist”- Login/logout works
- Item CRUD functional
- Depot CRUD functional
- URC CRUD functional
- Stock Entry creation works
- Material Request workflow works
- Role-based access working
- Mobile responsive
- Deployed to MeghRaj
Time Estimate
Section titled “Time Estimate”| Task | Duration |
|---|---|
| Setup + Data Provider | 4 hours |
| Auth Provider | 2 hours |
| Core resources | 2 days |
| Custom pages | 2 days |
| Testing & polish | 1 day |
| Total | ~5 days |