mirror of
https://github.com/renee-png/acmcc.git
synced 2026-06-21 01:40:01 +00:00
183fe0a93c
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
231 lines
8.6 KiB
React
231 lines
8.6 KiB
React
import React, { useState, useEffect } from 'react';
|
|
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Label } from '@/components/ui/label';
|
|
import { supabase } from '@/integrations/supabase/client';
|
|
import { useToast } from '@/hooks/use-toast';
|
|
|
|
const STATUS_OPTIONS = [
|
|
"open",
|
|
"in_progress",
|
|
"resolved",
|
|
"closed"
|
|
];
|
|
|
|
const normalizeCollectionStatus = (status) => status === 'resolved' ? 'closed' : status;
|
|
|
|
function CollectionDialog({ open, onOpenChange, collection, onSuccess }) {
|
|
const { toast } = useToast();
|
|
const db = supabase;
|
|
const [associations, setAssociations] = useState([]);
|
|
const [owners, setOwners] = useState([]);
|
|
const [units, setUnits] = useState([]);
|
|
const [formData, setFormData] = useState({
|
|
association_id: '',
|
|
owner_id: '',
|
|
unit_id: '',
|
|
notes: '',
|
|
status: 'open',
|
|
last_notice_date: '',
|
|
amount_owed: ''
|
|
});
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const fetchAssociations = async () => {
|
|
const { data, error } = await db.from('associations').select('id, name').eq('status', 'active').order('name');
|
|
if (!error) setAssociations(data || []);
|
|
};
|
|
if (open) fetchAssociations();
|
|
}, [open]);
|
|
|
|
useEffect(() => {
|
|
if (formData.association_id) {
|
|
const fetchOwnersAndUnits = async () => {
|
|
const [ownersRes, unitsRes] = await Promise.all([
|
|
db.from('owners').select('id, first_name, last_name').eq('association_id', formData.association_id).eq('status', 'active').order('last_name'),
|
|
db.from('units').select('id, unit_number, address').eq('association_id', formData.association_id).order('unit_number'),
|
|
]);
|
|
if (!ownersRes.error) setOwners(ownersRes.data || []);
|
|
if (!unitsRes.error) setUnits(unitsRes.data || []);
|
|
};
|
|
fetchOwnersAndUnits();
|
|
} else {
|
|
setOwners([]);
|
|
setUnits([]);
|
|
}
|
|
}, [formData.association_id]);
|
|
|
|
useEffect(() => {
|
|
if (collection) {
|
|
setFormData({
|
|
association_id: collection.association_id || '',
|
|
owner_id: collection.owner_id || '',
|
|
unit_id: collection.unit_id || '',
|
|
notes: collection.notes || '',
|
|
status: collection.status || 'open',
|
|
last_notice_date: collection.last_notice_date || '',
|
|
amount_owed: collection.amount_owed || ''
|
|
});
|
|
} else {
|
|
setFormData({
|
|
association_id: '', owner_id: '', unit_id: '', notes: '', status: 'open', last_notice_date: '', amount_owed: ''
|
|
});
|
|
}
|
|
}, [collection, open]);
|
|
|
|
const handleSubmit = async (e) => {
|
|
e.preventDefault();
|
|
setLoading(true);
|
|
|
|
if (!formData.association_id) {
|
|
toast({ variant: 'destructive', title: 'Missing Field', description: 'Please select an association.' });
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
const dataToSubmit = {
|
|
...formData,
|
|
status: normalizeCollectionStatus(formData.status),
|
|
last_notice_date: formData.last_notice_date || null,
|
|
amount_owed: formData.amount_owed || 0,
|
|
owner_id: formData.owner_id || null,
|
|
unit_id: formData.unit_id || null,
|
|
};
|
|
|
|
const { error } = collection
|
|
? await db.from('collections').update(dataToSubmit).eq('id', collection.id)
|
|
: await db.from('collections').insert([dataToSubmit]);
|
|
|
|
if (!error) {
|
|
toast({
|
|
title: collection ? 'Collection Updated' : 'Collection Created',
|
|
description: `Collection has been successfully ${collection ? 'updated' : 'created'}.`,
|
|
});
|
|
onSuccess();
|
|
onOpenChange(false);
|
|
} else {
|
|
toast({ variant: 'destructive', title: 'Error', description: error.message });
|
|
}
|
|
setLoading(false);
|
|
};
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent className="sm:max-w-[500px]">
|
|
<DialogHeader>
|
|
<DialogTitle>{collection ? 'Edit Collection' : 'Add New Collection'}</DialogTitle>
|
|
</DialogHeader>
|
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
<div>
|
|
<Label htmlFor="association_id">Association *</Label>
|
|
<select
|
|
id="association_id"
|
|
value={formData.association_id}
|
|
onChange={(e) => setFormData({ ...formData, association_id: e.target.value, owner_id: '' })}
|
|
required
|
|
className="w-full mt-1 px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-teal-500 focus:border-transparent"
|
|
>
|
|
<option value="">Select an association...</option>
|
|
{associations.map((a) => (
|
|
<option key={a.id} value={a.id}>{a.name}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<Label htmlFor="unit_id">Unit</Label>
|
|
<select
|
|
id="unit_id"
|
|
value={formData.unit_id}
|
|
onChange={(e) => setFormData({ ...formData, unit_id: e.target.value })}
|
|
className="w-full mt-1 px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-teal-500 focus:border-transparent"
|
|
disabled={!formData.association_id}
|
|
>
|
|
<option value="">Select a unit...</option>
|
|
{units.map((u) => (
|
|
<option key={u.id} value={u.id}>{u.unit_number}{u.address ? ` - ${u.address}` : ''}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<Label htmlFor="owner_id">Owner</Label>
|
|
<select
|
|
id="owner_id"
|
|
value={formData.owner_id}
|
|
onChange={(e) => setFormData({ ...formData, owner_id: e.target.value })}
|
|
className="w-full mt-1 px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-teal-500 focus:border-transparent"
|
|
disabled={!formData.association_id}
|
|
>
|
|
<option value="">Select an owner...</option>
|
|
{owners.map((o) => (
|
|
<option key={o.id} value={o.id}>{o.first_name} {o.last_name}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<Label htmlFor="status">Status *</Label>
|
|
<select
|
|
id="status"
|
|
value={formData.status}
|
|
onChange={(e) => setFormData({ ...formData, status: e.target.value })}
|
|
required
|
|
className="w-full mt-1 px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-teal-500 focus:border-transparent"
|
|
>
|
|
{STATUS_OPTIONS.map((status) => (
|
|
<option key={status} value={status}>{status}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<Label htmlFor="last_notice_date">Last Notice Date</Label>
|
|
<input
|
|
id="last_notice_date"
|
|
type="date"
|
|
value={formData.last_notice_date}
|
|
onChange={(e) => setFormData({ ...formData, last_notice_date: e.target.value })}
|
|
className="w-full mt-1 px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-teal-500 focus:border-transparent"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<Label htmlFor="amount_owed">Amount Owed ($)</Label>
|
|
<input
|
|
id="amount_owed"
|
|
type="number"
|
|
step="0.01"
|
|
min="0"
|
|
placeholder="0.00"
|
|
value={formData.amount_owed}
|
|
onChange={(e) => setFormData({ ...formData, amount_owed: e.target.value })}
|
|
className="w-full mt-1 px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-teal-500 focus:border-transparent"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<Label htmlFor="notes">Notes</Label>
|
|
<textarea
|
|
id="notes"
|
|
value={formData.notes}
|
|
onChange={(e) => setFormData({ ...formData, notes: e.target.value })}
|
|
rows={3}
|
|
className="w-full mt-1 px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-teal-500 focus:border-transparent"
|
|
/>
|
|
</div>
|
|
<div className="flex justify-end space-x-2 pt-4">
|
|
<Button type="button" variant="outline" onClick={() => onOpenChange(false)}>Cancel</Button>
|
|
<Button type="submit" disabled={loading}>
|
|
{loading ? 'Saving...' : collection ? 'Update' : 'Create'}
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|
|
|
|
export default CollectionDialog; |