KEMBAR78
QuotationFormDialog TSX | PDF | Software Engineering | Computer Programming
0% found this document useful (0 votes)
9 views7 pages

QuotationFormDialog TSX

Uploaded by

Nirav Virja
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views7 pages

QuotationFormDialog TSX

Uploaded by

Nirav Virja
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 7

import { useState, useEffect } from 'react';

import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle,


DialogTrigger } from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from
'@/components/ui/select';
import { Add01Icon } from 'hugeicons-react';
import { Client, EventType, Quotation } from '@/types/studio';
import { InlineDatePicker } from '@/components/ui/inline-date-picker';
import { useAuth } from '@/components/auth/AuthProvider';
import { supabase } from '@/integrations/supabase/client';

interface QuotationFormData {
title: string;
client_id: string;
event_type: EventType;
event_date: Date | undefined;
venue: string;
description: string;
valid_until: Date | undefined;
}

interface QuotationFormDialogProps {
clients: Client[];
isOpen: boolean;
onOpenChange: (open: boolean) => void;
onSubmit: (formData: QuotationFormData) => void;
onNewQuotation: () => void;
editingQuotation?: Quotation | null;
formData?: {
title: string;
client_id: string;
event_type: EventType;
event_date: string;
venue: string;
description: string;
valid_until: string;
};
}

const eventTypes: EventType[] = ['Ring-Ceremony', 'Pre-Wedding', 'Wedding',


'Maternity Photography', 'Others'];

