fix: form email validation

This commit is contained in:
suryac 2026-10-05 13:21:06 +05:30
parent 34573cf3ab
commit 1d57bf042f
3 changed files with 161 additions and 51 deletions

View File

@ -0,0 +1,10 @@
---
name: build-after-changes
description: Always run the build command and verify it passes after making any code changes.
---
# Build on Change
Whenever you finish making code changes to the project:
1. You MUST automatically run the build command (e.g., `npm run build` or `npm run typecheck` depending on the project configuration) to verify that the changes do not introduce any compilation or type errors.
2. If the build command fails, you MUST investigate and fix those build errors before concluding your task or presenting the final code to the user.

198
README.md
View File

@ -1,70 +1,166 @@
# Krishna Sales # Krishna Sales Mobile App
Field-sales console for **Zino sandbox app 434**. A Vite + React + TypeScript SPA that browses the app's three workflows (Order Booking, Store, Daily Reports) over the Zino gateway. This is a Progressive Web App (Pwa) built for the Krishna Sales team.
- **Base URL:** `https://sandbox.getzino.in` ## Version
- **Stack:** React 19 · TypeScript · Vite 8 · Tailwind v4 · react-router v7 · lucide-react **1.0.2**
## Getting started ## Sitemap
```bash ### Public Routes
npm install - `/login` - User Authentication
npm run dev # http://localhost:5173
### Authenticated Routes (Console)
These routes require the user to be logged in. Roles determine access.
#### Daily Logs
- `/daily` - My Daily Logs (Sales Officer, Manager, Admin)
- `/daily/:instanceId` - View specific daily log
- `/all-daily` - All Daily Logs (Manager, Admin)
- `/all-daily/:instanceId` - View specific daily log from all
#### Orders
- `/orders` - View Orders (Sales Officer, Manager, Admin)
- `/orders/:instanceId` - View specific order
#### Calls
- `/calls` - View Calls (Sales Officer, Manager, Admin)
- `/calls/:instanceId` - View specific call
#### Stores
- `/stores` - View Stores (Sales Officer, Manager, Admin)
- `/stores/:instanceId` - View specific store
#### Reports & Analytics
- `/analytics` - Analytics Dashboard (Sales Officer, Manager, Admin)
- `/sales-report` - Daily Sales Report (Manager, Admin)
#### Admin
- `/admin/users` - Manage Users (Manager, Admin)
- `/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.
Sign in with your sandbox credentials (email / password, optional org id). The JWT is stored in `localStorage` and shared across all three workflow clients. ### 4. Workflows Deep Dive & Activity Chains
Other scripts: #### 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`
```bash - **LOG_VISIT (Init)**
npm run build # tsc -b && vite build → dist/ - Fields: `select_store` (wf_lookup), `date_of_visit` (date), `time_of_visit` (time), `upload_image` (image)
npm run preview # serve the production build - **PLACE_ORDER**
npm run lint # eslint - 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)
## Workflows (app 434) #### 4.2 Store Workflow
Manages onboarding and updating retailers in the system.
**Activity Chain:**
`INIT` ➔ `EDIT_STORE`
The app has three workflows, each with its own `workflow_uuid` (shared `APP_ID` 434). One `ZinoClient` is bound per workflow. - **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.
| Slug (`config.ts`) | UI | Record views | Detail views | #### 4.3 Daily Reports Workflow
| --- | --- | --- | --- | Tracks Sales Officer attendance, daily planning, and end-of-day reports.
| `orderBooking` | Order Booking | Orders, Calls | Orders, Calls | **Activity Chain:**
| `store` | Store | Store | Store | `INIT` (Day Plan) ➔ `PUNCH_OUT` (EOD)
| `dailyReports` | Daily Reports | Daily Logs | Daily Logs |
All ids live in `src/api/config.ts`, sourced from the API docs in `src/docs/api/`. - **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)
## Structure #### 4.4 User Management Workflow
Manages Sales Officers and their credentials.
**Activity Chain:**
`ADD_USER` ➔ `EDIT_USER`
``` - **ADD_USER**
src/ - Fields: `name` (text), `email` (email), `password` (password)
api/ - **EDIT_USER**
client.ts ZinoClient — HTTP client (login, views, activities, upload) - Fields: `name_3` (text), `email_3` (email)
clients.ts per-workflow client singletons + shared auth (loginAll…)
config.ts app 434 ids: APP_ID, workflow uuids, activities, fields, views
types.ts API response types
auth/ AuthProvider + useAuth (JWT session)
components/
buttons/ Button, IconButton
reusable/ Card, Badge, Input, Select, Pagination, Modal, Spinner, EmptyState
rv/ generic RecordView + wired Orders/Calls/Stores/DailyLogs views
dv/ generic DetailView + wired Order/Call/Store/DailyLog details
screens/ LoginPage, ConsoleLayout, WorkflowPage, tab defs
lib/ cn (classnames), format (value formatting)
styles/ Tailwind v4 theme + design tokens
docs/api/ source API docs (one per workflow)
```
## Routing ### 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`:
| Path | Screen | - `/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`)**
| `/login` | Login gate | - `/api/p/krishna-group/v1/productive-call-summary`: Summarizes productive vs non-productive calls. **(Used in: `DynamicForm.tsx` during order workflows)**
| `/:tab` | Record view (`orders` · `calls` · `stores` · `daily`) | - `/api/p/krishna-group/v1/sales-officers`: Retrieves a list of Sales Officers for filtering. **(Used in: `DailySalesReportPage.tsx`)**
| `/:tab/:instanceId` | Same list with the detail modal open (deep-linkable) | - `/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`)**
Unknown paths redirect to `/orders`. Routes under the console require an authenticated session; a 401 bounces back to `/login`. ### 6. Admin Pages
## Design system The application includes two dedicated administrative panels restricted to users with the Admin role (and sometimes Manager).
Tailwind v4 with CSS-variable design tokens (`src/styles/`) — navy brand surface, sunrise accent gradient, slate neutrals. Utility names (`bg-navy-900`, `text-strong`, `shadow-md`, `rounded-lg`) resolve to the tokens via an `@theme` block. Referenced from the lead-to-policy console. - **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.

View File

@ -538,6 +538,10 @@ export function DynamicForm({ client, activityId: initialActivityId, instanceId:
continue; continue;
} }
if (f.data_type === 'email' && (val === '' || val == null)) {
continue;
}
if (val == null) continue; if (val == null) continue;
if (f.data_type === 'number' && typeof val === 'string') { if (f.data_type === 'number' && typeof val === 'string') {