salesiq / frontend /src /components /OpportunityForm.jsx
richlai's picture
added new UI, added form, added api message
3bb94b1
raw
history blame
9.11 kB
import React, { useState } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from './ui/card';
import { Input } from './ui/input';
import { Button } from './ui/button';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from './ui/select';
import { Textarea } from './ui/textarea';
import { AlertCircle } from 'lucide-react';
import { useAuth } from '../services/AuthContext';
const OpportunityForm = () => {
// Generate a simple ID using timestamp and random number
const generateId = () => `opp-${crypto.randomUUID()}`;
const initialFormState = {
opportunityId: generateId(),
customerName: '',
opportunityName: '',
opportunityState: '',
opportunityDescription: '',
opportunityValue: '',
closeDate: '',
customerContact: '',
customerContactRole: '',
activity: '',
nextSteps: ''
};
const [formData, setFormData] = useState(initialFormState);
const [isSubmitting, setIsSubmitting] = useState(false);
const [errors, setErrors] = useState({});
const { token } = useAuth();
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
// Clear error when field is modified
if (errors[name]) {
setErrors(prev => ({ ...prev, [name]: '' }));
}
};
const handleSelectChange = (value) => {
setFormData(prev => ({
...prev,
opportunityState: value
}));
if (errors.opportunityState) {
setErrors(prev => ({ ...prev, opportunityState: '' }));
}
};
const validateForm = () => {
const newErrors = {};
if (!formData.customerName.trim()) newErrors.customerName = 'Customer name is required';
if (!formData.opportunityName.trim()) newErrors.opportunityName = 'Opportunity name is required';
if (!formData.opportunityState) newErrors.opportunityState = 'Opportunity state is required';
if (!formData.opportunityDescription.trim()) newErrors.opportunityDescription = 'Description is required';
if (!formData.opportunityValue) newErrors.opportunityValue = 'Value is required';
if (!formData.closeDate) newErrors.closeDate = 'Close date is required';
if (!formData.customerContact.trim()) newErrors.customerContact = 'Customer contact is required';
if (!formData.customerContactRole.trim()) newErrors.customerContactRole = 'Contact role is required';
if (!formData.activity.trim()) newErrors.activity = 'Activity is required';
if (!formData.nextSteps.trim()) newErrors.nextSteps = 'Next steps are required';
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!validateForm()) {
return;
}
setIsSubmitting(true);
try {
const response = await fetch('/api/save_opportunity', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify(formData),
});
if (!response.ok) {
throw new Error('Submission failed');
}
handleClear();
alert('Form submitted successfully!');
} catch (error) {
alert('Error submitting form: ' + error.message);
} finally {
setIsSubmitting(false);
}
};
const handleClear = () => {
setFormData({
...initialFormState,
opportunityId: generateId()
});
setErrors({});
};
const FormLabel = ({ children, required }) => (
<div className="flex gap-1 text-sm font-medium leading-none mb-2">
{children}
{required && <span className="text-red-500">*</span>}
</div>
);
const ErrorMessage = ({ message }) => message ? (
<div className="flex items-center gap-1 text-red-500 text-sm mt-1">
<AlertCircle className="w-4 h-4" />
<span>{message}</span>
</div>
) : null;
return (
<Card className="w-full max-w-2xl mx-auto">
<CardHeader>
<CardTitle>New Opportunity</CardTitle>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-4">
<input
type="hidden"
name="opportunityId"
value={formData.opportunityId}
/>
<div>
<FormLabel required>Customer Name</FormLabel>
<Input
name="customerName"
value={formData.customerName}
onChange={handleChange}
className={errors.customerName ? 'border-red-500' : ''}
/>
<ErrorMessage message={errors.customerName} />
</div>
<div>
<FormLabel required>Opportunity Name</FormLabel>
<Input
name="opportunityName"
value={formData.opportunityName}
onChange={handleChange}
className={errors.opportunityName ? 'border-red-500' : ''}
/>
<ErrorMessage message={errors.opportunityName} />
</div>
<div>
<FormLabel required>Opportunity State</FormLabel>
<Select
value={formData.opportunityState}
onValueChange={handleSelectChange}
>
<SelectTrigger className={errors.opportunityState ? 'border-red-500' : ''}>
<SelectValue placeholder="Select state" />
</SelectTrigger>
<SelectContent>
<SelectItem value="proposal">Proposal</SelectItem>
<SelectItem value="negotiation">Negotiation</SelectItem>
</SelectContent>
</Select>
<ErrorMessage message={errors.opportunityState} />
</div>
<div>
<FormLabel required>Opportunity Description</FormLabel>
<Textarea
name="opportunityDescription"
value={formData.opportunityDescription}
onChange={handleChange}
className={`h-24 ${errors.opportunityDescription ? 'border-red-500' : ''}`}
/>
<ErrorMessage message={errors.opportunityDescription} />
</div>
<div>
<FormLabel required>Opportunity Value (USD)</FormLabel>
<Input
type="number"
name="opportunityValue"
value={formData.opportunityValue}
onChange={handleChange}
min="0"
step="0.01"
className={errors.opportunityValue ? 'border-red-500' : ''}
/>
<ErrorMessage message={errors.opportunityValue} />
</div>
<div>
<FormLabel required>Close Date</FormLabel>
<Input
type="date"
name="closeDate"
value={formData.closeDate}
onChange={handleChange}
className={errors.closeDate ? 'border-red-500' : ''}
/>
<ErrorMessage message={errors.closeDate} />
</div>
<div>
<FormLabel required>Customer Contact</FormLabel>
<Input
name="customerContact"
value={formData.customerContact}
onChange={handleChange}
className={errors.customerContact ? 'border-red-500' : ''}
/>
<ErrorMessage message={errors.customerContact} />
</div>
<div>
<FormLabel required>Customer Contact Role</FormLabel>
<Input
name="customerContactRole"
value={formData.customerContactRole}
onChange={handleChange}
className={errors.customerContactRole ? 'border-red-500' : ''}
/>
<ErrorMessage message={errors.customerContactRole} />
</div>
<div>
<FormLabel required>Activity</FormLabel>
<Textarea
name="activity"
value={formData.activity}
onChange={handleChange}
className={`h-24 ${errors.activity ? 'border-red-500' : ''}`}
/>
<ErrorMessage message={errors.activity} />
</div>
<div>
<FormLabel required>Next Steps</FormLabel>
<Textarea
name="nextSteps"
value={formData.nextSteps}
onChange={handleChange}
className={`h-24 ${errors.nextSteps ? 'border-red-500' : ''}`}
/>
<ErrorMessage message={errors.nextSteps} />
</div>
<div className="flex gap-4 justify-end">
<Button
type="button"
variant="outline"
onClick={handleClear}
disabled={isSubmitting}
>
Clear
</Button>
<Button
type="submit"
disabled={isSubmitting}
>
{isSubmitting ? 'Submitting...' : 'Submit'}
</Button>
</div>
</form>
</CardContent>
</Card>
);
};
export default OpportunityForm;