diff --git a/.gitignore b/.gitignore index 5c912f2..3e00084 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,5 @@ dist-ssr *.sw? # vite-plugin-pwa dev output -dev-dist \ No newline at end of file +dev-dist +.agents/ diff --git a/README.md b/README.md index 58289b1..22c21f6 100644 --- a/README.md +++ b/README.md @@ -40,3 +40,127 @@ These routes require the user to be logged in. Roles determine access. - `/admin/datasets` - Manage Datasets (Admin Only) - `/admin/datasets/:id` - Manage Dataset Items (Admin Only) +## Architecture & Workflows (How Things Work) + +This app interfaces with a generic backend platform via `ZinoClient` (see `src/api/client.ts`). The business logic is driven by configuring Workflows, Activities, and Views. + +### 1. Workflows +The app is divided into three primary Workflows, mapped out in `src/api/config.ts`: +- **ORDER_BOOKING**: Manages everything from logging a visit, taking an order, to closing an order. +- **STORE**: Manages the creation and editing of stores/retailers. +- **DAILY_REPORTS**: Manages the Sales Officer's daily punch-in (Day Plan) and punch-out (End of Day report). + +Each workflow has a unique `workflowUuid` and `versionUuid`. + +### 2. How to Add or Start a New Instance +When a user submits a form to create something new (e.g., adding a new Store or starting a new Daily Log), the app "starts an instance" of that specific workflow. + +1. **Instantiate the Client**: A `ZinoClient` is created for the specific workflow. + ```typescript + const client = new ZinoClient(BASE_URL, STORE.workflowUuid); + ``` +2. **Start the Instance**: The app calls the API to begin the first activity (e.g., `INIT` activity) with the form payload. + ```typescript + const res = await client.startInstance( + STORE.activities.INIT.uid, + payload, + { version_uuid: STORE.versionUuid } + ); + ``` +3. **Progressing the Instance**: If the workflow has multiple steps (like Order Booking, which progresses from `LOG_VISIT` to `PLACE_ORDER` to `CLOSE_ORDER`), subsequent updates use `progressInstance` with the existing `instance_id`. + ```typescript + const res = await client.progressInstance( + instanceId, + ORDER_BOOKING.activities.PLACE_ORDER.uid, + payload + ); + ``` + +### 3. Views (Listing Data) +To display lists of data (like the tables on the `/orders` or `/stores` pages), the app queries "Record Views" configured on the backend. +```typescript +const data = await client.listInstances(STORE.recordViews.STORE, filters); +``` +These views return tabular data containing the latest state of all instances in that workflow. + +### 4. Workflows Deep Dive & Activity Chains + +#### 4.1 Order Booking Workflow +Manages the end-to-end process of visiting a store and taking an order. +**Activity Chain:** +`LOG_VISIT` ➔ `PLACE_ORDER` ➔ `EDIT_ORDER` ➔ `PRODUCTIVITY_OF_VISIT` ➔ `POTENTIAL_MINING` ➔ `CLOSE_ORDER` + +- **LOG_VISIT (Init)** + - Fields: `select_store` (wf_lookup), `date_of_visit` (date), `time_of_visit` (time), `upload_image` (image) +- **PLACE_ORDER** + - Fields: `date_of_order` (date), `order_details` (grid), `total_bags` (number), `total_kgs` (number) +- **EDIT_ORDER** + - Fields: Same as Place Order, allowing modifications before closure. +- **PRODUCTIVITY_OF_VISIT** + - Fields: `is_productive_call` (radio), `store_status` (select), `order_received_channel` (select), `remarks` (longtext), `action` (radio) +- **POTENTIAL_MINING** + - Fields: `potential` (grid - tracks actual potential vs ordered) +- **CLOSE_ORDER** + - Fields: `remarks_2` (longtext) + +#### 4.2 Store Workflow +Manages onboarding and updating retailers in the system. +**Activity Chain:** +`INIT` ➔ `EDIT_STORE` + +- **INIT (Store Creation)** + - Fields: `business_name_2` (text), `area_2` (text), `complete_address_2` (longtext), `pin_code_2` (number), `owner_name_2` (text), `phone_number_2` (phone), `email_2` (email), `store_location_2` (geolocation), `store_image_2` (image), `route_name_2` (select), `route_code_2` (select), `sub_route_2` (select), `distributor_name_2` (select), `distributor_owner_name_2` (text), `distributor_email_2` (email), `distributor_phone_number_2` (phone), `notes_2` (longtext), `potential_2` (grid) +- **EDIT_STORE** + - Fields: Matches the INIT fields (uses `_3` suffix) to allow updates to store metadata. + +#### 4.3 Daily Reports Workflow +Tracks Sales Officer attendance, daily planning, and end-of-day reports. +**Activity Chain:** +`INIT` (Day Plan) ➔ `PUNCH_OUT` (EOD) + +- **INIT (Punch In)** + - Fields: `sales_officer_name` (app_user), `date` (date), `time` (time), `route_code` (select), `sub_route` (select), `store_image` (image), `day_plan_notes` (longtext) +- **PUNCH_OUT (Punch Out)** + - Fields: `total_productive_calls` (number), `total_non_productive_calls` (number), `eod_notes_remarks` (longtext) + +#### 4.4 User Management Workflow +Manages Sales Officers and their credentials. +**Activity Chain:** +`ADD_USER` ➔ `EDIT_USER` + +- **ADD_USER** + - Fields: `name` (text), `email` (email), `password` (password) +- **EDIT_USER** + - Fields: `name_3` (text), `email_3` (email) + +### 5. Custom Pipelines +The app uses backend pipelines (custom API endpoints) to fetch aggregated data that standard Record Views cannot provide. These are defined in `src/api/config.ts`: + +- `/api/p/krishna-group/v1/nearest-stores`: Fetches stores near the user's geolocation for logging visits. **(Used in: `NearestStoresMap.tsx`)** +- `/api/p/krishna-group/v1/daily-sales-report`: Aggregates the daily sales report for managers. **(Used in: `DailySalesReportPage.tsx` via `client.ts`)** +- `/api/p/krishna-group/v1/productive-call-summary`: Summarizes productive vs non-productive calls. **(Used in: `DynamicForm.tsx` during order workflows)** +- `/api/p/krishna-group/v1/sales-officers`: Retrieves a list of Sales Officers for filtering. **(Used in: `DailySalesReportPage.tsx`)** +- `/api/p/krishna-group/v1/auto-close-orders`: Batch process to close pending orders. **(Defined in config, executed via backend CRON or specific admin actions)** +- `/api/p/krishna-group/v1/edit-orders`: Pipeline to handle order modifications. **(Configured for future implementation)** +- `/api/p/krishna-group/v1/orders`: Fetches order summaries. **(Configured for future implementation)** +- `/api/p/krishna-group/v1/potential-mining`: Retrieves potential data for stores to pre-fill the smart grid. **(Used in: `DynamicForm.tsx`, `CallDetail.tsx`, `OrdersPage.tsx`, `CallsPage.tsx`)** + +### 6. Admin Pages + +The application includes two dedicated administrative panels restricted to users with the Admin role (and sometimes Manager). + +- **Manage Datasets (`/admin/datasets`)**: + Connects to Zino's dataset API via `client.getDatasets()`. It dynamically renders a dual-pane UI where admins can select a dataset (either a `List` or a `Table` of objects) from the left sidebar and manage its items on the right via `DatasetItemsPage.tsx`. This allows admins to update core master data (like product lists, route configurations) without deploying new code. + +- **Manage Users (`/admin/users`)**: + Operates on the `USER` workflow (`ADD_USER`, `EDIT_USER` activities) to onboard new Sales Officers and edit their profiles or access roles, using standard workflow instances. + +### 7. Daily Sales Report (DSR) + +The **Daily Sales Report (`/sales-report`)** page provides managers and administrators with a consolidated view of all orders booked on a specific day. + +#### How it works: +- **Filtering**: The user selects a specific Date, Route, Distributor, and Sales Officer. +- **Pipeline Aggregation**: Instead of relying on raw workflow lists, it queries a custom pipeline (`/api/p/krishna-group/v1/daily-sales-report`) that heavily aggregates order data from the backend, matching the selected filters. +- **PDF Generation**: It uses `jspdf` and `jspdf-autotable` to generate a dynamic, printable PDF in landscape mode. +- **SKU Chunking**: Because a store might order many different SKUs across multiple brands, the PDF logic dynamically splits columns (SKUs) into printable chunks (`PDF_MAX_SKUS_PER_CHUNK` defined in config) so that tables never overflow the page horizontally. It generates subsequent pages for overflowed SKUs automatically. diff --git a/src/components/forms/DynamicForm.tsx b/src/components/forms/DynamicForm.tsx index b610102..d3901e1 100644 --- a/src/components/forms/DynamicForm.tsx +++ b/src/components/forms/DynamicForm.tsx @@ -781,7 +781,6 @@ export function DynamicForm({ client, activityId: initialActivityId, instanceId: onChange={(newVal) => handleFieldChange(f.id, newVal)} isActivityPotentialMining={currentActivityId === ORDER_BOOKING.activities.POTENTIAL_MINING.uid} required={f.mandatory} - error={!!backendErrors[f.id]} /> ); } diff --git a/src/components/forms/fields/SmartGridField.tsx b/src/components/forms/fields/SmartGridField.tsx index 3b02d19..912901f 100644 --- a/src/components/forms/fields/SmartGridField.tsx +++ b/src/components/forms/fields/SmartGridField.tsx @@ -12,7 +12,6 @@ export function SmartGridField({ onChange, isActivityPotentialMining = false, required = false, - error = false, }: { label: string; columns: FormScreenField[]; @@ -20,7 +19,6 @@ export function SmartGridField({ onChange: (val: Record[]) => void; isActivityPotentialMining?: boolean; required?: boolean; - error?: boolean; }) { const isPotentialMining = isActivityPotentialMining; const formRef = useRef(null); diff --git a/src/components/forms/fields/TextField.tsx b/src/components/forms/fields/TextField.tsx index 1c21f4e..1a839ec 100644 --- a/src/components/forms/fields/TextField.tsx +++ b/src/components/forms/fields/TextField.tsx @@ -1,4 +1,3 @@ -import { useState } from 'react'; import { Input } from '../../reusable/Input'; export function TextField({ @@ -18,8 +17,6 @@ export function TextField({ disabled?: boolean; readOnly?: boolean; }) { - const [touched, setTouched] = useState(false); - const isPincode = label.toLowerCase().replace(/\s/g, '') === 'pincode'; return ( @@ -36,7 +33,6 @@ export function TextField({ } onChange(val); }} - onBlur={() => setTouched(true)} disabled={disabled} readOnly={readOnly} pattern={isPincode ? '^\\d{6}$' : undefined} @@ -45,3 +41,4 @@ export function TextField({ ); } +