Improve broadcast dialog and product selector UI

Enhanced the broadcast dialog with better product selection UX, including a 'Done' button and improved selected products display. Updated the product selector to show more concise product descriptions, adjusted scroll area height, and improved price styling for clarity.
This commit is contained in:
NotII
2025-07-30 16:05:46 +02:00
parent 4d1c37de92
commit 5b78e4f86c
3 changed files with 51 additions and 33 deletions

View File

@@ -6,7 +6,6 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "
import { Send, Bold, Italic, Code, Link as LinkIcon, Image as ImageIcon, X, Eye, EyeOff, Package } from "lucide-react"; import { Send, Bold, Italic, Code, Link as LinkIcon, Image as ImageIcon, X, Eye, EyeOff, Package } from "lucide-react";
import { toast } from "sonner"; import { toast } from "sonner";
import { apiRequest } from "@/lib/api"; import { apiRequest } from "@/lib/api";
import { cn } from "@/lib/utils/general";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import ProductSelector from "./product-selector"; import ProductSelector from "./product-selector";
@@ -30,7 +29,8 @@ export default function BroadcastDialog({ open, setOpen }: BroadcastDialogProps)
const handleImageSelect = (event: React.ChangeEvent<HTMLInputElement>) => { const handleImageSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0]; const file = event.target.files?.[0];
if (file) { if (file) {
if (file.size > 10 * 1024 * 1024) { // 10MB limit // Magic 10 MB Limit
if (file.size > 10 * 1024 * 1024) {
toast.error("Image size must be less than 10MB"); toast.error("Image size must be less than 10MB");
return; return;
} }
@@ -280,8 +280,17 @@ __italic text__
)} )}
{showProductSelector && ( {showProductSelector && (
<div className="border rounded-lg p-4"> <div className="border rounded-lg p-3">
<h4 className="font-medium mb-3">Select Products to Include</h4> <div className="flex items-center justify-between mb-3">
<h4 className="font-medium text-sm">Select Products to Include</h4>
<Button
variant="ghost"
size="sm"
onClick={() => setShowProductSelector(false)}
>
Done
</Button>
</div>
<ProductSelector <ProductSelector
selectedProducts={selectedProducts} selectedProducts={selectedProducts}
onSelectionChange={setSelectedProducts} onSelectionChange={setSelectedProducts}
@@ -290,18 +299,22 @@ __italic text__
)} )}
{selectedProducts.length > 0 && !showProductSelector && ( {selectedProducts.length > 0 && !showProductSelector && (
<div className="border rounded-lg p-3 bg-muted/50"> <div className="border rounded-lg p-3 bg-blue-50 dark:bg-blue-950/20">
<div className="flex items-center justify-between mb-2"> <div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2">
<Package className="h-4 w-4 text-blue-600 dark:text-blue-400" />
<span className="text-sm font-medium">Selected Products ({selectedProducts.length})</span> <span className="text-sm font-medium">Selected Products ({selectedProducts.length})</span>
</div>
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={() => setShowProductSelector(true)} onClick={() => setShowProductSelector(true)}
className="text-blue-600 hover:text-blue-700 dark:text-blue-400"
> >
Edit Edit
</Button> </Button>
</div> </div>
<div className="text-sm text-muted-foreground"> <div className="text-xs text-muted-foreground">
Products will be added as interactive buttons in the broadcast message. Products will be added as interactive buttons in the broadcast message.
</div> </div>
</div> </div>

View File

@@ -77,7 +77,7 @@ export default function ProductSelector({ selectedProducts, onSelectionChange }:
/> />
</div> </div>
<ScrollArea className="h-64"> <ScrollArea className="h-48">
<div className="space-y-2"> <div className="space-y-2">
{filteredProducts.length === 0 ? ( {filteredProducts.length === 0 ? (
<div className="text-center py-8 text-muted-foreground"> <div className="text-center py-8 text-muted-foreground">
@@ -88,23 +88,28 @@ export default function ProductSelector({ selectedProducts, onSelectionChange }:
filteredProducts.map((product) => ( filteredProducts.map((product) => (
<div <div
key={product._id} key={product._id}
className="flex items-center space-x-3 p-3 border rounded-lg hover:bg-muted/50" className="flex items-start space-x-3 p-3 border rounded-lg hover:bg-muted/50"
> >
<Checkbox <Checkbox
checked={selectedProducts.includes(product._id)} checked={selectedProducts.includes(product._id)}
onCheckedChange={() => handleProductToggle(product._id)} onCheckedChange={() => handleProductToggle(product._id)}
className="mt-0.5"
/> />
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="flex items-center justify-between"> <div className="flex items-start justify-between gap-2">
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<p className="font-medium truncate">{product.name}</p> <p className="font-medium text-sm leading-tight mb-1">
{product.name}
</p>
{product.description && ( {product.description && (
<p className="text-sm text-muted-foreground truncate"> <p className="text-xs text-muted-foreground leading-tight line-clamp-2">
{product.description} {product.description.length > 80
? `${product.description.substring(0, 80)}...`
: product.description}
</p> </p>
)} )}
</div> </div>
<div className="text-sm text-muted-foreground ml-2"> <div className="text-sm font-medium text-green-600 dark:text-green-400 flex-shrink-0">
£{getMinPrice(product).toFixed(2)} £{getMinPrice(product).toFixed(2)}
</div> </div>
</div> </div>

View File

@@ -1,4 +1,4 @@
{ {
"commitHash": "2452a3c", "commitHash": "4d1c37d",
"buildTime": "2025-07-28T20:50:17.059Z" "buildTime": "2025-07-30T14:02:04.518Z"
} }