Skip to content

Commit

Permalink
feat: smart orders ui
Browse files Browse the repository at this point in the history
  • Loading branch information
Emmanuel-Melon committed Jan 4, 2025
1 parent c13c879 commit 0303f0a
Show file tree
Hide file tree
Showing 2 changed files with 396 additions and 0 deletions.
259 changes: 259 additions & 0 deletions apps/agent/src/components/PromptOrderSuggestion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
import { useState } from 'react'
import { Button, Modal, Drawer } from 'ui'
import { HiOutlineShoppingCart, HiOutlineSparkles } from 'react-icons/hi2'

interface Item {
id: string
name: string
quantity: number
unit: string
price: number
}

interface Supplier {
name: string
rating: number
totalOrders: number
onTimeDelivery: number
}

interface PromptOrderSuggestionProps {
suggestion: {
items: Item[]
supplier: Supplier
deliveryDate: string
totalCost: number
}
onCompare: () => void
}

// Mock suppliers data
const mockSuppliers = [
{
id: 1,
name: "Global Foods Ltd",
rating: 4.8,
totalOrders: 1500,
onTimeDelivery: 98,
price: 42.50,
deliveryDate: "2025-01-10",
minOrderQuantity: 25
},
{
id: 2,
name: "Fresh Direct",
rating: 4.6,
totalOrders: 1200,
onTimeDelivery: 95,
price: 40.00,
deliveryDate: "2025-01-12",
minOrderQuantity: 20
},
{
id: 3,
name: "Premium Suppliers",
rating: 4.9,
totalOrders: 2000,
onTimeDelivery: 99,
price: 45.00,
deliveryDate: "2025-01-09",
minOrderQuantity: 30
}
]

interface SupplierCompareDrawerProps {
currentSupplier: Supplier
}

function SupplierCompareDrawer({ currentSupplier }: SupplierCompareDrawerProps) {
return (
<Drawer id="supplier-compare" CTA="Compare" className="justify-between space-y-4">
<div className="space-y-1 mb-6">
<h3 className="font-bold text-xl">Compare Suppliers</h3>
<p className="opacity-80">
Compare different suppliers and choose the best option for your order.
</p>
</div>

<div className="grid gap-4">
{mockSuppliers.map((supplier) => (
<div
key={supplier.id}
className="card card-bordered p-4 relative hover:shadow-md transition-shadow"
>
<div className="flex justify-between items-start">
<div>
<h3 className="font-medium text-lg">{supplier.name}</h3>
<div className="mt-2 space-y-1 text-sm text-gray-600">
<p className="flex items-center">
<span className="mr-1"></span>
{supplier.rating} ({supplier.totalOrders} orders)
</p>
<p className="flex items-center">
<HiOutlineSparkles className="mr-1 h-4 w-4 text-green-500" />
{supplier.onTimeDelivery}% On-time delivery
</p>
</div>
</div>
<div className="text-right">
<p className="text-lg font-semibold">${supplier.price}</p>
<p className="text-sm text-gray-500">Min. order: {supplier.minOrderQuantity} units</p>
</div>
</div>
<div className="mt-4">
<p className="text-sm text-gray-600">Delivery by: {supplier.deliveryDate}</p>
</div>
{supplier.name === currentSupplier.name && (
<div className="absolute top-2 right-2">
<span className="px-2 py-1 bg-green-100 text-green-800 text-xs rounded-full">
Current Supplier
</span>
</div>
)}
<div className="mt-4">
<Button className="w-full" variant={supplier.name === currentSupplier.name ? "outline" : "default"}>
{supplier.name === currentSupplier.name ? 'Current Selection' : 'Select Supplier'}
</Button>
</div>
</div>
))}
</div>
</Drawer>
)
}

