diff --git a/src/api/clients.ts b/src/api/clients.ts
index bbe0f12..b426984 100644
--- a/src/api/clients.ts
+++ b/src/api/clients.ts
@@ -21,10 +21,11 @@ export function clientFor(slug: WorkflowSlug): ZinoClient {
export const orderBookingClient = clientFor('orderBooking');
export const storeClient = clientFor('store');
export const dailyReportsClient = clientFor('dailyReports');
+export const userClient = clientFor('user');
// The user JWT (from /usr/login) is valid across every workflow, but each
// client holds its own copy — so auth helpers fan out to all of them.
-const ALL = [orderBookingClient, storeClient, dailyReportsClient];
+const ALL = [orderBookingClient, storeClient, dailyReportsClient, userClient];
/** Log in once and share the JWT with every workflow client. */
export async function loginAll(email: string, password: string, orgId?: string) {
diff --git a/src/api/config.ts b/src/api/config.ts
index 8a40343..d9aed87 100644
--- a/src/api/config.ts
+++ b/src/api/config.ts
@@ -177,11 +177,38 @@ export const DAILY_REPORTS = {
},
} as const;
+// --- User Management (src/docs/api/user.md) ---
+export const USER = {
+ workflowUuid: '9874af12-78b7-4730-834a-19a32221f3aa',
+ versionUuid: '', // No version UUID provided, server will use latest
+ activities: {
+ ADD_USER: {
+ uid: '3a8f132e-8c63-4d4d-b466-67595d345a99', // init
+ fields: {
+ name: 'name', // text
+ email: 'email', // email
+ password: 'password', // password
+ },
+ },
+ EDIT_USER: {
+ uid: '3091530c-4315-42b9-b53b-a148f2c54a81',
+ fields: {
+ name: 'name_3', // text
+ email: 'email_3', // email
+ },
+ },
+ },
+ recordViews: {
+ SALES_OFFICERS: '406f89d7-09f6-42ba-ba35-84334ccb6204',
+ },
+} as const;
+
// All workflows keyed by slug, for generic lookup.
export const WORKFLOWS = {
orderBooking: ORDER_BOOKING,
store: STORE,
dailyReports: DAILY_REPORTS,
+ user: USER,
} as const;
// --- Daily Sales Report Constants ---
diff --git a/src/components/rv/UsersView.tsx b/src/components/rv/UsersView.tsx
new file mode 100644
index 0000000..1564cc1
--- /dev/null
+++ b/src/components/rv/UsersView.tsx
@@ -0,0 +1,24 @@
+import { userClient } from '../../api/clients';
+import { USER } from '../../api/config';
+import { RecordView } from './RecordView';
+import type { WiredRecordViewProps } from './OrdersView';
+
+/** Users / Sales Officers record view (User workflow). */
+export function UsersView({ onRowClick, pageSize, headerActions, rowActions, refreshKey, initialFilters, presetAlias }: WiredRecordViewProps) {
+ return (
+
+ );
+}
diff --git a/src/components/rv/index.ts b/src/components/rv/index.ts
index 00fca48..ed3df33 100644
--- a/src/components/rv/index.ts
+++ b/src/components/rv/index.ts
@@ -5,3 +5,4 @@ export type { WiredRecordViewProps } from './OrdersView';
export { CallsView } from './CallsView';
export { StoresView } from './StoresView';
export { DailyLogsView } from './DailyLogsView';
+export { UsersView } from './UsersView';
diff --git a/src/routesConfig.tsx b/src/routesConfig.tsx
index 0a7b129..cee2142 100644
--- a/src/routesConfig.tsx
+++ b/src/routesConfig.tsx
@@ -8,6 +8,7 @@ import { MyDailyLogsPage } from './screens/MyDailyLogsPage'
import { DailySalesReportPage } from './screens/DailySalesReportPage'
import { ReportPage } from './screens/ReportPage'
import { DatasetsPage } from './screens/admin/DatasetsPage'
+import { UsersPage } from './screens/admin/UsersPage'
export const routeConfig = [
// Sales Officer
@@ -104,4 +105,9 @@ export const routeConfig = [
element: ,
adminOnly: true,
},
+ {
+ path: "/admin/users",
+ element: ,
+ adminOnly: true,
+ },
];
diff --git a/src/screens/ConsoleLayout.tsx b/src/screens/ConsoleLayout.tsx
index c826cae..81b24c5 100644
--- a/src/screens/ConsoleLayout.tsx
+++ b/src/screens/ConsoleLayout.tsx
@@ -1,6 +1,6 @@
import { useEffect } from 'react';
import { NavLink, Navigate, Outlet, useNavigate, useLocation } from 'react-router-dom';
-import { LogOut, ChevronDown, Database } from 'lucide-react';
+import { LogOut, ChevronDown, Database, Users } from 'lucide-react';
import { cn } from '../lib/cn';
import { useAuth } from '../auth/context';
import { onAuthErrorAll, orderBookingClient } from '../api/clients';
@@ -132,6 +132,13 @@ export function ConsoleLayout() {
Manage Datasets
+ cn("w-full text-left px-3 py-2 text-sm font-medium hover:bg-indigo-50 hover:text-indigo-700 rounded-md transition-colors flex items-center gap-2 cursor-pointer mt-1", isActive ? "text-indigo-700 bg-indigo-50" : "text-gray-700")}
+ >
+
+ Manage Users
+
)}
diff --git a/src/screens/admin/UsersPage.tsx b/src/screens/admin/UsersPage.tsx
new file mode 100644
index 0000000..c23a49d
--- /dev/null
+++ b/src/screens/admin/UsersPage.tsx
@@ -0,0 +1,64 @@
+import { useState } from 'react';
+import { Modal } from '../../components/reusable';
+import { UsersView } from '../../components/rv';
+import { DynamicForm } from '../../components/forms/DynamicForm';
+import { USER } from '../../api/config';
+import { userClient } from '../../api/clients';
+import { Button } from '../../components/buttons/Button';
+import { Plus, Edit } from 'lucide-react';
+
+export function UsersPage() {
+ const [isCreating, setIsCreating] = useState(false);
+ const [refreshKey, setRefreshKey] = useState(0);
+ const [activeActivity, setActiveActivity] = useState<{ id: string; name: string, instanceId?: number } | null>(null);
+
+ const handleEditRow = (row: any) => {
+ setActiveActivity({
+ id: USER.activities.EDIT_USER.uid,
+ name: "Edit User",
+ instanceId: row.instance_id
+ });
+ };
+
+ return (
+ <>
+ } onClick={() => setIsCreating(true)}>
+ Add Sales Officer
+
+ }
+ rowActions={(row) => (
+ } onClick={() => handleEditRow(row)}>
+ Edit
+
+ )}
+ />
+
+ {
+ setIsCreating(false);
+ setActiveActivity(null);
+ }}
+ title={activeActivity?.name || "Add User"}
+ >
+ {
+ setIsCreating(false);
+ setActiveActivity(null);
+ setRefreshKey(k => k + 1);
+ }}
+ onCancel={() => {
+ setIsCreating(false);
+ setActiveActivity(null);
+ }}
+ />
+
+ >
+ );
+}