fix: build errors and error handling refactored
This commit is contained in:
parent
13160aecb4
commit
a04d469b61
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,3 +25,4 @@ dist-ssr
|
|||||||
|
|
||||||
# vite-plugin-pwa dev output
|
# vite-plugin-pwa dev output
|
||||||
dev-dist
|
dev-dist
|
||||||
|
.agents/
|
||||||
|
|||||||
124
README.md
124
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` - Manage Datasets (Admin Only)
|
||||||
- `/admin/datasets/:id` - Manage Dataset Items (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.
|
||||||
|
|||||||
@ -781,7 +781,6 @@ export function DynamicForm({ client, activityId: initialActivityId, instanceId:
|
|||||||
onChange={(newVal) => handleFieldChange(f.id, newVal)}
|
onChange={(newVal) => handleFieldChange(f.id, newVal)}
|
||||||
isActivityPotentialMining={currentActivityId === ORDER_BOOKING.activities.POTENTIAL_MINING.uid}
|
isActivityPotentialMining={currentActivityId === ORDER_BOOKING.activities.POTENTIAL_MINING.uid}
|
||||||
required={f.mandatory}
|
required={f.mandatory}
|
||||||
error={!!backendErrors[f.id]}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,6 @@ export function SmartGridField({
|
|||||||
onChange,
|
onChange,
|
||||||
isActivityPotentialMining = false,
|
isActivityPotentialMining = false,
|
||||||
required = false,
|
required = false,
|
||||||
error = false,
|
|
||||||
}: {
|
}: {
|
||||||
label: string;
|
label: string;
|
||||||
columns: FormScreenField[];
|
columns: FormScreenField[];
|
||||||
@ -20,7 +19,6 @@ export function SmartGridField({
|
|||||||
onChange: (val: Record<string, unknown>[]) => void;
|
onChange: (val: Record<string, unknown>[]) => void;
|
||||||
isActivityPotentialMining?: boolean;
|
isActivityPotentialMining?: boolean;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
error?: boolean;
|
|
||||||
}) {
|
}) {
|
||||||
const isPotentialMining = isActivityPotentialMining;
|
const isPotentialMining = isActivityPotentialMining;
|
||||||
const formRef = useRef<HTMLDivElement>(null);
|
const formRef = useRef<HTMLDivElement>(null);
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
import { Input } from '../../reusable/Input';
|
import { Input } from '../../reusable/Input';
|
||||||
|
|
||||||
export function TextField({
|
export function TextField({
|
||||||
@ -18,8 +17,6 @@ export function TextField({
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
readOnly?: boolean;
|
readOnly?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const [touched, setTouched] = useState(false);
|
|
||||||
|
|
||||||
const isPincode = label.toLowerCase().replace(/\s/g, '') === 'pincode';
|
const isPincode = label.toLowerCase().replace(/\s/g, '') === 'pincode';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -36,7 +33,6 @@ export function TextField({
|
|||||||
}
|
}
|
||||||
onChange(val);
|
onChange(val);
|
||||||
}}
|
}}
|
||||||
onBlur={() => setTouched(true)}
|
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
pattern={isPincode ? '^\\d{6}$' : undefined}
|
pattern={isPincode ? '^\\d{6}$' : undefined}
|
||||||
@ -45,3 +41,4 @@ export function TextField({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user