Files
Krow-workspace/apps/web/src/features/business/clients/AddClient.tsx

381 lines
14 KiB
TypeScript

import { Button } from "@/common/components/ui/button";
import { Input } from "@/common/components/ui/input";
import { Label } from "@/common/components/ui/label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/common/components/ui/select";
import { Textarea } from "@/common/components/ui/textarea";
import DashboardLayout from "@/features/layouts/DashboardLayout";
import { ArrowLeft, Loader2, Save, X, Mail } from "lucide-react";
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import { useQueryClient } from "@tanstack/react-query";
import { useSelector } from "react-redux";
import type { RootState } from "@/store/store";
import {
useCreateBusiness,
useCreateTeamHub
} from "@/dataconnect-generated/react";
import {
BusinessArea,
BusinessSector,
BusinessStatus,
BusinessRateGroup
} from "@/dataconnect-generated";
import { dataConnect } from "@/features/auth/firebase";
import { motion, AnimatePresence } from "framer-motion";
export default function AddClient() {
const navigate = useNavigate();
const queryClient = useQueryClient();
const { user } = useSelector((state: RootState) => state.auth);
const [showSnackbar, setShowSnackbar] = useState(false);
const [snackbarMessage, setSnackbarMessage] = useState("");
const [formData, setFormData] = useState({
businessName: "",
companyLogoUrl: "",
contactName: "",
phone: "",
email: "",
hubBuilding: "",
address: "",
city: "",
area: BusinessArea.BAY_AREA,
sector: BusinessSector.OTHER,
rateGroup: BusinessRateGroup.STANDARD,
status: BusinessStatus.ACTIVE,
notes: ""
});
const { mutateAsync: createBusiness, isPending: isCreatingBusiness } = useCreateBusiness(dataConnect);
const { mutateAsync: createHub, isPending: isCreatingHub } = useCreateTeamHub(dataConnect);
const handleChange = (field: string, value: any) => {
setFormData(prev => ({ ...prev, [field]: value }));
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!user?.uid) return;
try {
// 1. Create the business record
const businessResult = await createBusiness({
businessName: formData.businessName,
contactName: formData.contactName,
userId: user.uid,
companyLogoUrl: formData.companyLogoUrl,
phone: formData.phone,
email: formData.email,
hubBuilding: formData.hubBuilding,
address: formData.address,
city: formData.city,
area: formData.area,
sector: formData.sector,
rateGroup: formData.rateGroup,
status: formData.status,
notes: formData.notes
});
const businessId = businessResult.business_insert.id;
// 2. Automatically create the client's first "hub" or location
await createHub({
teamId: businessId,
hubName: `${formData.businessName} - Main Hub`,
address: formData.address || "Main Office",
city: formData.city,
isActive: true
});
// 3. Show snackbar for welcome email
setSnackbarMessage(`Welcome email sent to ${formData.contactName} (${formData.email})`);
setShowSnackbar(true);
// Invalidate queries and navigate after a delay to show snackbar
queryClient.invalidateQueries({ queryKey: ['businesses'] });
setTimeout(() => {
navigate("/clients");
}, 3000);
} catch (error) {
console.error("Error creating client partnership:", error);
setSnackbarMessage("Failed to create client partnership. Please try again.");
setShowSnackbar(true);
}
};
const isPending = isCreatingBusiness || isCreatingHub;
return (
<DashboardLayout
title="Register Business"
subtitle="Initialize a new client partnership and hub location."
actions={
<Button
variant="outline"
onClick={() => navigate("/clients")}
leadingIcon={<ArrowLeft />}
>
Back to Directory
</Button>
}
>
<div className=" mx-auto pb-20 relative">
<form onSubmit={handleSubmit}>
<div className="py-8 space-y-8">
{/* Business Name & Company Logo */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<Label htmlFor="businessName">
Business Name <span className="text-red-500">*</span>
</Label>
<Input
id="businessName"
value={formData.businessName}
onChange={(e) => handleChange('businessName', e.target.value)}
placeholder="Enter business name"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="companyLogoUrl">
Company Logo URL
</Label>
<Input
id="companyLogoUrl"
value={formData.companyLogoUrl}
onChange={(e) => handleChange('companyLogoUrl', e.target.value)}
placeholder="https://example.com/logo.png"
/>
<p className="text-xs text-muted-text">Optional: URL to company logo image</p>
</div>
</div>
{/* Primary Contact */}
<div className="space-y-2">
<Label htmlFor="contactName">
Primary Contact <span className="text-red-500">*</span>
</Label>
<Input
id="contactName"
value={formData.contactName}
onChange={(e) => handleChange('contactName', e.target.value)}
placeholder="Contact name"
required
/>
</div>
{/* Contact Number & Email */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<Label htmlFor="phone">
Contact Number
</Label>
<Input
id="phone"
type="tel"
value={formData.phone}
onChange={(e) => handleChange('phone', e.target.value)}
placeholder="(555) 123-4567"
/>
</div>
<div className="space-y-2">
<Label htmlFor="email">
Email <span className="text-red-500">*</span>
</Label>
<Input
id="email"
type="email"
value={formData.email}
onChange={(e) => handleChange('email', e.target.value)}
placeholder="business@example.com"
required
/>
</div>
</div>
{/* Hub / Building */}
<div className="space-y-2">
<Label htmlFor="hubBuilding">
Hub / Building
</Label>
<Input
id="hubBuilding"
value={formData.hubBuilding}
onChange={(e) => handleChange('hubBuilding', e.target.value)}
placeholder="Building name or location"
/>
</div>
{/* Billing Address */}
<div className="space-y-2">
<Label htmlFor="address">
Billing Address <span className="text-red-500">*</span>
</Label>
<Input
id="address"
value={formData.address}
onChange={(e) => handleChange('address', e.target.value)}
placeholder="Street address"
required
/>
</div>
{/* City & Area */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<Label htmlFor="city">
City <span className="text-red-500">*</span>
</Label>
<Input
id="city"
value={formData.city}
onChange={(e) => handleChange('city', e.target.value)}
placeholder="City"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="area">
Area
</Label>
<Select value={formData.area} onValueChange={(value : any) => handleChange('area', value)}>
<SelectTrigger>
<SelectValue placeholder="Select area" />
</SelectTrigger>
<SelectContent>
<SelectItem value={BusinessArea.BAY_AREA}>Bay Area</SelectItem>
<SelectItem value={BusinessArea.SOUTHERN_CALIFORNIA}>Southern California</SelectItem>
<SelectItem value={BusinessArea.NORTHERN_CALIFORNIA}>Northern California</SelectItem>
<SelectItem value={BusinessArea.CENTRAL_VALLEY}>Central Valley</SelectItem>
<SelectItem value={BusinessArea.OTHER}>Other</SelectItem>
</SelectContent>
</Select>
</div>
</div>
{/* Sector & Rate Group */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<Label htmlFor="sector">
Industry / Sector <span className="text-red-500">*</span>
</Label>
<Select value={formData.sector} onValueChange={(value :any) => handleChange('sector', value)}>
<SelectTrigger>
<SelectValue placeholder="Select sector" />
</SelectTrigger>
<SelectContent>
<SelectItem value={BusinessSector.BON_APPETIT}>Bon Appétit</SelectItem>
<SelectItem value={BusinessSector.EUREST}>Eurest</SelectItem>
<SelectItem value={BusinessSector.ARAMARK}>Aramark</SelectItem>
<SelectItem value={BusinessSector.EPICUREAN_GROUP}>Epicurean Group</SelectItem>
<SelectItem value={BusinessSector.CHARTWELLS}>Chartwells</SelectItem>
<SelectItem value={BusinessSector.OTHER}>Other</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="rateGroup">
Rate Group <span className="text-red-500">*</span>
</Label>
<Select value={formData.rateGroup} onValueChange={(value : any) => handleChange('rateGroup', value)} required>
<SelectTrigger>
<SelectValue placeholder="Select pricing tier" />
</SelectTrigger>
<SelectContent>
<SelectItem value={BusinessRateGroup.STANDARD}>Standard</SelectItem>
<SelectItem value={BusinessRateGroup.PREMIUM}>Premium</SelectItem>
<SelectItem value={BusinessRateGroup.ENTERPRISE}>Enterprise</SelectItem>
<SelectItem value={BusinessRateGroup.CUSTOM}>Custom</SelectItem>
</SelectContent>
</Select>
</div>
</div>
{/* Status */}
<div className="space-y-2">
<Label htmlFor="status">
Status
</Label>
<Select value={formData.status} onValueChange={(value : any) => handleChange('status', value)}>
<SelectTrigger>
<SelectValue placeholder="Select status" />
</SelectTrigger>
<SelectContent>
<SelectItem value={BusinessStatus.ACTIVE}>Active</SelectItem>
<SelectItem value={BusinessStatus.INACTIVE}>Inactive</SelectItem>
<SelectItem value={BusinessStatus.PENDING}>Pending</SelectItem>
</SelectContent>
</Select>
</div>
{/* Notes */}
<div className="space-y-2">
<Label htmlFor="notes">Notes</Label>
<Textarea
id="notes"
value={formData.notes}
onChange={(e) => handleChange('notes', e.target.value)}
rows={4}
placeholder="Additional notes about this business..."
/>
</div>
</div>
<div className="flex justify-end gap-3 mt-8">
<Button
type="button"
variant="outline"
onClick={() => navigate("/clients")}
>
Cancel
</Button>
<Button
type="submit"
disabled={isPending}
leadingIcon={isPending ? <Loader2 className="animate-spin" /> : <Save />}
>
{isPending ? "Initializing Partnership..." : "Create Business Client"}
</Button>
</div>
</form>
{/* Snackbar Notification */}
<AnimatePresence>
{showSnackbar && (
<motion.div
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: 50 }}
className="fixed bottom-8 left-1/2 -translate-x-1/2 z-50 flex items-center gap-3 bg-slate-900 text-white px-6 py-4 rounded-2xl shadow-2xl border border-white/10 min-w-[320px]"
>
<div className="p-2 bg-emerald-500/20 rounded-lg text-emerald-400">
<Mail size={18} />
</div>
<p className="text-sm font-bold flex-1">{snackbarMessage}</p>
<button
onClick={() => setShowSnackbar(false)}
className="p-1 hover:bg-white/10 rounded-full transition-colors"
>
<X size={16} />
</button>
</motion.div>
)}
</AnimatePresence>
</div>
</DashboardLayout>
);
}