Localize order modals, add dual logos, and improve admin order tools.
Farsi business order/variation UI, light/dark logo upload on business profile, payment order steps, variation editing, super-admin site links, and checkout/auth fixes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
dc570c1e7f
commit
5e799eddf9
@@ -23,7 +23,10 @@ interface AuthContextValue {
|
||||
user: AuthUser | null
|
||||
isLoading: boolean
|
||||
login: (cellNumber: string, password: string) => Promise<void>
|
||||
loginWithOtp: (cellNumber: string, code: string) => Promise<void>
|
||||
loginWithOtp: (
|
||||
cellNumber: string,
|
||||
code: string,
|
||||
) => Promise<{ passwordUpdated?: boolean }>
|
||||
logout: () => void
|
||||
setUser: (user: AuthUser) => void
|
||||
}
|
||||
@@ -130,6 +133,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
const data = await loginWithOtpRequest(cellNumber, code, getTenantDomain())
|
||||
await assertCustomerAccess(data.user)
|
||||
setUser(data.user)
|
||||
return { passwordUpdated: data.passwordUpdated }
|
||||
}, [])
|
||||
|
||||
const value = useMemo(
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import { createContext, useCallback, useContext, useMemo, useState, type ReactNode } from 'react'
|
||||
import {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useMemo,
|
||||
useState,
|
||||
type ReactNode,
|
||||
} from 'react'
|
||||
import type { Order } from '../services/orderService'
|
||||
import { getTenantDomain } from '../lib/config'
|
||||
|
||||
export type DeliveryMode = 'delivery' | 'pickup'
|
||||
|
||||
@@ -25,23 +33,94 @@ const defaultState: CheckoutState = {
|
||||
placedOrder: null,
|
||||
}
|
||||
|
||||
function storageKey() {
|
||||
return `meshkee_checkout:${getTenantDomain()}`
|
||||
}
|
||||
|
||||
function readPersisted(): Pick<CheckoutState, 'deliveryMode' | 'selectedAddressId' | 'discountCode'> {
|
||||
try {
|
||||
const raw = sessionStorage.getItem(storageKey())
|
||||
if (!raw) return {
|
||||
deliveryMode: defaultState.deliveryMode,
|
||||
selectedAddressId: defaultState.selectedAddressId,
|
||||
discountCode: defaultState.discountCode,
|
||||
}
|
||||
const parsed = JSON.parse(raw) as {
|
||||
deliveryMode?: unknown
|
||||
selectedAddressId?: unknown
|
||||
discountCode?: unknown
|
||||
}
|
||||
return {
|
||||
deliveryMode:
|
||||
parsed.deliveryMode === 'pickup' || parsed.deliveryMode === 'delivery'
|
||||
? parsed.deliveryMode
|
||||
: defaultState.deliveryMode,
|
||||
selectedAddressId:
|
||||
typeof parsed.selectedAddressId === 'string' && parsed.selectedAddressId
|
||||
? parsed.selectedAddressId
|
||||
: null,
|
||||
discountCode:
|
||||
typeof parsed.discountCode === 'string' ? parsed.discountCode : '',
|
||||
}
|
||||
} catch {
|
||||
return {
|
||||
deliveryMode: defaultState.deliveryMode,
|
||||
selectedAddressId: defaultState.selectedAddressId,
|
||||
discountCode: defaultState.discountCode,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function persistPartial(state: CheckoutState) {
|
||||
try {
|
||||
sessionStorage.setItem(
|
||||
storageKey(),
|
||||
JSON.stringify({
|
||||
deliveryMode: state.deliveryMode,
|
||||
selectedAddressId: state.selectedAddressId,
|
||||
discountCode: state.discountCode,
|
||||
}),
|
||||
)
|
||||
} catch {
|
||||
// ignore quota / private mode
|
||||
}
|
||||
}
|
||||
|
||||
const CheckoutContext = createContext<CheckoutContextValue | null>(null)
|
||||
|
||||
export function CheckoutProvider({ children }: { children: ReactNode }) {
|
||||
const [state, setState] = useState<CheckoutState>(defaultState)
|
||||
const [state, setState] = useState<CheckoutState>(() => ({
|
||||
...defaultState,
|
||||
...readPersisted(),
|
||||
}))
|
||||
|
||||
const setDeliveryMode = useCallback((deliveryMode: DeliveryMode) => {
|
||||
setState((prev) => ({ ...prev, deliveryMode }))
|
||||
setState((prev) => {
|
||||
const next = { ...prev, deliveryMode }
|
||||
persistPartial(next)
|
||||
return next
|
||||
})
|
||||
}, [])
|
||||
|
||||
const setSelectedAddressId = useCallback((selectedAddressId: string | null) => {
|
||||
setState((prev) =>
|
||||
prev.selectedAddressId === selectedAddressId ? prev : { ...prev, selectedAddressId },
|
||||
)
|
||||
const normalized =
|
||||
selectedAddressId === null || selectedAddressId === undefined
|
||||
? null
|
||||
: String(selectedAddressId)
|
||||
setState((prev) => {
|
||||
if (prev.selectedAddressId === normalized) return prev
|
||||
const next = { ...prev, selectedAddressId: normalized }
|
||||
persistPartial(next)
|
||||
return next
|
||||
})
|
||||
}, [])
|
||||
|
||||
const setDiscountCode = useCallback((discountCode: string) => {
|
||||
setState((prev) => ({ ...prev, discountCode }))
|
||||
setState((prev) => {
|
||||
const next = { ...prev, discountCode }
|
||||
persistPartial(next)
|
||||
return next
|
||||
})
|
||||
}, [])
|
||||
|
||||
const setPlacedOrder = useCallback((placedOrder: Order | null) => {
|
||||
@@ -50,6 +129,11 @@ export function CheckoutProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
const resetCheckout = useCallback(() => {
|
||||
setState(defaultState)
|
||||
try {
|
||||
sessionStorage.removeItem(storageKey())
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}, [])
|
||||
|
||||
const value = useMemo<CheckoutContextValue>(
|
||||
|
||||
Reference in New Issue
Block a user