freshyo/apps/admin-web/src/components/OrderOptionsMenu.tsx
2026-09-06 23:05:20 +05:30

327 lines
11 KiB
TypeScript

import React from 'react'
import { p as P, BottomDialog, Checkbox } from 'web-components'
import { trpc } from '@/lib/trpc-client'
import { ChevronRight, Eye, Pencil, XCircle, MapPin, Map, MessageCircle, Phone } from 'lucide-react'
interface OrderOptionsMenuProps {
open: boolean
onClose: () => void
order: {
id: number
readableId: number
isPackaged: boolean
isDelivered: boolean
isFlashDelivery?: boolean
address: string
addressId: number
adminNotes?: string | null
userNotes?: string | null
latitude?: number | null
longitude?: number | null
status?: string
}
onViewDetails: () => void
onTogglePackaged: () => void
onToggleDelivered: () => void
onOpenAdminNotes: () => void
onCancelOrder: () => void
onAttachLocation: () => void
onWhatsApp: () => void
onDial: () => void
}
function getCurrentPosition(): Promise<GeolocationPosition> {
return new Promise((resolve, reject) => {
if (!navigator.geolocation) {
reject(new Error('Geolocation is not supported by this browser'))
return
}
navigator.geolocation.getCurrentPosition(resolve, reject, { enableHighAccuracy: true })
})
}
export function OrderOptionsMenu({
open,
onClose,
order,
onViewDetails,
onTogglePackaged,
onToggleDelivered,
onOpenAdminNotes,
onCancelOrder,
onAttachLocation,
onWhatsApp,
onDial,
}: OrderOptionsMenuProps) {
const updateAddressCoordsMutation = trpc.admin.order.updateAddressCoords.useMutation()
const handleAttachLocation = async () => {
try {
const location = await getCurrentPosition()
const { latitude, longitude } = location.coords
await updateAddressCoordsMutation.mutateAsync({
addressId: order.addressId,
latitude,
longitude,
})
window.alert('Success: Location attached to address successfully.')
onAttachLocation()
} catch (error: any) {
if (error?.code === 1) {
window.alert('Permission Denied: Location permission is required to attach coordinates.')
return
}
window.alert('Error: Failed to attach location. Please try again.')
}
}
const extractPhone = (address: string) => {
const phoneMatch = address.match(/Phone: (\d+)/)
return phoneMatch ? phoneMatch[1] : null
}
const handleWhatsApp = () => {
const phone = extractPhone(order.address)
if (phone) {
window.open(`https://wa.me/91${phone}`, '_blank')
} else {
window.alert('No phone number found')
}
}
const handleDial = () => {
const phone = extractPhone(order.address)
if (phone) {
window.open(`tel:${phone}`, '_blank')
} else {
window.alert('No phone number found')
}
}
const handleOpenInMaps = () => {
if (order.latitude && order.longitude) {
const url = `https://www.google.com/maps/search/?api=1&query=${order.latitude},${order.longitude}`
window.open(url, '_blank')
} else {
window.alert('No location coordinates available')
}
}
const hasCoordinates = order.latitude !== null && order.latitude !== undefined &&
order.longitude !== null && order.longitude !== undefined
return (
<BottomDialog open={open} onClose={onClose}>
<div style={{ maxHeight: '70vh' }} className="overflow-hidden flex flex-col">
<div className="overflow-auto grow">
<div className="pb-8 pt-2 px-4">
<div className="items-center mb-6 flex flex-col">
<div className="w-12 h-1.5 bg-gray-200 rounded-full mb-4" />
<P className="text-lg font-bold text-gray-900">
Order #{order.readableId}
</P>
<P className="text-sm text-gray-500">
Select an action to perform
</P>
</div>
<button
type="button"
onClick={() => {
onViewDetails()
onClose()
}}
className="flex flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm w-full text-left"
>
<div className="w-10 h-10 rounded-full bg-purple-50 items-center justify-center mr-4 flex shrink-0">
<Eye className="h-5 w-5 text-purple-600" />
</div>
<div>
<P className="font-semibold text-gray-800 text-base block">
View Details
</P>
<P className="text-gray-500 text-xs block">
See full order information
</P>
</div>
<ChevronRight className="h-6 w-6 text-gray-400 ml-auto" />
</button>
<div className="flex flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm">
<button type="button" onClick={onTogglePackaged} className="p-1" aria-label="Toggle packaged">
<Checkbox
checked={order.isPackaged}
onPress={onTogglePackaged}
fillColor={order.isPackaged ? '#10B981' : '#1570EF'}
/>
</button>
<button type="button" onClick={onTogglePackaged} className="ml-3 flex-1 text-left">
<P className="font-semibold text-gray-800 text-base block">
Packaged
</P>
<P className="text-gray-500 text-xs block">
{order.isPackaged ? 'Mark as not packaged' : 'Mark as packaged'}
</P>
</button>
</div>
<div className="flex flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm">
<button type="button" onClick={onToggleDelivered} className="p-1" aria-label="Toggle delivered">
<Checkbox
checked={order.isDelivered}
onPress={onToggleDelivered}
fillColor={order.isDelivered ? '#10B981' : '#1570EF'}
/>
</button>
<button type="button" onClick={onToggleDelivered} className="ml-3 flex-1 text-left">
<P className="font-semibold text-gray-800 text-base block">
Delivered
</P>
<P className="text-gray-500 text-xs block">
{order.isDelivered ? 'Mark as not delivered' : 'Mark as delivered'}
</P>
</button>
</div>
<button
type="button"
onClick={() => {
onOpenAdminNotes()
onClose()
}}
className="flex flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm w-full text-left"
>
<div className="w-10 h-10 rounded-full bg-yellow-50 items-center justify-center mr-4 flex shrink-0">
<Pencil className="h-5 w-5 text-yellow-600" />
</div>
<div>
<P className="font-semibold text-gray-800 text-base block">
Admin Notes
</P>
<P className="text-gray-500 text-xs block">
{order.adminNotes ? 'Edit existing notes' : 'Add admin notes'}
</P>
</div>
<ChevronRight className="h-6 w-6 text-gray-400 ml-auto" />
</button>
{order.status !== 'cancelled' && (
<button
type="button"
onClick={() => {
onCancelOrder()
onClose()
}}
className="flex flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm w-full text-left"
>
<div className="w-10 h-10 rounded-full bg-red-50 items-center justify-center mr-4 flex shrink-0">
<XCircle className="h-5 w-5 text-red-600" />
</div>
<div>
<P className="font-semibold text-red-700 text-base block">
Cancel Order
</P>
<P className="text-red-500 text-xs block">
Cancel and provide reason
</P>
</div>
<ChevronRight className="h-6 w-6 text-gray-400 ml-auto" />
</button>
)}
<button
type="button"
onClick={() => {
handleAttachLocation()
onClose()
}}
disabled={updateAddressCoordsMutation.isPending}
className="flex flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm w-full text-left disabled:opacity-50"
>
<div className="w-10 h-10 rounded-full bg-orange-50 items-center justify-center mr-4 flex shrink-0">
<MapPin className="h-5 w-5 text-orange-600" />
</div>
<div>
<P className="font-semibold text-gray-800 text-base block">
Attach Location
</P>
<P className="text-gray-500 text-xs block">
Save GPS coordinates to address
</P>
</div>
<ChevronRight className="h-6 w-6 text-gray-400 ml-auto" />
</button>
{hasCoordinates && (
<button
type="button"
onClick={() => {
handleOpenInMaps()
onClose()
}}
className="flex flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm w-full text-left"
>
<div className="w-10 h-10 rounded-full bg-blue-50 items-center justify-center mr-4 flex shrink-0">
<Map className="h-5 w-5 text-blue-600" />
</div>
<div>
<P className="font-semibold text-gray-800 text-base block">
Open in Maps
</P>
<P className="text-gray-500 text-xs block">
View delivery location on Google Maps
</P>
</div>
<ChevronRight className="h-6 w-6 text-gray-400 ml-auto" />
</button>
)}
<button
type="button"
onClick={() => {
handleWhatsApp()
onClose()
}}
className="flex flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm w-full text-left"
>
<div className="w-10 h-10 rounded-full bg-green-50 items-center justify-center mr-4 flex shrink-0">
<MessageCircle className="h-5 w-5 text-green-600" />
</div>
<div>
<P className="font-semibold text-gray-800 text-base block">
Message On WhatsApp
</P>
<P className="text-gray-500 text-xs block">
Send message via WhatsApp
</P>
</div>
<ChevronRight className="h-6 w-6 text-gray-400 ml-auto" />
</button>
<button
type="button"
onClick={() => {
handleDial()
onClose()
}}
className="flex flex-row items-center p-4 bg-white border border-gray-100 rounded-xl mb-3 shadow-sm w-full text-left"
>
<div className="w-10 h-10 rounded-full bg-green-50 items-center justify-center mr-4 flex shrink-0">
<Phone className="h-5 w-5 text-green-600" />
</div>
<div>
<P className="font-semibold text-gray-800 text-base block">
Dial Mobile Number
</P>
<P className="text-gray-500 text-xs block">
Call customer directly
</P>
</div>
<ChevronRight className="h-6 w-6 text-gray-400 ml-auto" />
</button>
</div>
</div>
</div>
</BottomDialog>
)
}