const QuotationFormDialog = ({ clients, isOpen, onOpenChange, onSubmit,


onNewQuotation, editingQuotation, formData: initialFormData }:
QuotationFormDialogProps) => {
const { currentFirmId } = useAuth();
const [venueSuggestions, setVenueSuggestions] = useState<string[]>([]);
const [showVenueSuggestions, setShowVenueSuggestions] = useState(false);
const [formData, setFormData] = useState<QuotationFormData>({
title: '',
client_id: '',
event_type: 'Wedding',
event_date: undefined,
venue: '',
description: '',
valid_until: undefined
});

// Initialize form data when editing


useEffect(() => {
if (editingQuotation && initialFormData) {
setFormData({
title: initialFormData.title,
client_id: initialFormData.client_id,
event_type: initialFormData.event_type as EventType,
event_date: initialFormData.event_date ? new
Date(initialFormData.event_date) : undefined,
venue: initialFormData.venue,
description: initialFormData.description,
valid_until: initialFormData.valid_until ? new
Date(initialFormData.valid_until) : undefined
});
} else if (!editingQuotation) {
// Reset to default when not editing
setFormData({
title: '',
client_id: '',
event_type: 'Wedding',
event_date: undefined,
venue: '',
description: '',
valid_until: undefined
});
}
}, [editingQuotation, initialFormData, isOpen]);

// Calculate default expiry date based on event date


const calculateDefaultExpiry = (eventDate: Date) => {
const today = new Date();
const thirtyDaysFromToday = new Date(today.getTime() + 30 * 24 * 60 * 60 *
1000);
const oneDayBeforeEvent = new Date(eventDate.getTime() - 1 * 24 * 60 * 60 *
1000);

// Return whichever is shorter


return thirtyDaysFromToday < oneDayBeforeEvent ? thirtyDaysFromToday :
oneDayBeforeEvent;
};

// Load venue suggestions from database


const loadVenueSuggestions = async () => {
if (!currentFirmId) return;

try {
// Get unique venues from both events and quotations
const [eventsData, quotationsData] = await Promise.all([
supabase
.from('events')
.select('venue')
.eq('firm_id', currentFirmId)
.not('venue', 'is', null)
.not('venue', 'eq', ''),
supabase
.from('quotations')
.select('venue')
.eq('firm_id', currentFirmId)
.not('venue', 'is', null)
.not('venue', 'eq', '')
]);

const venues = new Set<string>();

if (eventsData.data) {
eventsData.data.forEach(item => {
if (item.venue && item.venue.trim()) venues.add(item.venue.trim());
});
}

if (quotationsData.data) {
quotationsData.data.forEach(item => {
if (item.venue && item.venue.trim()) venues.add(item.venue.trim());
});
}

setVenueSuggestions(Array.from(venues).sort());
} catch (error) {
console.error('Error loading venue suggestions:', error);
}
};

useEffect(() => {
if (isOpen) {
loadVenueSuggestions();
}
}, [isOpen, currentFirmId]);

// Auto-set expiry date when event date changes (only for new forms, not editing)
useEffect(() => {
if (formData.event_date && isOpen && !editingQuotation) {
const defaultExpiry = calculateDefaultExpiry(formData.event_date);
setFormData(prev => ({ ...prev, valid_until: defaultExpiry }));
}
}, [formData.event_date, isOpen, editingQuotation]);

// Validate event date (must be future date)


const isValidEventDate = (date: Date | undefined) => {
if (!date) return true; // Allow empty date
const today = new Date();
today.setHours(23, 59, 59, 999); // End of today
return date > today;
};

const resetForm = () => {


if (!editingQuotation) {
setFormData({
title: '',
client_id: '',
event_type: 'Wedding',
event_date: undefined,
venue: '',
description: '',
valid_until: undefined
});
}
setShowVenueSuggestions(false);
};

const handleSubmit = (e: React.FormEvent) => {


e.preventDefault();

// Validate event date


if (formData.event_date && !isValidEventDate(formData.event_date)) {
return; // Don't submit if event date is invalid
}

// Validate required fields


if (!formData.title || !formData.client_id || !formData.event_date) {
return; // Don't submit if required fields are missing
}

onSubmit(formData);
resetForm();
};

const handleOpenChange = (open: boolean) => {


onOpenChange(open);
if (!open) resetForm();
};

return (
<Dialog open={isOpen} onOpenChange={handleOpenChange}>
<DialogTrigger asChild>
<Button onClick={onNewQuotation} className="rounded-full p-3">
<Add01Icon className="h-4 w-4" />
</Button>
</DialogTrigger>
<DialogContent className="w-[95vw] max-w-[500px] md:max-w-[600px] max-h-
[70vh] md:max-h-[90vh] overflow-y-auto mx-auto">
<DialogHeader>
<DialogTitle>{editingQuotation ? 'Edit Quotation' : 'Create New
Quotation'}</DialogTitle>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="title">Quotation Title *</Label>
<Input
id="title"
placeholder="Enter quotation title"
value={formData.title}
onChange={(e) => setFormData({ ...formData, title:
e.target.value })}
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="client">Client *</Label>
<Select
value={formData.client_id}
onValueChange={(value) => setFormData({ ...formData, client_id:
value })}
>
<SelectTrigger>
<SelectValue placeholder="Select a client" />
</SelectTrigger>
<SelectContent>
{clients.map((client) => (
<SelectItem key={client.id} value={client.id}>
{client.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>

<div className="grid grid-cols-1 md:grid-cols-2 gap-4">


<div className="space-y-2">
<Label htmlFor="event_type">Event Type *</Label>
<Select
value={formData.event_type}
onValueChange={(value: EventType) => setFormData({ ...formData,
event_type: value })}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
{eventTypes.map((type) => (
<SelectItem key={type} value={type}>
{type}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="event_date">Event Date *</Label>
<div className="h-10">
<InlineDatePicker
onSelect={(date) => {
setFormData({ ...formData, event_date: date });
}}
value={formData.event_date}
placeholder="DD/MM/YYYY"
/>
</div>
{formData.event_date && !isValidEventDate(formData.event_date) && (
<p className="text-sm text-destructive">Event date must be in the
future</p>
)}
</div>
</div>

<div className="grid grid-cols-1 md:grid-cols-2 gap-4">


<div className="space-y-2">
<Label htmlFor="venue">Venue</Label>
<div className="relative">
<Input
id="venue"
placeholder="Type venue name..."
value={formData.venue}
onFocus={() => setShowVenueSuggestions(true)}
onBlur={() => {
// Delay hiding to allow clicking on suggestions
setTimeout(() => setShowVenueSuggestions(false), 150);
}}
onChange={(e) => {
setFormData({ ...formData, venue: e.target.value });
setShowVenueSuggestions(true);
}}
className="pr-8"
/>
{showVenueSuggestions && formData.venue && venueSuggestions.length
> 0 && (
<div className="absolute top-full left-0 right-0 z-50 mt-1 max-h-
48 overflow-auto bg-background border border-border rounded-md shadow-lg">
{venueSuggestions
.filter(venue =>
venue.toLowerCase().includes(formData.venue.toLowerCase()))
.slice(0, 10)
.map((venue, index) => (
<div
key={index}
className="px-3 py-2 cursor-pointer hover:bg-muted text-
sm"
onMouseDown={(e) => {
e.preventDefault(); // Prevent blur
setFormData({ ...formData, venue });
setShowVenueSuggestions(false);
}}
>
{venue}
</div>
))
}
</div>
)}
</div>
</div>
<div className="space-y-2">
<Label htmlFor="valid_until">Valid Until</Label>
<div className="h-10">
<InlineDatePicker
onSelect={(date) => setFormData({ ...formData, valid_until:
date })}
value={formData.valid_until}
placeholder="DD/MM/YYYY"
/>
</div>
</div>
</div>

<div className="space-y-2">
<Label htmlFor="description">Description</Label>
<Textarea
id="description"
placeholder="Enter quotation description"
value={formData.description}
onChange={(e) => setFormData({ ...formData, description:
e.target.value })}
rows={3}
/>
</div>
<div className="grid grid-cols-2 gap-3 pt-4 border-t">
<Button
type="button"
variant="outline"
onClick={() => onOpenChange(false)}
className="rounded-full"
>
Cancel
</Button>
<Button type="submit" className="rounded-full">
{editingQuotation ? 'Update' : 'Continue'}
</Button>
</div>
</form>
</DialogContent>
</Dialog>
);
};

export default QuotationFormDialog;

You might also like