enh
This commit is contained in:
parent
4199ff7d9b
commit
2759bc1488
4 changed files with 3624 additions and 10 deletions
|
|
@ -6,10 +6,6 @@ import { useRouter } from 'expo-router';
|
||||||
import { tw, useManualRefresh, MyText, MyFlatList, useMarkDataFetchers, REFUND_STATUS, MyTouchableOpacity, theme } from 'common-ui';
|
import { tw, useManualRefresh, MyText, MyFlatList, useMarkDataFetchers, REFUND_STATUS, MyTouchableOpacity, theme } from 'common-ui';
|
||||||
|
|
||||||
import { trpc } from '@/src/trpc-client';
|
import { trpc } from '@/src/trpc-client';
|
||||||
// import RazorpayCheckout from 'react-native-razorpay';
|
|
||||||
import OrderMenu from '@/components/OrderMenu';
|
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import { orderStatusManipulator } from '@/src/lib/string-manipulators';
|
|
||||||
|
|
||||||
// Type definitions
|
// Type definitions
|
||||||
interface OrderItem {
|
interface OrderItem {
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@ import { View, Alert, Platform } from 'react-native';
|
||||||
import { useRouter } from 'expo-router';
|
import { useRouter } from 'expo-router';
|
||||||
import { tw, MyTextInput, LoadingDialog, MyText, MyTouchableOpacity } from 'common-ui';
|
import { tw, MyTextInput, LoadingDialog, MyText, MyTouchableOpacity } from 'common-ui';
|
||||||
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
|
||||||
// import RazorpayCheckout from 'react-native-razorpay';
|
|
||||||
|
|
||||||
import { trpc } from '@/src/trpc-client';
|
import { trpc } from '@/src/trpc-client';
|
||||||
import { useCentralProductStore } from '@/src/store/centralProductStore';
|
import { useCentralProductStore } from '@/src/store/centralProductStore';
|
||||||
import { useCentralSlotStore } from '@/src/store/centralSlotStore';
|
import { useCentralSlotStore } from '@/src/store/centralSlotStore';
|
||||||
|
|
|
||||||
3117
package-lock.json
generated
3117
package-lock.json
generated
File diff suppressed because it is too large
Load diff
511
web-user-ui.md
Normal file
511
web-user-ui.md
Normal file
|
|
@ -0,0 +1,511 @@
|
||||||
|
# Web User UI — TanStack Start App
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Create `apps/web-ui` — a server-rendered web app using TanStack Start, Tailwind CSS, shadcn/ui, and tRPC. It mirrors `apps/user-ui` functionality (auth, products, stores, 1hr delivery, cart, checkout, orders, addresses, coupons, complaints, profile) excluding mobile-only features (notifications, location, haptics, hardware back, status bar) and payment (COD only).
|
||||||
|
|
||||||
|
Create `packages/web-components` — a shared web component library with shadcn primitives and domain components used by the web-ui app.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1: Initialize `packages/web-components`
|
||||||
|
|
||||||
|
### 1.1 Create the package
|
||||||
|
|
||||||
|
```
|
||||||
|
packages/web-components/
|
||||||
|
├── package.json # name: "web-components", private: true
|
||||||
|
├── tsconfig.json
|
||||||
|
├── tailwind.config.ts
|
||||||
|
├── postcss.config.js
|
||||||
|
├── globals.css # Tailwind base + CSS variables from shadcn
|
||||||
|
├── components.json # shadcn config
|
||||||
|
├── src/
|
||||||
|
│ ├── index.ts # barrel export
|
||||||
|
│ ├── lib/
|
||||||
|
│ │ ├── utils.ts # cn() helper (clsx + tailwind-merge)
|
||||||
|
│ │ └── constants.ts # shared constants
|
||||||
|
│ ├── components/
|
||||||
|
│ │ └── ui/ # shadcn primitives (added via CLI)
|
||||||
|
│ ├── hooks/
|
||||||
|
│ └── services/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 Steps
|
||||||
|
|
||||||
|
1. Create `packages/web-components/package.json`:
|
||||||
|
- name: `web-components`
|
||||||
|
- main: `./src/index.ts`
|
||||||
|
- types: `./src/index.ts`
|
||||||
|
- private: true
|
||||||
|
- peerDependencies: `react`, `react-dom`, `tailwindcss`, `class-variance-authority`, `clsx`, `tailwind-merge`, `lucide-react`
|
||||||
|
- devDependencies: `typescript`, `@types/react`, `tailwindcss`, `postcss`, `autoprefixer`
|
||||||
|
|
||||||
|
2. Create `tsconfig.json`:
|
||||||
|
- jsx: `react-jsx`
|
||||||
|
- strict: true
|
||||||
|
- paths: `@/*` -> `./src/*`
|
||||||
|
|
||||||
|
3. Initialize Tailwind:
|
||||||
|
- `npx tailwindcss init` in the package
|
||||||
|
- Configure `content` paths to scan `./src/**/*.{ts,tsx}`
|
||||||
|
|
||||||
|
4. Initialize shadcn:
|
||||||
|
- Run `npx shadcn@latest init` inside `packages/web-components`
|
||||||
|
- Config values:
|
||||||
|
- style: default
|
||||||
|
- base color: neutral / slate
|
||||||
|
- CSS variables: yes
|
||||||
|
- globals.css path: `./globals.css`
|
||||||
|
- components path: `./src/components/ui`
|
||||||
|
- utils path: `./src/lib/utils.ts`
|
||||||
|
- tailwind config: `./tailwind.config.ts`
|
||||||
|
|
||||||
|
5. Install shadcn UI primitives via CLI:
|
||||||
|
- `npx shadcn@latest add button -p .`
|
||||||
|
- `npx shadcn@latest add input -p .`
|
||||||
|
- `npx shadcn@latest add dialog -p .`
|
||||||
|
- `npx shadcn@latest add card -p .`
|
||||||
|
- `npx shadcn@latest add badge -p .`
|
||||||
|
- `npx shadcn@latest add checkbox -p .`
|
||||||
|
- `npx shadcn@latest add sheet -p .`
|
||||||
|
- `npx shadcn@latest add separator -p .`
|
||||||
|
- `npx shadcn@latest add scroll-area -p .`
|
||||||
|
- `npx shadcn@latest add select -p .`
|
||||||
|
- `npx shadcn@latest add tabs -p .`
|
||||||
|
- `npx shadcn@latest add alert-dialog -p .`
|
||||||
|
- `npx shadcn@latest add avatar -p .`
|
||||||
|
|
||||||
|
6. Build domain components in `packages/web-components/src/components/`:
|
||||||
|
|
||||||
|
| Component | Web Implementation | Mirrors from `common-ui` |
|
||||||
|
|-----------|-------------------|--------------------------|
|
||||||
|
| `my-text.tsx` | `<p>`/`<span>` with Tailwind + variant props | text.tsx |
|
||||||
|
| `my-button.tsx` | Wraps shadcn `Button` with color variants | button.tsx |
|
||||||
|
| `my-text-input.tsx` | Wraps shadcn `Input` with label + error | textinput.tsx |
|
||||||
|
| `my-touchable-opacity.tsx` | `<button>` with hover/active/ripple | touchable-opacity.tsx |
|
||||||
|
| `loading-dialog.tsx` | shadcn `Dialog` with spinner | loading-dialog.tsx |
|
||||||
|
| `bottom-dialog.tsx` | shadcn `Sheet` from bottom | dialog.tsx |
|
||||||
|
| `confirmation-dialog.tsx` | shadcn `AlertDialog` | dialog.tsx |
|
||||||
|
| `checkbox.tsx` | Wraps shadcn `Checkbox` | checkbox.tsx |
|
||||||
|
| `search-bar.tsx` | Input with search icon + debounced onChange | search-bar.tsx |
|
||||||
|
| `data-table.tsx` | HTML `<table>` with Tailwind styling | data-table.tsx |
|
||||||
|
| `quantifier.tsx` | Quantity +/- stepper control | quantifier.tsx |
|
||||||
|
| `mini-quantifier.tsx` | Compact quantity stepper | mini-quantifier.tsx |
|
||||||
|
| `image-viewer.tsx` | Full-screen image modal | image-viewer.tsx |
|
||||||
|
| `image-uploader.tsx` | File input → preview grid | ImageUploader.tsx |
|
||||||
|
| `image-uploader-neo.tsx` | File input with usePickImage pattern | ImageUploaderNeo.tsx |
|
||||||
|
| `image-carousel.tsx` | Horizontal scroll with dot indicators | ImageCarousel.tsx |
|
||||||
|
| `profile-image.tsx` | Avatar with fallback initials | profile-image.tsx |
|
||||||
|
| `app-container.tsx` | Max-width centered column layout (scrollable div, no KeyboardAwareScrollView) | app-container.tsx |
|
||||||
|
| `flat-list.tsx` | CSS grid / flexbox layout | flat-list.tsx |
|
||||||
|
| `dropdown.tsx` | shadcn `Select` | dropdown.tsx |
|
||||||
|
| `date-picker.tsx` | Native `<input type="date">` or shadcn popover | date-picker.tsx |
|
||||||
|
| `google-sign-in.tsx` | OAuth PKCE via expo-auth-session equivalent (web flow) | google-sign-in.tsx |
|
||||||
|
|
||||||
|
7. Copy hooks from `packages/ui/hooks/` to `packages/web-components/src/hooks/`:
|
||||||
|
- `theme-context.tsx` — adapt to CSS variable-based theming (remove react-native dependency)
|
||||||
|
- `usePagination.tsx` — strip react-native imports, use pure JS
|
||||||
|
- `useIsDevMode.ts` — use `window.location.hostname` instead of Platform
|
||||||
|
- `useFocusCallback.ts` — use IntersectionObserver or TanStack hooks
|
||||||
|
|
||||||
|
8. Copy services from `packages/ui/src/services/` to `packages/web-components/src/services/`:
|
||||||
|
- `StorageService.ts` — localStorage only (remove SecureStore branch + Platform check)
|
||||||
|
- `StorageServiceCasual.ts` — adapt to localStorage only
|
||||||
|
|
||||||
|
9. Copy lib files from `packages/ui/src/lib/` as needed:
|
||||||
|
- `constants.ts` — shared string constants (export same values)
|
||||||
|
- `theme-colors.ts` — adapt to CSS variable names
|
||||||
|
- `refresh-context.tsx` — strip react-native imports
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2: Initialize `apps/web-ui`
|
||||||
|
|
||||||
|
### 2.1 Bootstrap with TanStack Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd apps
|
||||||
|
mkdir web-ui && cd web-ui
|
||||||
|
npm create tanstack-app@latest . -- --start --tailwind --typescript
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates:
|
||||||
|
- File-based routing in `app/routes/`
|
||||||
|
- SSR enabled via Vinxi
|
||||||
|
- Tailwind CSS configured
|
||||||
|
- TypeScript
|
||||||
|
- TanStack Router with file conventions
|
||||||
|
|
||||||
|
### 2.2 Add additional dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @trpc/client @trpc/react-query @trpc/server @tanstack/react-query zustand axios dayjs formik yup fuse.js jwt-decode clsx tailwind-merge class-variance-authority lucide-react
|
||||||
|
npm install -D @types/react
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 Configure path aliases in `tsconfig.json`
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./app/*"],
|
||||||
|
"web-components": ["../../packages/web-components/src"],
|
||||||
|
"web-components/*": ["../../packages/web-components/src/*"],
|
||||||
|
"@packages/shared": ["../../packages/shared"],
|
||||||
|
"@packages/shared/*": ["../../packages/shared/*"],
|
||||||
|
"@backend/*": ["../../apps/backend/src/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.4 Configure workspace in root `package.json`
|
||||||
|
|
||||||
|
Add `"apps/web-ui"` to the `workspaces` array.
|
||||||
|
|
||||||
|
### 2.5 Add to `turbo.json`
|
||||||
|
|
||||||
|
```json
|
||||||
|
"web-ui#dev": { "cache": false, "dependsOn": ["^build"] },
|
||||||
|
"web-ui#build": { "dependsOn": ["^build"], "outputs": ["dist/**"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.6 Add workspace script to root `package.json`
|
||||||
|
|
||||||
|
```json
|
||||||
|
"web-ui": "bun run --filter web-ui",
|
||||||
|
"web-ui:dev": "bun run web-ui dev",
|
||||||
|
"web-ui:build": "bun run web-ui build"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3: Build Core Infrastructure
|
||||||
|
|
||||||
|
### 3.1 tRPC Client (`app/lib/trpc-client.ts`)
|
||||||
|
|
||||||
|
- Import `AppRouter` type from `@backend/trpc/router` (via path alias)
|
||||||
|
- `createTRPCReact<AppRouter>()`
|
||||||
|
- `httpBatchLink` pointing to backend URL (same as user-ui: `BASE_API_URL + '/api/trpc'`)
|
||||||
|
- Auth token injection: read from localStorage via `StorageService.getAuthToken()`
|
||||||
|
- No superjson (match user-ui's transport)
|
||||||
|
|
||||||
|
### 3.2 Auth Context (`app/lib/auth-context.tsx`)
|
||||||
|
|
||||||
|
Same flow as `user-ui/src/contexts/AuthContext.tsx`:
|
||||||
|
- On mount: check localStorage for stored JWT token
|
||||||
|
- If found: set `isAuthenticated=true`, fetch user data via `trpc.user.user.getSelfData`
|
||||||
|
- Provide: `login`, `loginWithToken`, `register`, `logout`, `updateUser`, `updateUserDetails`
|
||||||
|
- Post-login redirect via stored redirect URL in localStorage
|
||||||
|
- JWT token management: `getAuthToken()`, `saveAuthToken()`, `deleteAuthToken()` using localStorage
|
||||||
|
|
||||||
|
### 3.3 Zustand Stores (`app/lib/stores/`)
|
||||||
|
|
||||||
|
Copy and adapt from `user-ui/src/store/`:
|
||||||
|
|
||||||
|
| Store | File | Adaptations for Web |
|
||||||
|
|-------|------|---------------------|
|
||||||
|
| `addressStore` | `addressStore.ts` | None |
|
||||||
|
| `appStore` | `appStore.ts` | None |
|
||||||
|
| `cartStore` | `cartStore.ts` | None |
|
||||||
|
| `centralProductStore` | `centralProductStore.ts` | Init via loader instead of effect hook |
|
||||||
|
| `centralSlotStore` | `centralSlotStore.ts` | Init via loader instead of effect hook |
|
||||||
|
| `navigationStore` | `navigationStore.ts` | None |
|
||||||
|
| `quickDeliveryStore` | `quickDeliveryStore.ts` | None |
|
||||||
|
| `flashCartStore` | `flashCartStore.ts` | None |
|
||||||
|
| `flashNavigationStore` | `flashNavigationStore.ts` | None |
|
||||||
|
| `slotStore` | `slotStore.ts` | None |
|
||||||
|
| `storeHeaderStore` | `storeHeaderStore.ts` | None |
|
||||||
|
|
||||||
|
### 3.4 Query Client (`app/lib/queryClient.ts`)
|
||||||
|
|
||||||
|
Standard `QueryClient` from `@tanstack/react-query` with same defaults as user-ui.
|
||||||
|
|
||||||
|
### 3.5 API Hooks (`app/lib/prominent-api-hooks.ts`)
|
||||||
|
|
||||||
|
Copy from `user-ui/src/hooks/prominent-api-hooks.ts`:
|
||||||
|
- `useGetEssentialConsts()` — tRPC query with 60s refetch
|
||||||
|
- `useAllProducts()` — Axios get cached products JSON
|
||||||
|
- `useStores()` — Axios get cached stores JSON
|
||||||
|
- `useSlots()` — Axios get cached slots JSON
|
||||||
|
- `useBanners()` — Axios get cached banners JSON
|
||||||
|
- `useStoreWithProducts(storeId)` — Axios get store-specific JSON
|
||||||
|
|
||||||
|
The `BASE_API_URL` pointed to same backend URL.
|
||||||
|
|
||||||
|
### 3.6 Cart Query Hooks (`app/lib/cart-query-hooks.ts`)
|
||||||
|
|
||||||
|
Copy from `user-ui/hooks/cart-query-hooks.tsx`:
|
||||||
|
- `useGetCart(cartType)` — localStorage-based cart
|
||||||
|
- `useAddToCart(cartType)` — add item, invalidate query
|
||||||
|
- `useUpdateCartItem(cartType)` — update quantity
|
||||||
|
- `useRemoveFromCart(cartType)` — remove item
|
||||||
|
- `clearLocalCart(cartType)` — clear all
|
||||||
|
|
||||||
|
### 3.7 Other Hooks (`app/lib/hooks/`)
|
||||||
|
|
||||||
|
| Hook | Source | Notes |
|
||||||
|
|------|--------|-------|
|
||||||
|
| `useJWT.ts` | `user-ui/hooks/useJWT.ts` | Adapt to localStorage instead of SecureStore |
|
||||||
|
| `useCurrentUserId.ts` | `user-ui/hooks/useCurrentUserId.ts` | Decode JWT, same logic |
|
||||||
|
| `useUploadToObjectStore.ts` | `user-ui/hooks/useUploadToObjectStore.ts` | Same presigned URL upload logic |
|
||||||
|
| `useHideTabNav.ts` | `user-ui/src/hooks/useHideTabNav.ts` | Adapt to web scroll-based hiding |
|
||||||
|
|
||||||
|
### 3.8 Shared Types
|
||||||
|
|
||||||
|
Import types from `@packages/shared` directly (already in monorepo).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 4: Build Routes
|
||||||
|
|
||||||
|
### 4.1 Root Layout (`app/routes/__root.tsx`)
|
||||||
|
|
||||||
|
Provider hierarchy:
|
||||||
|
```
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<trpc.Provider client={trpcClient} queryClient={queryClient}>
|
||||||
|
<AuthProvider>
|
||||||
|
<RefreshProvider queryClient={queryClient}>
|
||||||
|
<CentralStoreInitializer>
|
||||||
|
<Outlet />
|
||||||
|
</CentralStoreInitializer>
|
||||||
|
</RefreshProvider>
|
||||||
|
</AuthProvider>
|
||||||
|
<Toast /> ← react-hot-toast or sonner (toast library)
|
||||||
|
</trpc.Provider>
|
||||||
|
</QueryClientProvider>
|
||||||
|
```
|
||||||
|
|
||||||
|
Removed compared to user-ui's root layout:
|
||||||
|
- ❌ MyStatusBar — not needed on web
|
||||||
|
- ❌ SafeAreaProvider/SafeAreaView — use CSS safe area env()
|
||||||
|
- ❌ UpdateChecker — not relevant for web
|
||||||
|
- ❌ HealthTestWrapper — version enforcement not needed
|
||||||
|
- ❌ WebViewWrapper — no webview overlay
|
||||||
|
- ❌ FirstUserWrapper — no first-time user flow
|
||||||
|
- ❌ NotificationProvider + NotifChecker — no push notifications
|
||||||
|
- ❌ PaperProvider — using shadcn instead
|
||||||
|
- ❌ LocationTestWrapper — no location checks
|
||||||
|
- ❌ BackHandlerWrapper — no hardware back button
|
||||||
|
- ❌ ThemeProvider (react-navigation) — use Tailwind dark mode instead
|
||||||
|
|
||||||
|
### 4.2 Route Map (Flattened)
|
||||||
|
|
||||||
|
| Route | Source (user-ui) | Description |
|
||||||
|
|-------|------------------|-------------|
|
||||||
|
| `/` | `app/index.tsx` | Redirect to `/home` |
|
||||||
|
| `/login` | `app/(auth)/login.tsx` | 3-step OTP/password login |
|
||||||
|
| `/register` | `app/(auth)/register.tsx` | Registration form |
|
||||||
|
| `/home` | `app/(drawer)/(tabs)/home/index.tsx` | Dashboard: stores carousel, banners, products grid, search bar, floating cart bar |
|
||||||
|
| `/home/cart` | `app/(drawer)/(tabs)/home/cart.tsx` | Cart page |
|
||||||
|
| `/home/checkout` | `app/(drawer)/(tabs)/home/checkout.tsx` | COD-only checkout with address selector, slot picker, order summary |
|
||||||
|
| `/home/order-success` | `app/(drawer)/(tabs)/home/order-success.tsx` | Order placed success screen |
|
||||||
|
| `/home/product/$id` | `app/(drawer)/(tabs)/home/product-detail/[id].tsx` | Product detail with reviews |
|
||||||
|
| `/home/search` | `app/(drawer)/(tabs)/home/search-results/index.tsx` | Live search with Fuse.js |
|
||||||
|
| `/flash` | `app/(drawer)/(tabs)/flash-delivery/(products)/index.tsx` | 1hr delivery product listing |
|
||||||
|
| `/flash/cart` | `app/(drawer)/(tabs)/flash-delivery/(cart)/cart.tsx` | Flash delivery cart |
|
||||||
|
| `/flash/checkout` | `app/(drawer)/(tabs)/flash-delivery/checkout.tsx` | Flash delivery checkout (COD only) |
|
||||||
|
| `/flash/order-success` | `app/(drawer)/(tabs)/flash-delivery/order-success.tsx` | Flash delivery success |
|
||||||
|
| `/flash/product/$id` | `app/(drawer)/(tabs)/flash-delivery/product-detail/[id].tsx` | Flash product detail |
|
||||||
|
| `/stores` | `app/(drawer)/(tabs)/stores/index.tsx` | Store listing |
|
||||||
|
| `/stores/$storeId` | `app/(drawer)/(tabs)/stores/store-detail/[id].tsx` | Store products with tag filters |
|
||||||
|
| `/stores/$storeId/product/$productId` | `app/(drawer)/(tabs)/stores/store-detail/product-detail/[id].tsx` | Store product detail |
|
||||||
|
| `/me` | `app/(drawer)/(tabs)/me/index.tsx` | Account menu (orders, addresses, coupons, complaints, profile, about) |
|
||||||
|
| `/me/orders` | `app/(drawer)/(tabs)/me/my-orders/index.tsx` | Order list with infinite scroll |
|
||||||
|
| `/me/orders/$id` | `app/(drawer)/(tabs)/me/my-orders/[id].tsx` | Order detail with cancel/notes/complaint |
|
||||||
|
| `/me/addresses` | `app/(drawer)/(tabs)/me/addresses/index.tsx` | Address CRUD |
|
||||||
|
| `/me/coupons` | `app/(drawer)/(tabs)/me/coupons/index.tsx` | Coupon list + redeem |
|
||||||
|
| `/me/complaints` | `app/(drawer)/(tabs)/me/complaints/index.tsx` | Complaint list + raise |
|
||||||
|
| `/me/edit-profile` | `app/(drawer)/(tabs)/me/edit-profile/index.tsx` | Edit profile |
|
||||||
|
| `/me/about` | `app/(drawer)/(tabs)/me/about/index.tsx` | About page |
|
||||||
|
| `/me/terms` | `app/(drawer)/(tabs)/me/terms/index.tsx` | Terms & Conditions |
|
||||||
|
|
||||||
|
### 4.3 Navigation Layout (`app/components/nav-layout.tsx`)
|
||||||
|
|
||||||
|
Bottom navigation bar:
|
||||||
|
- Home, Stores, Me tabs
|
||||||
|
- 1hr Delivery as a prominent center button (matching mobile's FAB style)
|
||||||
|
- Active state highlighting
|
||||||
|
- Responsive: bottom bar on mobile, sidebar on desktop (optional enhancement)
|
||||||
|
|
||||||
|
Wraps all /home, /flash, /stores, /me routes.
|
||||||
|
|
||||||
|
### 4.4 Page Components (common-ui → web-components migration)
|
||||||
|
|
||||||
|
Rebuild in `app/components/` using web-components:
|
||||||
|
|
||||||
|
| Component | Replaces from user-ui |
|
||||||
|
|-----------|-----------------------|
|
||||||
|
| `ProductCard.tsx` | Card with image, name, price, add-to-cart |
|
||||||
|
| `CartPage.tsx` | Cart items with quantifiers, totals, proceed to checkout |
|
||||||
|
| `CheckoutPage.tsx` | Address selector, slot picker, order summary, place order button (COD only) |
|
||||||
|
| `CheckoutAddressSelector.tsx` | Address selection in checkout |
|
||||||
|
| `AddressForm.tsx` | Formik + yup address form |
|
||||||
|
| `ProductDetail.tsx` | Full product detail with reviews |
|
||||||
|
| `SlotProducts.tsx` | Slot-specific product grid |
|
||||||
|
| `StoreProducts.tsx` | Store product grid with tag filters |
|
||||||
|
| `OrderMenu.tsx` | Order actions (cancel, notes, complaint) |
|
||||||
|
| `BannerCarousel.tsx` | Image banners carousel |
|
||||||
|
| `FloatingCartBar.tsx` | Sticky bottom cart summary bar |
|
||||||
|
| `TermsAndConditionsContent.tsx` | Static terms content |
|
||||||
|
| `RegistrationForm.tsx` | Registration form fields |
|
||||||
|
| `NextOrderGlimpse.tsx` | Next upcoming order summary |
|
||||||
|
| `TabLayoutWrapper.tsx` | Wrapper for screen with optional tabs |
|
||||||
|
| `FlashDeliveryProducts.tsx` | Flash-eligible products list |
|
||||||
|
| `UserAddressHeader.tsx` | Collapsed address display |
|
||||||
|
|
||||||
|
### 4.5 Payment — Omitted Entirely
|
||||||
|
|
||||||
|
- No Razorpay integration
|
||||||
|
- No PhonePe integration
|
||||||
|
- Checkout defaults to COD without a payment method selector
|
||||||
|
- Order status: placed directly without payment flow
|
||||||
|
- `placeOrder` mutation sends `paymentMethod: 'cod'` always
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 5: Wrappers Removed (vs user-ui)
|
||||||
|
|
||||||
|
| user-ui Wrapper | Reason Removed |
|
||||||
|
|----------------|---------------|
|
||||||
|
| ThemeProvider (react-navigation) | Using Tailwind CSS variables + dark mode |
|
||||||
|
| SafeAreaProvider / SafeAreaView | Handled by CSS `env(safe-area-inset-*)` |
|
||||||
|
| MyStatusBar | No status bar on web |
|
||||||
|
| UpdateChecker (expo-updates) | Not applicable to web |
|
||||||
|
| HealthTestWrapper | Version enforcement not needed |
|
||||||
|
| WebViewWrapper | No webview overlay needed |
|
||||||
|
| FirstUserWrapper | No first-time user flow needed |
|
||||||
|
| NotificationProvider | No push notifications on web |
|
||||||
|
| NotifChecker | No push token registration |
|
||||||
|
| PaperProvider | Using shadcn/ui instead |
|
||||||
|
| LocationTestWrapper | No location checks |
|
||||||
|
| BackHandlerWrapper | No hardware back button on web |
|
||||||
|
| CentralStoreInitializer | **KEPT** — needed for product/slot store hydration |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 6: tRPC Integration
|
||||||
|
|
||||||
|
### 6.1 Client Setup
|
||||||
|
|
||||||
|
Direct `httpBatchLink` to the existing backend URL. No proxy needed — the backend CORS config already allows web origins (currently allows `localhost:5174` and `ui.freshyo.in`; add web-ui dev URL).
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const trpcClient = trpc.createClient({
|
||||||
|
links: [
|
||||||
|
httpBatchLink({
|
||||||
|
url: `${BASE_API_URL}/api/trpc`,
|
||||||
|
headers: async () => {
|
||||||
|
const token = await getAuthToken()
|
||||||
|
return token ? { Authorization: `Bearer ${token}` } : {}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 Used Procedures (same as user-ui)
|
||||||
|
|
||||||
|
| Category | Procedures |
|
||||||
|
|----------|-----------|
|
||||||
|
| Auth | `trpc.user.auth.login`, `.sendOtp`, `.verifyOtp`, `.register`, `.updateProfile`, `.deleteAccount` |
|
||||||
|
| User | `trpc.user.user.getSelfData`, `.checkProfileComplete` |
|
||||||
|
| Address | `trpc.user.address.getUserAddresses`, `.getDefaultAddress`, `.createAddress`, `.updateAddress`, `.deleteAddress` |
|
||||||
|
| Order | `trpc.user.order.getOrders`, `.getOrderById`, `.getRecentlyOrderedProducts`, `.placeOrder`, `.cancelOrder`, `.updateUserNotes` |
|
||||||
|
| Product | `trpc.user.product.getProductDetails`, `.getProductReviews`, `.createReview` |
|
||||||
|
| Coupon | `trpc.user.coupon.getEligible`, `.getMyCoupons`, `.redeemReservedCoupon` |
|
||||||
|
| Complaint | `trpc.user.complaint.getAll`, `.raise` |
|
||||||
|
| Cart | `trpc.user.cart.getCartSlots` |
|
||||||
|
| File | `trpc.user.fileUpload.generateUploadUrls`, `trpc.common.generateUploadUrls` |
|
||||||
|
| Common | `trpc.common.healthCheck`, `.essentialConsts`, `.getStoresSummary` |
|
||||||
|
|
||||||
|
### 6.3 Caching Strategy
|
||||||
|
|
||||||
|
Same as user-ui:
|
||||||
|
- Products, stores, slots, banners: fetched via Axios from cached JSON files on CDN/assets domain
|
||||||
|
- tRPC used for dynamic data (auth, orders, addresses, etc.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 7: Implementation Order
|
||||||
|
|
||||||
|
| Step | Task | Depends On |
|
||||||
|
|------|------|-----------|
|
||||||
|
| 1 | Create `packages/web-components/` structure, package.json, tsconfig | — |
|
||||||
|
| 2 | Initialize Tailwind + shadcn CLI in web-components | 1 |
|
||||||
|
| 3 | Install shadcn primitives (button, input, dialog, etc.) | 2 |
|
||||||
|
| 4 | Build domain components in web-components | 3 |
|
||||||
|
| 5 | Copy hooks + services to web-components | 1 |
|
||||||
|
| 6 | Bootstrap `apps/web-ui` with TanStack Start | — |
|
||||||
|
| 7 | Add dependencies, configure tsconfig, workspace, turbo | 6 |
|
||||||
|
| 8 | Build tRPC client, auth context, zustand stores | 7 |
|
||||||
|
| 9 | Build query client, API hooks, cart hooks | 7 |
|
||||||
|
| 10 | Build root layout with all providers | 8 |
|
||||||
|
| 11 | Build login + register routes | 8, 9 |
|
||||||
|
| 12 | Build home dashboard route | 9, 4 |
|
||||||
|
| 13 | Build 1hr delivery (flash) routes | 9, 4 |
|
||||||
|
| 14 | Build stores routes | 9, 4 |
|
||||||
|
| 15 | Build cart + checkout (COD only) | 9, 4 |
|
||||||
|
| 16 | Build me section (orders, addresses, coupons, complaints, profile) | 8, 9 |
|
||||||
|
| 17 | Build about + terms routes | 8 |
|
||||||
|
| 18 | Add navigation layout (bottom bar) | 10 |
|
||||||
|
| 19 | Test end-to-end with existing backend | all |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files That Don't Need Migration
|
||||||
|
|
||||||
|
| user-ui File | Reason Not Needed |
|
||||||
|
|-------------|------------------|
|
||||||
|
| `services/notif-service/` | Push notifications not supported on web |
|
||||||
|
| `services/toaster.ts` | Replace with sonner/react-hot-toast |
|
||||||
|
| `src/components/LocationAttacher.tsx` | No location attach on web |
|
||||||
|
| `components/LocationTestWrapper.tsx` | No location checks |
|
||||||
|
| `components/HealthTestWrapper.tsx` | No version enforcement |
|
||||||
|
| `components/WebViewWrapper.tsx` | No webview overlay |
|
||||||
|
| `components/UpdateChecker.tsx` | No expo-updates |
|
||||||
|
| `components/BackHandler.tsx` | No hardware back |
|
||||||
|
| `components/FirstUserWrapper.tsx` | No first-time user splash |
|
||||||
|
| `app/api/auth/` | OAuth handled differently on web (no expo-router API routes needed) |
|
||||||
|
| `src/components/MyStatusBar.tsx` | No status bar concepts on web |
|
||||||
|
| `eas.json`, `app.json`, `expo-env.d.ts` | Expo-specific config files |
|
||||||
|
| `metro.config.js` | Metro not used; TanStack uses Vite |
|
||||||
|
| `assets/` | Static assets handled differently in web builds |
|
||||||
|
| `google-services.json` | Android-specific |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## State Flow Notes
|
||||||
|
|
||||||
|
### Auth Flow (same as user-ui)
|
||||||
|
|
||||||
|
1. App mounts → `AuthProvider.init()` checks localStorage for JWT
|
||||||
|
2. If token found: fetch user data via `trpc.user.user.getSelfData`
|
||||||
|
3. Login: OTP or password → receive token → store in localStorage → set auth state
|
||||||
|
4. Registration: collect details → register via tRPC → auto-login
|
||||||
|
5. Logout: clear localStorage → reset auth state → redirect to `/login`
|
||||||
|
|
||||||
|
### Cart Flow (same as user-ui)
|
||||||
|
|
||||||
|
- Cart stored in localStorage (`StorageServiceCasual`)
|
||||||
|
- Uses TanStack Query with key `local-cart-{cartType}`
|
||||||
|
- Add/update/remove operations invalidate query to trigger re-render
|
||||||
|
- No server-side cart — purely local
|
||||||
|
|
||||||
|
### Checkout Flow (COD only, no payment)
|
||||||
|
|
||||||
|
1. User selects address (from saved addresses or adds new)
|
||||||
|
2. User selects delivery slot per store
|
||||||
|
3. User reviews order summary (items, total, delivery charge)
|
||||||
|
4. User clicks "Place Order" → `trpc.user.order.placeOrder` with `paymentMethod: 'cod'`
|
||||||
|
5. On success → clear cart → navigate to order-success page with order ID
|
||||||
|
|
||||||
|
### Product/Slot Caching (same as user-ui)
|
||||||
|
|
||||||
|
- Products, stores, slots cached JSON fetched via Axios from CDN
|
||||||
|
- `centralProductStore` keeps all products in memory with `productsById` map
|
||||||
|
- `centralSlotStore` keeps slots with per-product availability
|
||||||
|
- Refetch functions stored in stores, triggered by pull-to-refresh
|
||||||
Loading…
Add table
Reference in a new issue