added create user
This commit is contained in:
parent
dc4a119a7a
commit
8f69af24ba
@ -21,10 +21,11 @@ export function clientFor(slug: WorkflowSlug): ZinoClient {
|
|||||||
export const orderBookingClient = clientFor('orderBooking');
|
export const orderBookingClient = clientFor('orderBooking');
|
||||||
export const storeClient = clientFor('store');
|
export const storeClient = clientFor('store');
|
||||||
export const dailyReportsClient = clientFor('dailyReports');
|
export const dailyReportsClient = clientFor('dailyReports');
|
||||||
|
export const userClient = clientFor('user');
|
||||||
|
|
||||||
// The user JWT (from /usr/login) is valid across every workflow, but each
|
// 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.
|
// 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. */
|
/** Log in once and share the JWT with every workflow client. */
|
||||||
export async function loginAll(email: string, password: string, orgId?: string) {
|
export async function loginAll(email: string, password: string, orgId?: string) {
|
||||||
|
|||||||
@ -177,11 +177,38 @@ export const DAILY_REPORTS = {
|
|||||||
},
|
},
|
||||||
} as const;
|
} 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.
|
// All workflows keyed by slug, for generic lookup.
|
||||||
export const WORKFLOWS = {
|
export const WORKFLOWS = {
|
||||||
orderBooking: ORDER_BOOKING,
|
orderBooking: ORDER_BOOKING,
|
||||||
store: STORE,
|
store: STORE,
|
||||||
dailyReports: DAILY_REPORTS,
|
dailyReports: DAILY_REPORTS,
|
||||||
|
user: USER,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// --- Daily Sales Report Constants ---
|
// --- Daily Sales Report Constants ---
|
||||||
|
|||||||
24
src/components/rv/UsersView.tsx
Normal file
24
src/components/rv/UsersView.tsx
Normal file
@ -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 (
|
||||||
|
<RecordView
|
||||||
|
client={userClient}
|
||||||
|
rvUid={USER.recordViews.SALES_OFFICERS}
|
||||||
|
title="Sales Officers"
|
||||||
|
onRowClick={onRowClick}
|
||||||
|
pageSize={pageSize}
|
||||||
|
headerActions={headerActions}
|
||||||
|
rowActions={rowActions}
|
||||||
|
refreshKey={refreshKey}
|
||||||
|
sortBy="instance_id"
|
||||||
|
sortDir="desc"
|
||||||
|
initialFilters={initialFilters}
|
||||||
|
presetAlias={presetAlias}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -5,3 +5,4 @@ export type { WiredRecordViewProps } from './OrdersView';
|
|||||||
export { CallsView } from './CallsView';
|
export { CallsView } from './CallsView';
|
||||||
export { StoresView } from './StoresView';
|
export { StoresView } from './StoresView';
|
||||||
export { DailyLogsView } from './DailyLogsView';
|
export { DailyLogsView } from './DailyLogsView';
|
||||||
|
export { UsersView } from './UsersView';
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { MyDailyLogsPage } from './screens/MyDailyLogsPage'
|
|||||||
import { DailySalesReportPage } from './screens/DailySalesReportPage'
|
import { DailySalesReportPage } from './screens/DailySalesReportPage'
|
||||||
import { ReportPage } from './screens/ReportPage'
|
import { ReportPage } from './screens/ReportPage'
|
||||||
import { DatasetsPage } from './screens/admin/DatasetsPage'
|
import { DatasetsPage } from './screens/admin/DatasetsPage'
|
||||||
|
import { UsersPage } from './screens/admin/UsersPage'
|
||||||
|
|
||||||
export const routeConfig = [
|
export const routeConfig = [
|
||||||
// Sales Officer
|
// Sales Officer
|
||||||
@ -104,4 +105,9 @@ export const routeConfig = [
|
|||||||
element: <DatasetsPage />,
|
element: <DatasetsPage />,
|
||||||
adminOnly: true,
|
adminOnly: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/admin/users",
|
||||||
|
element: <UsersPage />,
|
||||||
|
adminOnly: true,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { NavLink, Navigate, Outlet, useNavigate, useLocation } from 'react-router-dom';
|
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 { cn } from '../lib/cn';
|
||||||
import { useAuth } from '../auth/context';
|
import { useAuth } from '../auth/context';
|
||||||
import { onAuthErrorAll, orderBookingClient } from '../api/clients';
|
import { onAuthErrorAll, orderBookingClient } from '../api/clients';
|
||||||
@ -132,6 +132,13 @@ export function ConsoleLayout() {
|
|||||||
<Database size={16} />
|
<Database size={16} />
|
||||||
Manage Datasets
|
Manage Datasets
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
<NavLink
|
||||||
|
to="/admin/users"
|
||||||
|
className={({ isActive }) => 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")}
|
||||||
|
>
|
||||||
|
<Users size={16} />
|
||||||
|
Manage Users
|
||||||
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="px-2">
|
<div className="px-2">
|
||||||
|
|||||||
64
src/screens/admin/UsersPage.tsx
Normal file
64
src/screens/admin/UsersPage.tsx
Normal file
@ -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 (
|
||||||
|
<>
|
||||||
|
<UsersView
|
||||||
|
refreshKey={refreshKey}
|
||||||
|
headerActions={
|
||||||
|
<Button size="sm" iconLeft={<Plus size={14} />} onClick={() => setIsCreating(true)}>
|
||||||
|
Add Sales Officer
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
rowActions={(row) => (
|
||||||
|
<Button size="sm" variant="outline" iconLeft={<Edit size={14} />} onClick={() => handleEditRow(row)}>
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
open={isCreating || activeActivity !== null}
|
||||||
|
onClose={() => {
|
||||||
|
setIsCreating(false);
|
||||||
|
setActiveActivity(null);
|
||||||
|
}}
|
||||||
|
title={activeActivity?.name || "Add User"}
|
||||||
|
>
|
||||||
|
<DynamicForm
|
||||||
|
client={userClient}
|
||||||
|
activityId={activeActivity?.id || USER.activities.ADD_USER.uid}
|
||||||
|
instanceId={activeActivity?.instanceId}
|
||||||
|
onSuccess={() => {
|
||||||
|
setIsCreating(false);
|
||||||
|
setActiveActivity(null);
|
||||||
|
setRefreshKey(k => k + 1);
|
||||||
|
}}
|
||||||
|
onCancel={() => {
|
||||||
|
setIsCreating(false);
|
||||||
|
setActiveActivity(null);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user