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.

200
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`
- **Stack:** React 19 · TypeScript · Vite 8 · Tailwind v4 · react-router v7 · lucide-react
## Version
**1.0.2**
## Getting started
## Sitemap
```bash
npm install
npm run dev # http://localhost:5173
### Public Routes
- `/login` - User Authentication
### 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
);
```
Sign in with your sandbox credentials (email / password, optional org id). The JWT is stored in `localStorage` and shared across all three workflow clients.
Other scripts:
```bash
npm run build # tsc -b && vite build → dist/
npm run preview # serve the production build
npm run lint # eslint
### 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.
## Workflows (app 434)
### 4. Workflows Deep Dive & Activity Chains
The app has three workflows, each with its own `workflow_uuid` (shared `APP_ID` 434). One `ZinoClient` is bound per workflow.
#### 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`
| Slug (`config.ts`) | UI | Record views | Detail views |
| --- | --- | --- | --- |
| `orderBooking` | Order Booking | Orders, Calls | Orders, Calls |
| `store` | Store | Store | Store |
| `dailyReports` | Daily Reports | Daily Logs | Daily Logs |
- **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)
All ids live in `src/api/config.ts`, sourced from the API docs in `src/docs/api/`.
#### 4.2 Store Workflow
Manages onboarding and updating retailers in the system.
**Activity Chain:**
`INIT` ➔ `EDIT_STORE`
## Structure
- **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.
```
src/
api/
client.ts ZinoClient — HTTP client (login, views, activities, upload)
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)
```
#### 4.3 Daily Reports Workflow
Tracks Sales Officer attendance, daily planning, and end-of-day reports.
**Activity Chain:**
`INIT` (Day Plan) ➔ `PUNCH_OUT` (EOD)
## Routing
- **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)
| Path | Screen |
| --- | --- |
| `/login` | Login gate |
| `/:tab` | Record view (`orders` · `calls` · `stores` · `daily`) |
| `/:tab/:instanceId` | Same list with the detail modal open (deep-linkable) |
#### 4.4 User Management Workflow
Manages Sales Officers and their credentials.
**Activity Chain:**
`ADD_USER` ➔ `EDIT_USER`
Unknown paths redirect to `/orders`. Routes under the console require an authenticated session; a 401 bounces back to `/login`.
- **ADD_USER**
- Fields: `name` (text), `email` (email), `password` (password)
- **EDIT_USER**
- Fields: `name_3` (text), `email_3` (email)
## Design system
### 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`:
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.
- `/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.

View File

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