export function PromptOrderSuggestion({ suggestion, onCompare }: PromptOrderSuggestionProps) {
const [isEditModalOpen, setIsEditModalOpen] = useState(false)
const [editedItem, setEditedItem] = useState<Item | null>(null)

const handleEditItem = (item: Item) => {
setEditedItem(item)
setIsEditModalOpen(true)
}

const handleSaveEdit = () => {
console.log('Saving edited item:', editedItem)
setIsEditModalOpen(false)
}

return (
<div className="p-6">
<h3 className="text-xl font-semibold mb-6">Suggested Order</h3>
<p className="text-gray-500 text-sm mb-4">Based on your request, here's what we suggest:</p>

<div className="bg-gray-50 rounded-lg p-4 mb-6">
<h4 className="text-lg font-medium mb-2">Supplier Information</h4>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div>
<p className="text-sm text-gray-500">Supplier</p>
<p className="font-medium">{suggestion.supplier.name}</p>
</div>
<div>
<p className="text-sm text-gray-500">Rating</p>
<p className="font-medium"> {suggestion.supplier.rating}</p>
</div>
<div>
<p className="text-sm text-gray-500">Total Orders</p>
<p className="font-medium">{suggestion.supplier.totalOrders}</p>
</div>
<div>
<p className="text-sm text-gray-500">On-Time Delivery</p>
<p className="font-medium">{suggestion.supplier.onTimeDelivery}%</p>
</div>
</div>
</div>

<div className="mb-4">
<p className="text-sm text-gray-500 mb-2">Delivery Date</p>
<p className="font-medium">{suggestion.deliveryDate}</p>
</div>

<table className="w-full mb-6">
<thead className="bg-gray-50">
<tr>
<th className="text-left p-2">Item</th>
<th className="text-left p-2">Quantity</th>
<th className="text-right p-2">Price</th>
<th className="text-right p-2">Actions</th>
</tr>
</thead>
<tbody>
{suggestion.items.map((item) => (
<tr key={item.id} className="border-b">
<td className="p-2">{item.name}</td>
<td className="p-2">{item.quantity} {item.unit}</td>
<td className="text-right p-2">${item.price.toFixed(2)}</td>
<td className="text-right p-2">
<Button
variant="outline"
size="sm"
onClick={() => handleEditItem(item)}
>
Edit
</Button>
</td>
</tr>
))}
<tr className="font-medium">
<td colSpan={2} className="p-2 text-right">Total:</td>
<td className="p-2 text-right">${suggestion.totalCost.toFixed(2)}</td>
<td></td>
</tr>
</tbody>
</table>

<div className="flex justify-between">
<SupplierCompareDrawer currentSupplier={suggestion.supplier} />
<Button>
<HiOutlineShoppingCart className="h-4 w-4 mr-2" />
Place Order
</Button>
</div>

<Modal
open={isEditModalOpen}
onClose={() => setIsEditModalOpen(false)}
title="Edit Item"
>
{editedItem && (
<div className="space-y-4 py-4">
<div className="space-y-2">
<label className="block text-sm font-medium text-gray-700">Item Name</label>
<input
value={editedItem.name}
readOnly
className="w-full p-2 border rounded-md bg-gray-50"
/>
</div>
<div className="space-y-2">
<label className="block text-sm font-medium text-gray-700">Quantity</label>
<input
type="number"
value={editedItem.quantity}
onChange={(e) => setEditedItem({ ...editedItem, quantity: parseInt(e.target.value) })}
className="w-full p-2 border rounded-md"
/>
</div>
<div className="space-y-2">
<label className="block text-sm font-medium text-gray-700">Price per {editedItem.unit}</label>
<input
type="number"
value={editedItem.price}
onChange={(e) => setEditedItem({ ...editedItem, price: parseFloat(e.target.value) })}
className="w-full p-2 border rounded-md"
/>
</div>
<div className="flex justify-end space-x-2 mt-6">
<Button variant="outline" onClick={() => setIsEditModalOpen(false)}>
Cancel
</Button>
<Button onClick={handleSaveEdit}>
Save Changes
</Button>
</div>
</div>
)}
</Modal>
</div>
)
}
137 changes: 137 additions & 0 deletions apps/agent/src/pages/orders/smart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
'use client'

import { useState } from 'react'
import { Button, Card, Input } from "ui"
import { HiOutlineServer, HiOutlinePaperAirplane } from "react-icons/hi2"
import { useForm, SubmitHandler } from "react-hook-form"
import { z } from "zod"
import { zodResolver } from "@hookform/resolvers/zod"
import { PromptOrderSuggestion } from '@/components/PromptOrderSuggestion'

const orderPromptSchema = z.object({
prompt: z.string().min(1, "Please enter your order requirements"),
notes: z.string().optional()
})

type FormData = z.infer<typeof orderPromptSchema>

interface OrderSuggestion {
items: Array<{ id: string; name: string; quantity: number; unit: string; price: number }>
supplier: {
name: string
rating: number
totalOrders: number
onTimeDelivery: number
}
deliveryDate: string
totalCost: number
}

export default function SmartOrdersPage() {
const [suggestion, setSuggestion] = useState<OrderSuggestion | null>(null)
const [loading, setLoading] = useState(false)

const {
register,
handleSubmit,
formState: { errors }
} = useForm<FormData>({
resolver: zodResolver(orderPromptSchema)
})

const handleCompare = () => {
console.log('Comparing suppliers...')
}

const onSubmit: SubmitHandler<FormData> = async (data) => {
setLoading(true)

try {
// Simulated API response
const mockResponse: OrderSuggestion = {
items: [
{
id: "item_1",
name: "Premium Basmati Rice",
quantity: 50,
unit: "kgs",
price: 2.5
}
],
supplier: {
name: "Global Rice Suppliers",
rating: 4.8,
totalOrders: 1234,
onTimeDelivery: 98
},
deliveryDate: "2025-01-10",
totalCost: 125.00
}

// Simulate network delay
await new Promise(resolve => setTimeout(resolve, 1000))
setSuggestion(mockResponse)
} catch (error) {
console.error('Error:', error)
} finally {
setLoading(false)
}
}

return (
<div className="min-h-screen bg-gray-50 p-4 space-y-4">
<Card className="max-w-3xl mx-auto">
<div className="p-6">
<h2 className="text-2xl font-semibold mb-2">Quick Order</h2>
<p className="text-gray-500 text-sm mb-6">Tell us in plain language and we'll suggest an order for you.</p>

<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Your Request</label>
<input
{...register("prompt")}
placeholder="e.g. '50kg of rice every Friday'"
/>
{errors.prompt && (
<p className="text-sm text-red-500 mt-1">{errors.prompt.message}</p>
)}
</div>

<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Additional Notes (Optional)</label>
<textarea
{...register("notes")}
rows={3}
/>
</div>

<div className="flex justify-end">
<Button type="submit" disabled={loading}>
{loading ? (
<>
<HiOutlineServer className="h-4 w-4 animate-spin mr-2" />
Processing...
</>
) : (
<>
Submit
<HiOutlinePaperAirplane className="h-4 w-4 ml-2" />
</>
)}
</Button>
</div>
</form>
</div>
</Card>

{suggestion && (
<Card className="max-w-3xl mx-auto">
<PromptOrderSuggestion
suggestion={suggestion}
onCompare={handleCompare}
/>
</Card>
)}
</div>
)
}

0 comments on commit 0303f0a

Please sign in to comment.