DEAD_CODE_CLEAN #7

Merged
shafi merged 30 commits from DEAD_CODE_CLEAN into master 2026-09-14 04:32:52 +00:00
50 changed files with 260 additions and 180 deletions
Showing only changes of commit b5fd64e5ed - Show all commits

View file

@ -63,7 +63,7 @@ export default function Complaints() {
if (!selectedComplaintId) return;
resolveComplaint.mutate(
{ id: String(selectedComplaintId), response },
{ id: String(selectedComplaintId), response: response ?? '' },
{
onSuccess: () => {
Alert.alert("Success", "Complaint marked as resolved");

View file

@ -94,7 +94,7 @@ export default function AllItemsOrder() {
// Get current order from constants
const { data: constants, isLoading: isLoadingConstants, error: constantsError } = trpc.admin.const.getConstants.useQuery();
const { data: allProducts, isLoading: isLoadingProducts, error: productsError } = trpc.common.product.getAllProductsSummary.useQuery({});
const { data: allProducts, isLoading: isLoadingProducts, error: productsError } = trpc.common.product.getAllProductsSummary.useQuery();
const updateConstants = trpc.admin.const.updateConstants.useMutation();
// Initialize products from constants

View file

@ -34,7 +34,7 @@ type PopularProduct = Omit<
> & {
price: number;
marketPrice: number | null;
images: string[];
images: string[] | null;
storeId: number | null;
nextDeliveryDate: string | null;
};
@ -121,7 +121,7 @@ export default function CustomizePopularItems() {
// Get current popular items from constants
const { data: constants, isLoading: isLoadingConstants, error: constantsError } = trpc.admin.const.getConstants.useQuery();
const { data: allProducts, isLoading: isLoadingProducts, error: productsError } = trpc.common.product.getAllProductsSummary.useQuery({});
const { data: allProducts, isLoading: isLoadingProducts, error: productsError } = trpc.common.product.getAllProductsSummary.useQuery();
const updateConstants = trpc.admin.const.updateConstants.useMutation();
// Initialize popular products from constants

View file

@ -33,6 +33,7 @@ export default function EditProduct() {
shortDescription: '',
longDescription: '',
storeId: 0,
productType: 'item' as const,
variants: [],
}
}

View file

@ -10,7 +10,9 @@ import type { AdminSku } from '@packages/shared';
type FilterType = 'all' | 'in-stock' | 'out-of-stock';
function getDefaultSku(product: { skus: AdminSku[] }): AdminSku | null {
type SerializedAdminSku = Omit<AdminSku, 'createdAt'> & { createdAt: string | Date }
function getDefaultSku(product: { skus: SerializedAdminSku[] }): SerializedAdminSku | null {
return product.skus?.[0] ?? null
}
@ -114,7 +116,6 @@ export default function Products() {
<SearchBar
value={searchTerm}
onChangeText={setSearchTerm}
onSearch={() => {}}
placeholder="Search products..."
containerStyle={tw`mb-0`}
/>

View file

@ -25,7 +25,7 @@ const ProductGroupForm: React.FC<ProductGroupFormProps> = ({
onSuccess,
}) => {
// Fetch products
const { data: productsData } = trpc.common.product.getAllProductsSummary.useQuery({});
const { data: productsData } = trpc.common.product.getAllProductsSummary.useQuery();
const createGroup = trpc.admin.product.createGroup.useMutation();
const updateGroup = trpc.admin.product.updateGroup.useMutation();

View file

@ -45,7 +45,7 @@ export default function ProductsSelector({
selectedGroupIds = [],
onGroupChange,
}: ProductsSelectorProps) {
const { data: skusData } = trpc.common.product.getAllSkusSummary.useQuery({});
const { data: skusData } = trpc.common.product.getAllSkusSummary.useQuery();
const allSkus: SkuSummary[] = skusData?.skus || [];
const [searchQuery, setSearchQuery] = useState('');

View file

@ -182,7 +182,7 @@ const ProductForm = forwardRef<ProductFormRef, ProductFormProps>(({
value: store.id,
})) || []
const { data: skusData } = trpc.common.product.getAllSkusSummary.useQuery({})
const { data: skusData } = trpc.common.product.getAllSkusSummary.useQuery()
const skuOptions = (skusData?.skus || []).map((sku) => ({
label: sku.label,
value: sku.id,

View file

@ -1,4 +1,5 @@
import { Hono } from 'hono'
import type { Context } from 'hono'
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import { trpcServer } from '@hono/trpc-server'
@ -26,7 +27,7 @@ export const createApp = () => {
// tRPC middleware
app.use('/api/trpc/*', trpcServer({
router: appRouter,
createContext: async ({ req, c }) => {
createContext: async ({ req }, c) => {
let user = null
let staffUser = null
const authHeader = req.headers.get('authorization')

View file

@ -1,7 +1,7 @@
import '@/src/lib/notif-job'
import { initializeAllStores } from '@/src/stores/store-initializer'
import { initializeUserNegativityStore } from '@/src/stores/user-negativity-store'
import { startOrderHandler, startCancellationHandler, publishOrder } from '@/src/lib/post-order-handler'
import { publishOrder } from '@/src/lib/post-order-handler'
import { deleteOrders } from '@/src/lib/delete-orders'
import { createAllCacheFiles } from '@/src/lib/cloud_cache'
@ -22,8 +22,6 @@ export const initFunc = async (): Promise<void> => {
await Promise.all([
initializeAllStores(),
initializeUserNegativityStore(),
startOrderHandler(),
startCancellationHandler(),
]);
// Create all cache files after stores are initialized

View file

@ -50,7 +50,7 @@ export const imageUploadS3 = async(body: Buffer, type: string, key: string) => {
headers: {
'Content-Type': type,
},
body,
body: body as unknown as BodyInit,
})
if (!resp.ok) {
const responseBody = await resp.text().catch(() => '')
@ -121,8 +121,8 @@ export async function generateSignedUrlFromS3Url(s3UrlRaw: string|null, expiresI
const url = buildObjectUrl(getS3BucketName(), s3Url)
const signedRequest = await client.sign(url, {
method: 'GET',
signQuery: true,
expires: expiresIn,
headers: { 'X-Amz-Expires': String(expiresIn) },
aws: { signQuery: true },
})
return signedRequest.url
} catch (error) {
@ -173,11 +173,11 @@ export async function generateUploadUrl(key: string, mimeType: string, expiresIn
const url = buildObjectUrl(getS3BucketName(), key)
const signedRequest = await client.sign(url, {
method: 'PUT',
signQuery: true,
expires: expiresIn,
headers: {
'Content-Type': mimeType,
'X-Amz-Expires': String(expiresIn),
},
aws: { signQuery: true },
})
return signedRequest.url

View file

@ -3,7 +3,7 @@
// Re-export database connection
import type { D1Database } from '@cloudflare/workers-types'
import { db, initDb as initDbBase } from 'sqliteService'
import { db, initDb as initDbBase } from '../../../packages/db_helper_sqlite'
export { db }
let dbInitialized = false
@ -15,7 +15,7 @@ export const initDb = (database: D1Database) => {
}
// Re-export all schema exports
export * from 'sqliteService'
export * from '../../../packages/db_helper_sqlite'
// Re-export all helper methods from sqliteService
export {
@ -280,4 +280,4 @@ export {
// Upload URL Helpers
createUploadUrlStatus,
claimUploadUrlStatus,
} from 'sqliteService'
} from '../../../packages/db_helper_sqlite'

View file

@ -59,6 +59,10 @@ export async function scaffoldProducts() {
productType: product.productType || 'item',
isComboOnly: product.isComboOnly,
isOffer: product.isOffer,
price: Number(product.price ?? 0),
marketPrice: product.marketPrice != null ? Number(product.marketPrice) : null,
flashPrice: product.flashPrice != null ? Number(product.flashPrice) : null,
isFlashAvailable: product.isFlashAvailable,
};
})
);

View file

@ -44,7 +44,7 @@
},
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [""], /* Specify multiple folders that act like './node_modules/@types'. */
"typeRoots": ["./node_modules/@types", "../shared-types"],
"typeRoots": ["../../node_modules/@types", "./node_modules/@types", "../shared-types"],
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */

View file

@ -4,7 +4,6 @@ import { trpc } from "../trpc/client";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import dayjs from "dayjs";
import items from "razorpay/dist/types/items";
interface VendorOrder {
orderId: string;

View file

@ -22,7 +22,7 @@ type ProductSummary = Omit<
> & {
isFlashAvailable: boolean
name?: string
flashPrice?: string | null
flashPrice?: number | null
images?: string[] | null
productQuantity?: number
unitNotation?: string

View file

@ -1,7 +1,4 @@
import { hydrateRoot } from 'react-dom/client'
import { StartClient } from '@tanstack/react-start/client'
import { getRouter } from './router'
const router = getRouter()
hydrateRoot(document, <StartClient router={router} />)
hydrateRoot(document, <StartClient />)

View file

@ -42,7 +42,7 @@ export default function AddToCartDialog() {
useEffect(() => {
if (isOpen && product) {
const cartItem = cartData?.items?.find((item: any) => item.productId === product.id)
const cartItem = cartData?.items?.find((item: any) => item.skuId === product.id)
const cartQuantity = cartItem?.quantity || 0
setQuantity(cartQuantity === 0 ? 1 : cartQuantity)
setSelectedSlotId(cartItem?.slotId || null)
@ -75,7 +75,7 @@ export default function AddToCartDialog() {
.filter(Boolean)
.filter((slot) => dayjs(slot.deliveryTime).isAfter(dayjs()))
const cartItem = cartData?.items?.find((item: any) => item.productId === product?.id)
const cartItem = cartData?.items?.find((item: any) => item.skuId === product?.id)
const isUpdate = (cartItem?.quantity || 0) >= 1
const productAvailability = productSlotsMap[product?.id]
@ -89,14 +89,14 @@ export default function AddToCartDialog() {
}
if (isUpdate && cartItem) {
updateItem.mutate(
{ productId: product.id, quantity, slotId: selectedSlotId, deliveryDate: selectedSlotId ? slotMap[selectedSlotId]?.deliveryTime : null },
{ skuId: product.id, quantity, slotId: selectedSlotId, deliveryDate: selectedSlotId ? slotMap[selectedSlotId]?.deliveryTime : null },
{ onSuccess: () => clearAddedToCartProduct() }
)
} else {
const slotId = selectedSlotId ?? availableSlotIds[0] ?? 0
addToCart.mutate(
{
productId: product.id,
skuId: product.id,
quantity,
storeId: product.storeId || 1,
slotId,

View file

@ -1,6 +1,6 @@
import React, { useState } from 'react'
import { trpc } from '../lib/trpc-client'
import { p, pInput, MyButton, div } from 'web-components'
import { p, pInput as PInput, MyButton, div } from 'web-components'
import { MapPin, X, Plus } from 'lucide-react'
import * as Yup from 'yup'
import type { AddressFormProps } from '@packages/shared'
@ -123,7 +123,7 @@ export function AddressForm({ onSuccess, initialValues, isEdit = false }: Addres
<div className="space-y-4">
<div>
<pInput
<PInput
placeholder="Name"
value={values.name}
onChange={(e) => handleChange('name', e.target.value)}
@ -133,7 +133,7 @@ export function AddressForm({ onSuccess, initialValues, isEdit = false }: Addres
</div>
<div>
<pInput
<PInput
placeholder="Phone"
type="tel"
value={values.phone}
@ -144,7 +144,7 @@ export function AddressForm({ onSuccess, initialValues, isEdit = false }: Addres
</div>
<div>
<pInput
<PInput
placeholder="Address Line 1"
value={values.addressLine1}
onChange={(e) => handleChange('addressLine1', e.target.value)}
@ -154,7 +154,7 @@ export function AddressForm({ onSuccess, initialValues, isEdit = false }: Addres
</div>
<div>
<pInput
<PInput
placeholder="Address Line 2 (Optional)"
value={values.addressLine2}
onChange={(e) => handleChange('addressLine2', e.target.value)}
@ -163,7 +163,7 @@ export function AddressForm({ onSuccess, initialValues, isEdit = false }: Addres
<div className="grid grid-cols-2 gap-4">
<div>
<pInput
<PInput
placeholder="City"
value={values.city}
disabled
@ -171,7 +171,7 @@ export function AddressForm({ onSuccess, initialValues, isEdit = false }: Addres
/>
</div>
<div>
<pInput
<PInput
placeholder="State"
value={values.state}
disabled
@ -181,7 +181,7 @@ export function AddressForm({ onSuccess, initialValues, isEdit = false }: Addres
</div>
<div>
<pInput
<PInput
placeholder="Pincode"
value={values.pincode}
disabled
@ -208,7 +208,7 @@ export function AddressForm({ onSuccess, initialValues, isEdit = false }: Addres
3. Click on Share and Click on Copy<br />
4. Paste the copied url here in the field.
</p>
<pInput
<PInput
placeholder="Google Maps Shared URL"
value={values.googleMapsUrl}
onChange={(e) => handleChange('googleMapsUrl', e.target.value)}

View file

@ -55,7 +55,7 @@ export function FloatingCartBar({ isFlashDelivery = false }: FlashDeliveryProps)
// Calculate total cart value
const totalCartValue = cartItems.reduce(
(sum, item) => {
const product = productsById[item.productId]
const product = productsById[item.skuId]
const basePrice = product?.price ?? 0
const price = isFlashDelivery ? (product?.flashPrice ?? basePrice) : basePrice
return sum + price * item.quantity
@ -142,17 +142,17 @@ export function FloatingCartBar({ isFlashDelivery = false }: FlashDeliveryProps)
<React.Fragment key={item.id}>
<div className="flex items-center gap-4 py-4">
<img
src={productsById[item.productId]?.images?.[0]}
src={productsById[item.skuId]?.images?.[0]}
alt=""
className="h-14 w-14 shrink-0 rounded-lg border border-gray-100 bg-gray-50 object-cover"
/>
<div className="min-w-0 flex-1">
<p className="truncate text-sm font-bold text-gray-900">
{productsById[item.productId]?.name || ''}
{productsById[item.skuId]?.name || ''}
</p>
<p className="text-xs font-medium text-gray-500">
{productsById[item.productId]?.productQuantity || 0}
{productsById[item.productId]?.unitNotation || ''}
{productsById[item.skuId]?.productQuantity || 0}
{productsById[item.skuId]?.unitNotation || ''}
</p>
{item.slotId && (
<div className="mt-1 flex items-center gap-1 rounded border border-brand-100 bg-brand-25 px-1.5 py-0.5">
@ -167,17 +167,17 @@ export function FloatingCartBar({ isFlashDelivery = false }: FlashDeliveryProps)
value={quantities[item.id] || item.quantity}
setValue={(value) => {
if (value === 0) {
removeFromCart.mutate(item.productId)
removeFromCart.mutate(item.skuId)
} else {
setQuantities((prev) => ({ ...prev, [item.id]: value }))
updateCartItem.mutate({ productId: item.productId, quantity: value })
updateCartItem.mutate({ skuId: item.skuId, quantity: value })
}
}}
step={productsById[item.productId]?.incrementStep || 1}
step={productsById[item.skuId]?.incrementStep || 1}
/>
<p className="text-[13px] font-bold text-gray-900 sm:text-sm">
{(() => {
const product = productsById[item.productId]
const product = productsById[item.skuId]
const basePrice = product?.price ?? 0
const price = isFlashDelivery ? (product?.flashPrice ?? basePrice) : basePrice
return price * item.quantity

View file

@ -36,7 +36,7 @@ export function PaymentAndOrderComponent({
// Calculate totals
const totalPrice = cartItems.reduce((sum, item) => {
const product = productsById[item.productId]
const product = productsById[item.skuId]
if (!product) return sum
const price = isFlashDelivery ? (product.flashPrice ?? product.price ?? 0) : (product.price || 0)
return sum + price * item.quantity
@ -61,7 +61,7 @@ export function PaymentAndOrderComponent({
navigate({
to: '/home/order-success',
search: { orderId: firstOrder?.id, totalAmount: finalTotalWithDelivery },
search: { orderId: String(firstOrder?.id), totalAmount: String(finalTotalWithDelivery) },
})
},
onError: (error: any) => {
@ -87,7 +87,7 @@ export function PaymentAndOrderComponent({
const orderData = {
selectedItems: cartItems.map((item) => ({
productId: item.productId,
skuId: item.skuId,
quantity: item.quantity,
slotId: isFlashDelivery ? null : item.slotId,
})),

View file

@ -42,7 +42,7 @@ export function ProductCard({
const addToCart = useAddToCart('regular')
// Find current quantity from cart data
const cartItem = cartData?.items?.find((cartItem: any) => cartItem.productId === item.id)
const cartItem = cartData?.items?.find((cartItem: any) => cartItem.skuId === item.id)
const quantity = cartItem?.quantity || 0
// Get slots data from central store
@ -82,7 +82,7 @@ export function ProductCard({
const slot = slotMap[slotId]
const deliveryTime = slot ? dayjs(slot.deliveryTime).format('ddd, DD MMM • h:mm A') : ''
addToCart.mutate(
{ productId: item.id, quantity: 1, slotId, storeId: item.storeId },
{ skuId: item.id, quantity: 1, slotId, storeId: item.storeId },
{
onSuccess: () => {
alert(`Added ${item.name} for delivery at ${deliveryTime}`)
@ -90,7 +90,7 @@ export function ProductCard({
}
)
} else if (cartItem) {
updateCartItem.mutate({ productId: item.id, quantity: newQuantity })
updateCartItem.mutate({ skuId: item.id, quantity: newQuantity })
}
}

View file

@ -5,9 +5,9 @@ type CartType = 'regular' | 'flash'
interface CartItem {
id: number
productId: number
skuId: number
quantity: number
storeId: number
storeId: number | null
addedAt: number
slotId?: number | null
deliveryDate?: string | null
@ -50,20 +50,20 @@ export function useAddToCart(cartType: CartType = 'regular') {
return useMutation({
mutationFn: async ({
productId,
skuId,
quantity,
storeId,
slotId,
deliveryDate,
}: {
productId: number
skuId: number
quantity: number
storeId: number
storeId?: number | null
slotId?: number | null
deliveryDate?: string | null
}) => {
const items = readCart(cartType)
const existing = items.find((i) => i.productId === productId)
const existing = items.find((i) => i.skuId === skuId)
if (existing) {
existing.quantity += quantity
if (slotId) existing.slotId = slotId
@ -71,9 +71,9 @@ export function useAddToCart(cartType: CartType = 'regular') {
} else {
items.push({
id: Date.now(),
productId,
skuId,
quantity,
storeId,
storeId: storeId ?? null,
addedAt: Date.now(),
slotId: slotId ?? null,
deliveryDate: deliveryDate ?? null,
@ -95,22 +95,22 @@ export function useUpdateCartItem(cartType: CartType = 'regular') {
return useMutation({
mutationFn: async ({
productId,
skuId,
quantity,
slotId,
deliveryDate,
}: {
productId: number
skuId: number
quantity: number
slotId?: number | null
deliveryDate?: string | null
}) => {
let items = readCart(cartType)
const existing = items.find((i) => i.productId === productId)
const existing = items.find((i) => i.skuId === skuId)
if (!existing) return
if (quantity <= 0) {
// Quantity hit 0 — remove the item from the cart entirely
items = items.filter((i) => i.productId !== productId)
items = items.filter((i) => i.skuId !== skuId)
} else {
existing.quantity = quantity
if (slotId !== undefined) existing.slotId = slotId
@ -129,9 +129,9 @@ export function useRemoveFromCart(cartType: CartType = 'regular') {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (productId: number) => {
mutationFn: async (skuId: number) => {
let items = readCart(cartType)
items = items.filter((i) => i.productId !== productId)
items = items.filter((i) => i.skuId !== skuId)
writeCart(cartType, items)
queryClient.setQueryData([getCartKey(cartType)], toCartData(items))
},

View file

@ -7,7 +7,7 @@ import type { User as SharedUser, AuthStateCore } from '@packages/shared'
type User = Pick<SharedUser, 'id' | 'email'> &
Partial<Omit<SharedUser, 'id' | 'email' | 'createdAt' | 'mobile'>> & {
mobile: string | null
profileImage: string | null
profileImage?: string | null
}
interface AuthState extends AuthStateCore {

View file

@ -1,19 +1,10 @@
import { create } from 'zustand'
import type { CentralProductState as CentralProductsState } from '@packages/shared'
import type { MergedProduct } from '../../hooks/prominent-api-hooks'
interface Product {
id: number
name: string
price: number
discountedPrice: number | null
images: { uri: string }[]
unit: string
unitValue: number
storeId: number
category: string
isActive: boolean
description: string | null
}
// Central cache holds the API product (MergedProduct) — single product shape
// across the app (numeric pricing, string images).
type Product = MergedProduct
type CentralProductStore = CentralProductsState<Product>

View file

@ -18,11 +18,11 @@ function CartPage() {
const productsById: Record<number, any> = {}
products.forEach((p: any) => { productsById[p.id] = p })
const cartItems = (cart?.items || []).filter((item) => productsById[item.productId])
const cartItems = (cart?.items || []).filter((item) => productsById[item.skuId])
let total = 0
cartItems.forEach((item) => {
const product = productsById[item.productId]
const product = productsById[item.skuId]
if (product) {
total += product.price * item.quantity
}
@ -57,11 +57,11 @@ function CartPage() {
{/* Items */}
<div className="flex flex-col gap-3">
{cartItems.map((item) => {
const product = productsById[item.productId]
const product = productsById[item.skuId]
const price = product.price
return (
<div
key={item.productId}
key={item.skuId}
className="flex items-center gap-4 rounded-xl border border-gray-200 bg-white p-4"
>
<img
@ -77,7 +77,7 @@ function CartPage() {
<div className="mt-2 flex items-center gap-3">
<Quantifier
value={item.quantity}
setValue={(q) => updateItem.mutate({ productId: item.productId, quantity: q })}
setValue={(q) => updateItem.mutate({ skuId: item.skuId, quantity: q })}
/>
<p className="text-sm font-bold text-gray-900">{price}</p>
</div>
@ -85,7 +85,7 @@ function CartPage() {
<div className="flex flex-col items-end gap-2">
<p className="text-sm font-extrabold text-brand-600 sm:text-base">{price * item.quantity}</p>
<button
onClick={() => removeItem.mutate(item.productId)}
onClick={() => removeItem.mutate(item.skuId)}
className="flex h-9 w-9 items-center justify-center rounded-lg text-gray-400 transition-colors hover:bg-red-50 hover:text-red-500"
aria-label={`Remove ${product.name}`}
>

View file

@ -38,7 +38,7 @@ function CheckoutContent() {
const productsById: Record<number, any> = {}
products.forEach((p: any) => { productsById[p.id] = p })
const cartItems = (cartData?.items || []).filter((item) => productsById[item.productId])
const cartItems = (cartData?.items || []).filter((item) => productsById[item.skuId])
// Handle empty cart case
if (cartItems.length === 0) {

View file

@ -14,13 +14,13 @@ function FlashCartPage() {
const removeItem = useRemoveFromCart('flash')
const productsById = useCentralProductStore((s) => s.productsById)
const cartItems = (cart?.items || []).filter((item) => productsById[item.productId])
const cartItems = (cart?.items || []).filter((item) => productsById[item.skuId])
let total = 0
cartItems.forEach((item) => {
const product = productsById[item.productId]
const product = productsById[item.skuId]
if (product) {
total += (product.discountedPrice ?? product.price) * item.quantity
total += product.price * item.quantity
}
})
@ -56,27 +56,27 @@ function FlashCartPage() {
{/* Items */}
<div className="flex flex-col gap-3">
{cartItems.map((item) => {
const product = productsById[item.productId]
const price = product.discountedPrice ?? product.price
const product = productsById[item.skuId]
const price = product.price
return (
<div
key={item.productId}
key={item.skuId}
className="flex items-center gap-4 rounded-xl border border-gray-200 bg-white p-4"
>
<img
src={product.images?.[0]?.uri || product.images?.[0] as any}
src={product.images?.[0] ?? ''}
alt={product.name}
className="h-20 w-20 shrink-0 rounded-lg object-cover"
/>
<div className="min-w-0 flex-1">
<p className="text-sm font-bold text-gray-900">{product.name}</p>
<p className="mt-0.5 text-xs text-gray-500">
{product.unitValue || 1}{product.unit || ''} per unit
{product.unitNotation || ''} per unit
</p>
<div className="mt-2 flex items-center gap-3">
<Quantifier
value={item.quantity}
setValue={(q) => updateItem.mutate({ productId: item.productId, quantity: q })}
setValue={(q) => updateItem.mutate({ skuId: item.skuId, quantity: q })}
/>
<p className="text-sm font-bold text-gray-900">{price}</p>
</div>
@ -84,7 +84,7 @@ function FlashCartPage() {
<div className="flex flex-col items-end gap-2">
<p className="text-base font-extrabold text-flash-500">{price * item.quantity}</p>
<button
onClick={() => removeItem.mutate(item.productId)}
onClick={() => removeItem.mutate(item.skuId)}
className="flex h-9 w-9 items-center justify-center rounded-lg text-gray-400 transition-colors hover:bg-red-50 hover:text-red-500"
aria-label={`Remove ${product.name}`}
>

View file

@ -30,19 +30,19 @@ function FlashCheckoutPage() {
queryClient.invalidateQueries({ queryKey: ['local-cart-flash'] })
navigate({
to: '/flash/order-success',
search: { orderId: order?.id, totalAmount: total },
search: { orderId: String(order?.id), totalAmount: String(total) },
})
},
onSettled: () => setIsLoading(false),
})
const cartItems = (cart?.items || []).filter((item) => productsById[item.productId])
const cartItems = (cart?.items || []).filter((item) => productsById[item.skuId])
let total = 0
cartItems.forEach((item) => {
const product = productsById[item.productId]
const product = productsById[item.skuId]
if (product) {
total += (product.discountedPrice ?? product.price) * item.quantity
total += product.price * item.quantity
}
})
@ -51,7 +51,7 @@ function FlashCheckoutPage() {
setIsLoading(true)
placeOrderMutation.mutate({
selectedItems: cartItems.map((item) => ({
productId: item.productId,
skuId: item.skuId,
quantity: item.quantity,
slotId: null,
})),
@ -95,15 +95,15 @@ function FlashCheckoutPage() {
Order Summary
</p>
{cartItems.map((item) => {
const product = productsById[item.productId]
const product = productsById[item.skuId]
if (!product) return null
return (
<div key={item.productId} className="flex items-center justify-between py-2">
<div key={item.skuId} className="flex items-center justify-between py-2">
<p className="text-sm">
{product.name} x{item.quantity}
</p>
<p className="text-sm font-bold">
{(product.discountedPrice ?? product.price) * item.quantity}
{product.price * item.quantity}
</p>
</div>
)

View file

@ -21,7 +21,7 @@ function FlashProductDetailPage() {
const handleAddToCart = () => {
if (!product) return
addToCart.mutate(
{ productId: product.id, quantity, storeId: product.storeId },
{ skuId: product.id, quantity, storeId: product.storeId },
{ onSuccess: () => navigate({ to: '/flash/cart' }) }
)
}
@ -34,7 +34,7 @@ function FlashProductDetailPage() {
)
}
const price = product.discountedPrice ?? product.price
const price = product.price
const imageUrl = product.images?.[0]
return (
@ -60,18 +60,13 @@ function FlashProductDetailPage() {
{product.name}
</p>
<p className="mb-4 text-sm text-gray-500">
{product.unitValue}{product.unit}
{product.unitNotation}
</p>
<div className="mb-4 flex items-baseline gap-2">
<p className="font-bold text-2xl text-brand-600">
{price}
</p>
{product.discountedPrice && (
<p className="text-sm text-gray-400 line-through">
{product.price}
</p>
)}
</div>
<div className="mb-6">

View file

@ -82,7 +82,7 @@ function FlashDeliveryPage() {
const handleAddToCart = (productId: number) => {
const item = filteredProducts.find((p: any) => p.id === productId)
addToCart.mutate(
{ productId, quantity: 1, storeId: item?.storeId ?? 0 },
{ skuId: productId, quantity: 1, storeId: item?.storeId ?? 0 },
{
onSuccess: () => {
alert(`Added ${item?.name || 'item'} for 1 Hr Delivery`)
@ -215,7 +215,7 @@ function CompactProductCard({
const removeFromCart = useRemoveFromCart('flash');
const cartItem = cartData?.items?.find(
(cartItem: any) => cartItem.productId === item.id,
(cartItem: any) => cartItem.skuId === item.id,
);
const quantity = cartItem?.quantity || 0;
const isOutOfStock = productSlotsMap[item.id]?.isOutOfStock;
@ -226,7 +226,7 @@ function CompactProductCard({
} else if (newQuantity === 1 && !cartItem) {
handleAddToCart(item.id);
} else if (cartItem) {
updateCartItem.mutate({ productId: cartItem.id, quantity: newQuantity });
updateCartItem.mutate({ skuId: cartItem.skuId, quantity: newQuantity });
}
};

View file

@ -18,11 +18,11 @@ function CartPage() {
const productsById: Record<number, any> = {}
products.forEach((p: any) => { productsById[p.id] = p })
const cartItems = (cart?.items || []).filter((item) => productsById[item.productId])
const cartItems = (cart?.items || []).filter((item) => productsById[item.skuId])
let total = 0
cartItems.forEach((item) => {
const product = productsById[item.productId]
const product = productsById[item.skuId]
if (product) {
total += product.price * item.quantity
}
@ -57,11 +57,11 @@ function CartPage() {
{/* Items */}
<div className="flex flex-col gap-3">
{cartItems.map((item) => {
const product = productsById[item.productId]
const product = productsById[item.skuId]
const price = product.price
return (
<div
key={item.productId}
key={item.skuId}
className="flex items-center gap-4 rounded-xl border border-gray-200 bg-white p-4"
>
<img
@ -77,7 +77,7 @@ function CartPage() {
<div className="mt-2 flex items-center gap-3">
<Quantifier
value={item.quantity}
setValue={(q) => updateItem.mutate({ productId: item.productId, quantity: q })}
setValue={(q) => updateItem.mutate({ skuId: item.skuId, quantity: q })}
/>
<p className="text-sm font-bold text-gray-900">{price}</p>
</div>
@ -85,7 +85,7 @@ function CartPage() {
<div className="flex flex-col items-end gap-2">
<p className="text-sm font-extrabold text-brand-600 sm:text-base">{price * item.quantity}</p>
<button
onClick={() => removeItem.mutate(item.productId)}
onClick={() => removeItem.mutate(item.skuId)}
className="flex h-9 w-9 items-center justify-center rounded-lg text-gray-400 transition-colors hover:bg-red-50 hover:text-red-500"
aria-label={`Remove ${product.name}`}
>

View file

@ -30,19 +30,19 @@ function CheckoutPage() {
queryClient.invalidateQueries({ queryKey: ['local-cart-regular'] })
navigate({
to: '/home/order-success',
search: { orderId: order?.id, totalAmount: total },
search: { orderId: String(order?.id), totalAmount: String(total) },
})
},
onSettled: () => setIsLoading(false),
})
const cartItems = (cart?.items || []).filter((item) => productsById[item.productId])
const cartItems = (cart?.items || []).filter((item) => productsById[item.skuId])
let total = 0
cartItems.forEach((item) => {
const product = productsById[item.productId]
const product = productsById[item.skuId]
if (product) {
total += (product.discountedPrice ?? product.price) * item.quantity
total += product.price * item.quantity
}
})
@ -51,7 +51,7 @@ function CheckoutPage() {
setIsLoading(true)
placeOrderMutation.mutate({
selectedItems: cartItems.map((item) => ({
productId: item.productId,
skuId: item.skuId,
quantity: item.quantity,
slotId: null,
})),
@ -97,15 +97,15 @@ function CheckoutPage() {
Order Summary
</p>
{cartItems.map((item) => {
const product = productsById[item.productId]
const product = productsById[item.skuId]
if (!product) return null
return (
<div key={item.productId} className="flex items-center justify-between py-2">
<div key={item.skuId} className="flex items-center justify-between py-2">
<p className="text-sm">
{product.name} x{item.quantity}
</p>
<p className="text-sm font-bold">
{(product.discountedPrice ?? product.price) * item.quantity}
{product.price * item.quantity}
</p>
</div>
)

View file

@ -67,7 +67,7 @@ function ProductDetailPage() {
}, [slotsData, productDetail])
const cartItem = productDetail
? cartData?.items?.find((item: any) => item.productId === productDetail.id)
? cartData?.items?.find((item: any) => item.skuId === productDetail.id)
: null
const quantity = cartItem?.quantity || 0
@ -83,7 +83,7 @@ function ProductDetailPage() {
} else if (newQuantity === 1 && !cartItem) {
handleAddToCart()
} else if (cartItem) {
updateCartItem.mutate({ productId: cartItem.id, quantity: newQuantity })
updateCartItem.mutate({ skuId: cartItem.skuId, quantity: newQuantity })
}
}
@ -95,7 +95,7 @@ function ProductDetailPage() {
return
}
addToCart.mutate(
{ productId: productDetail.id, quantity: 1, slotId, storeId: productDetail.storeId },
{ skuId: productDetail.id, quantity: 1, slotId, storeId: productDetail.store?.id },
{
onSuccess: () => {
const slot = slotsData?.slots?.find((s: any) => s.id === slotId)
@ -111,7 +111,7 @@ function ProductDetailPage() {
const handleBuyNow = () => {
if (!productDetail) return
addToFlashCart.mutate(
{ productId: productDetail.id, quantity: 1, storeId: productDetail.storeId },
{ skuId: productDetail.id, quantity: 1, storeId: productDetail.store?.id },
{
onSuccess: () => {
navigate({ to: '/flash/cart' })
@ -125,18 +125,18 @@ function ProductDetailPage() {
if (cartItem) {
removeFromCart.mutate(
{ itemId: cartItem.id },
cartItem.skuId,
{
onSuccess: () => {
addToCart.mutate(
{ productId: productDetail.id, quantity: cartItem.quantity + 1, slotId: selectedSlotId, storeId: productDetail.storeId }
{ skuId: productDetail.id, quantity: cartItem.quantity + 1, slotId: selectedSlotId, storeId: productDetail.store?.id }
)
},
}
)
} else {
addToCart.mutate(
{ productId: productDetail.id, quantity: 1, slotId: selectedSlotId, storeId: productDetail.storeId }
{ skuId: productDetail.id, quantity: 1, slotId: selectedSlotId, storeId: productDetail.store?.id }
)
}
setShowAllSlots(false)

View file

@ -8,7 +8,7 @@ export const Route = createFileRoute('/me/orders')({ component: OrdersPage })
function OrdersPage() {
const navigate = useNavigate()
const location = useLocation()
const { data } = trpc.user.order.getOrders.useQuery({ page: 1, limit: 20 })
const { data } = trpc.user.order.getOrders.useQuery({ page: 1 })
const orders = data?.data || []
// Check if we're on the exact /me/orders path (not a child route like /me/orders/123)

View file

@ -2,7 +2,7 @@ import { createFileRoute, useNavigate } from '@tanstack/react-router'
import { useState } from 'react'
import { useAuth } from '../lib/auth-context'
import { trpc } from '../lib/trpc-client'
import { p, MyButton, pInput, pButton } from 'web-components'
import { p, MyButton, pInput as PInput, pButton } from 'web-components'
export const Route = createFileRoute('/register')({ component: RegisterPage })
@ -16,8 +16,8 @@ function RegisterPage() {
const registerMutation = trpc.user.auth.register.useMutation({
onSuccess: (data) => {
if (data.token && data.user) {
authRegister(data.token, data.user)
if (data.data.token && data.data.user) {
authRegister(data.data.token, data.data.user)
navigate({ to: '/home' })
}
},
@ -40,19 +40,19 @@ function RegisterPage() {
<div className="rounded-2xl bg-white p-8 shadow-xl">
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
<pInput
<PInput
placeholder="Full Name"
value={name}
onChange={(e) => setName(e.target.value)}
required
/>
<pInput
<PInput
placeholder="Email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<pInput
<PInput
placeholder="Mobile Number"
value={mobile}
onChange={(e) => {
@ -61,7 +61,7 @@ function RegisterPage() {
}}
required
/>
<pInput
<PInput
placeholder="Password"
type="password"
value={password}

View file

@ -112,7 +112,7 @@ function SlotViewPage() {
? dayjs(slot.deliveryTime).format("ddd, DD MMM • h:mm A")
: "";
addToCart.mutate(
{ productId, quantity: 1, slotId: slotId || 0, storeId: item?.storeId },
{ skuId: productId, quantity: 1, slotId: slotId || 0, storeId: item?.storeId },
{
onSuccess: () => {
alert(
@ -420,7 +420,7 @@ function CompactProductCard({
} else if (newQuantity === 1 && !cartItem) {
handleAddToCart(item.id);
} else if (cartItem) {
updateCartItem.mutate({ productId: cartItem.id, quantity: newQuantity });
updateCartItem.mutate({ skuId: cartItem.skuId, quantity: newQuantity });
}
};

View file

@ -22,7 +22,7 @@ function StoreProductDetailPage() {
const handleAddToCart = () => {
if (!product) return
addToCart.mutate(
{ productId: product.id, quantity, storeId: product.storeId },
{ skuId: product.id, quantity, storeId: product.storeId },
{ onSuccess: () => navigate({ to: '/cart' }) }
)
}
@ -35,7 +35,7 @@ function StoreProductDetailPage() {
)
}
const price = product.discountedPrice ?? product.price
const price = product.price
const imageUrl = product.images?.[0]
return (
@ -61,24 +61,15 @@ function StoreProductDetailPage() {
{product.name}
</p>
<p className="mb-2 text-sm text-gray-500">
{product.unitValue}{product.unit}
{product.unitNotation}
</p>
<div className="mb-4 flex items-baseline gap-2">
<p className="font-bold text-2xl text-brand-600">
{price}
</p>
{product.discountedPrice && (
<p className="text-sm text-gray-400 line-through">
{product.price}
</p>
)}
</div>
{product.description && (
<p className="mb-4 text-gray-600">{product.description}</p>
)}
<div className="mb-6">
<Quantifier value={quantity} setValue={setQuantity} max={10} />
</div>

View file

@ -2,8 +2,7 @@ import {
createStartHandler,
defaultStreamHandler,
} from '@tanstack/react-start/server'
import { getRouter } from './router'
export default createStartHandler({
createRouter: getRouter,
})(defaultStreamHandler)
handler: defaultStreamHandler,
})

View file

@ -14,7 +14,7 @@
"@backend/*": ["../../apps/backend/src/*"]
},
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"types": ["vite/client"],
"types": ["vite/client", "node"],
/* Bundler mode */
"moduleResolution": "bundler",

View file

@ -1416,3 +1416,21 @@ packages/web-components/tsconfig.json:
package.json (root): + "typecheck": "bash typecheck"
[2026-09-05 17:55:00] VERIFIED ./typecheck: all 9 targets report exactly their known baselines — backend 11, user-ui 106, admin-ui 122, web-ui 150, fallback-ui 107, db_helper_sqlite 4, db_helper_postgres 45, migrator 0 ✅, web-components 3 (the pre-existing `<p weight>` custom-prop errors; standalone count went 21 → 3 after removing the declaration-emit settings). Script exits 1 while any target fails; run via `./typecheck` or `npm run typecheck` / `bun run typecheck`.
Phase 3 (2026-09-05 18:40) — admin-ui debt paydown (15 → 0) + API completeness (rule 1):
apps/backend/src/trpc/apis/common-apis/common.ts:
- scaffoldProducts response gains the numeric pricing fields it was missing (they came only from the availability feed): + price: Number(product.price ?? 0); + marketPrice (number|null); + flashPrice (number|null); + isFlashAvailable. Additive — MergedProduct/BaseProduct now carry numeric pricing consistently.
packages/ui/shared-types.ts (c: coupons edit screen):
- CreateCouponPayload + skuIds?: number[] (the edit form pre-fills coupon.skuIds; create flow already forwarded it).
apps/admin-ui:
- all-items-order.tsx(97), popular-items.tsx(124), ProductGroupForm.tsx(28), ProductsSelector.tsx(48), ProductForm.tsx(185): trpc void-input procedures called with `.useQuery({})` → `.useQuery()`
- complaints/index.tsx(66): resolveComplaint response `?? ''`
- coupons/edit/[id].tsx(92): unblocked by CreateCouponPayload.skuIds
- products/index.tsx(41/99×2/100): getDefaultSku param typed to the trpc-serialized SKU (`Omit<AdminSku,'createdAt'> & { createdAt: string | Date }`)
- products/index.tsx(117): SearchBar usage passed a non-existent onSearch prop (ignored at runtime) → removed
- products/edit.tsx(215): empty-branch initialValues gains productType: 'item'
- popular-items.tsx(150/191): unblocked by the scaffoldProducts pricing fields; PopularProduct.images loosened to string[]|null (Item already optional-chains images)
[2026-09-05 19:00:00] TYPE-DEBT PAYDOWN COMPLETE. ./typecheck: 8/9 targets ✅ clean (backend, user-ui, admin-ui, web-ui, fallback-ui, db_helper_sqlite, migrator, web-components); db_helper_postgres intentionally left red (45 pre-existing errors) per user decision — documented exception. Total debt went 455 → 45.
Genuine runtime bugs the types caught and fixed along the way: (1) backend init.ts called non-existent startOrderHandler/startCancellationHandler → startup crash removed (queue consumers live in worker); (2) s3-client passed signQuery/expires at the top level of aws4fetch's sign() where they were silently ignored → presigned URLs were header-signed and useless; now signed via aws:{signQuery} + X-Amz-Expires header; (3) web-ui register read the pre-wrapper response shape (data.token) → registration never navigated; now data.data.token; (4) web-ui localStorage cart stored sku ids under a `productId` field and matched update/remove by cart-line id — quantity updates and removals silently did nothing; renamed to skuId end-to-end; (5) web-ui used lowercase <pInput>/<p> JSX (unknown HTML elements — styled components never rendered); renamed to <PInput>/<P>; (6) availability cache wrote string prices while every consumer Number()-cast → cache now numeric (flashPrice number|null per rule 2).
Phase-1 note: backend's 'sqliteService' paths-only alias failed under the app tsconfigs (TS2307 → export * yielded nothing → hundreds of cascade errors); switched to relative imports, which is what collapsed the 455-error flood to 68 and then to 45.

View file

@ -137,7 +137,7 @@ export async function seed() {
if (!existing) {
await db.insert(keyValStore).values({
key: constant.key,
value: constant.value,
value: String(constant.value),
})
}
}

View file

@ -128,7 +128,7 @@ export async function getAvailabilityForCache(): Promise<AvailabilityCacheData[]
id: stat.skuId,
price: Number(stat.ourPrice ?? 0),
marketPrice: stat.marketPrice != null ? Number(stat.marketPrice) : null,
flashPrice: stat.flashPrice ? String(stat.flashPrice) : null,
flashPrice: stat.flashPrice != null ? Number(stat.flashPrice) : null,
isFlashAvailable: stat.isFlashAvailable,
isOutOfStock: stat.isOutOfStock,
isSuspended: stat.isSuspended,

View file

@ -584,7 +584,6 @@ export async function getOrdersByIdsWithFullData(
sku: {
columns: {
name: true,
price: true,
},
with: {
product: {
@ -604,7 +603,7 @@ export async function getOrdersByIdsWithFullData(
})
})
return orderChunks.flat() as OrderWithFullData[]
return orderChunks.flat() as unknown as OrderWithFullData[]
}
export interface OrderWithCancellationData extends OrderWithFullData {

View file

@ -349,6 +349,7 @@ const mapOffersPageProduct = (sku: {
isOutOfStock: boolean
} | null
images: unknown
name: string | null
product: { name: string; incrementStep: number | null } | null
features: Array<{ featureValue: string }>
}): OffersPageProductData => {

View file

@ -0,0 +1,84 @@
/**
* UI component prop types shared between common-ui (RN) and web-components.
* Type-only module consumers use `import type`, so nothing ships at runtime.
*/
import type { ReactNode } from 'react'
// Dropdown option — identical in packages/ui dropdown.tsx + packages/web-components dropdown.tsx
export interface DropdownOption {
label: string
value: string | number
}
// Base dialog shell — packages/ui `DialogProps` + packages/web-components `BottomDialogProps`
// + web-ui `Dialog` (optional title).
export interface DialogProps {
open: boolean
onClose: () => void
children: ReactNode
enableDismiss?: boolean
title?: string
}
// Confirm dialog — identical in both packages' dialog.tsx
export interface ConfirmationDialogProps {
open: boolean
positiveAction: (comment?: string) => void
commentNeeded?: boolean
negativeAction?: () => void
title?: string
message?: string
confirmText?: string
cancelText?: string
isLoading?: boolean
}
// Loading dialog — identical in both packages' loading-dialog.tsx
export interface LoadingDialogProps {
open: boolean
message?: string
}
// Quantity stepper — identical in both packages' quantifier.tsx
export interface QuantifierProps {
value: number
setValue: (value: number) => void
step?: number
unit?: string | { shortNotation: string }
min?: number
max?: number
}
// Legacy image uploader — identical in both packages' image-uploader components
export interface ImageUploaderProps {
images: { uri?: string }[]
onAddImage: () => void
onRemoveImage: (uri: string) => void
existingImageUrls?: string[]
onRemoveExistingImage?: (url: string) => void
allowMultiple?: boolean
}
// Neo image uploader — identical in both packages' image-uploader-neo components
export interface ImageUploaderNeoItem {
imgUrl: string
mimeType: string | null
}
export interface ImageUploaderNeoPayload {
url: string
mimeType: string | null
}
export interface ImageUploaderNeoProps {
images: ImageUploaderNeoItem[]
onImageAdd: (images: ImageUploaderNeoPayload[]) => void
onImageRemove: (image: ImageUploaderNeoPayload) => void
allowMultiple?: boolean
}
// Text-button extras — packages/ui `MyTextButtonProps` + packages/web-components `pButtonProps`
export interface TextButtonProps {
text: string
}

View file

@ -280,7 +280,7 @@ export interface UserProductDetail extends UserProductDetailData {
export interface ProductPriceFlags {
price: number;
marketPrice: number | null;
flashPrice: string | null;
flashPrice: number | null;
isFlashAvailable: boolean;
isOutOfStock: boolean;
isSuspended: boolean;

View file

@ -10,6 +10,7 @@ export interface CreateCouponPayload {
isUserBased: boolean;
targetUsers?: number[];
productIds?: number[];
skuIds?: number[];
applicableUsers?: number[];
applicableProducts?: number[];
exclusiveApply?: boolean;

View file

@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react'
import type { ConfirmationDialogProps, DialogProps } from '@packages/shared'
import { p } from './my-text'
import { p as P } from './my-text'
import { MyButton } from './my-button'
import { cn } from '../lib/utils'
@ -72,9 +72,9 @@ export function ConfirmationDialog({
return (
<BottomDialog open={open} onClose={handleCancel}>
<div className="p-4">
<p weight="bold" className="mb-4 text-lg">
<P weight="bold" className="mb-4 text-lg">
{title}
</p>
</P>
<p className="mb-6 text-gray-600">{message}</p>
{commentNeeded && (
<textarea

View file

@ -1,6 +1,6 @@
import React from 'react'
import { cn } from '../lib/utils'
import { p } from './my-text'
import { p as P } from './my-text'
import type { QuantifierProps } from '@packages/shared'
import { Minus, Plus } from 'lucide-react'
@ -32,9 +32,9 @@ export function Quantifier({
>
<Minus className="h-3.5 w-3.5" />
</button>
<p weight="semibold" className="min-w-[32px] text-center text-sm">
<P weight="semibold" className="min-w-[32px] text-center text-sm">
{value}
</p>
</P>
<button
onClick={(e) => {
e.stopPropagation()
@ -68,9 +68,9 @@ export function MiniQuantifier({
>
<Minus className="h-3 w-3" />
</button>
<p weight="semibold" className="min-w-[24px] text-center text-xs">
<P weight="semibold" className="min-w-[24px] text-center text-xs">
{value}
</p>
</P>
<button
onClick={(e) => {
e.stopPropagation()