feat: quick contact dialog from registratura supports name OR company
- QuickContactDialog now has Company/Organization field - Either name or company is required (same logic as address book) - Auto-sets type to "institution" when only company is provided - Display name in registry form uses company as fallback Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,17 +13,24 @@ import {
|
|||||||
DialogFooter,
|
DialogFooter,
|
||||||
} from "@/shared/components/ui/dialog";
|
} from "@/shared/components/ui/dialog";
|
||||||
|
|
||||||
|
export interface QuickContactData {
|
||||||
|
name: string;
|
||||||
|
company: string;
|
||||||
|
phone: string;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface QuickContactDialogProps {
|
interface QuickContactDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
/** Pre-filled name from the text the user typed */
|
/** Pre-filled name from the text the user typed */
|
||||||
initialName: string;
|
initialName: string;
|
||||||
onConfirm: (data: { name: string; phone: string; email: string }) => void;
|
onConfirm: (data: QuickContactData) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rapid popup for creating a new Address Book contact from Registratura.
|
* Rapid popup for creating a new Address Book contact from Registratura.
|
||||||
* Only requires Name; Phone and Email are optional.
|
* Requires either Name or Company; Phone and Email are optional.
|
||||||
*/
|
*/
|
||||||
export function QuickContactDialog({
|
export function QuickContactDialog({
|
||||||
open,
|
open,
|
||||||
@@ -32,27 +39,34 @@ export function QuickContactDialog({
|
|||||||
onConfirm,
|
onConfirm,
|
||||||
}: QuickContactDialogProps) {
|
}: QuickContactDialogProps) {
|
||||||
const [name, setName] = useState(initialName);
|
const [name, setName] = useState(initialName);
|
||||||
|
const [company, setCompany] = useState("");
|
||||||
const [phone, setPhone] = useState("");
|
const [phone, setPhone] = useState("");
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
|
|
||||||
// Sync name with initialName whenever the dialog opens
|
// Sync name with initialName whenever the dialog opens
|
||||||
// (useState(initialName) only works on first mount; controlled open
|
|
||||||
// bypasses onOpenChange so we need useEffect)
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
setName(initialName);
|
setName(initialName);
|
||||||
|
setCompany("");
|
||||||
setPhone("");
|
setPhone("");
|
||||||
setEmail("");
|
setEmail("");
|
||||||
}
|
}
|
||||||
}, [open, initialName]);
|
}, [open, initialName]);
|
||||||
|
|
||||||
|
const hasIdentifier = name.trim() || company.trim();
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// Stop propagation so the submit doesn't bubble through the React portal
|
// Stop propagation so the submit doesn't bubble through the React portal
|
||||||
// to the outer registry-entry-form, which would close the whole form.
|
// to the outer registry-entry-form, which would close the whole form.
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (!name.trim()) return;
|
if (!hasIdentifier) return;
|
||||||
onConfirm({ name: name.trim(), phone: phone.trim(), email: email.trim() });
|
onConfirm({
|
||||||
|
name: name.trim(),
|
||||||
|
company: company.trim(),
|
||||||
|
phone: phone.trim(),
|
||||||
|
email: email.trim(),
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -65,15 +79,31 @@ export function QuickContactDialog({
|
|||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<form onSubmit={handleSubmit} className="space-y-3">
|
<form onSubmit={handleSubmit} className="space-y-3">
|
||||||
<div>
|
{!hasIdentifier && (
|
||||||
<Label>Nume *</Label>
|
<p className="text-xs text-destructive">
|
||||||
<Input
|
Completează cel puțin Numele sau Compania/Organizația.
|
||||||
value={name}
|
</p>
|
||||||
onChange={(e) => setName(e.target.value)}
|
)}
|
||||||
className="mt-1"
|
<div className="grid gap-3 sm:grid-cols-2">
|
||||||
required
|
<div>
|
||||||
autoFocus
|
<Label>Nume {!company.trim() ? "*" : ""}</Label>
|
||||||
/>
|
<Input
|
||||||
|
value={name}
|
||||||
|
onChange={(e) => setName(e.target.value)}
|
||||||
|
className="mt-1"
|
||||||
|
required={!company.trim()}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label>Companie/Organizație {!name.trim() ? "*" : ""}</Label>
|
||||||
|
<Input
|
||||||
|
value={company}
|
||||||
|
onChange={(e) => setCompany(e.target.value)}
|
||||||
|
className="mt-1"
|
||||||
|
required={!name.trim()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid gap-3 sm:grid-cols-2">
|
<div className="grid gap-3 sm:grid-cols-2">
|
||||||
<div>
|
<div>
|
||||||
@@ -104,7 +134,7 @@ export function QuickContactDialog({
|
|||||||
>
|
>
|
||||||
Anulează
|
Anulează
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="submit" disabled={!name.trim()}>
|
<Button type="submit" disabled={!hasIdentifier}>
|
||||||
Creează contact
|
Creează contact
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ export function RegistraturaModule() {
|
|||||||
const handleCreateContact = useCallback(
|
const handleCreateContact = useCallback(
|
||||||
async (data: {
|
async (data: {
|
||||||
name: string;
|
name: string;
|
||||||
|
company: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
email: string;
|
email: string;
|
||||||
}): Promise<AddressContact | undefined> => {
|
}): Promise<AddressContact | undefined> => {
|
||||||
@@ -101,8 +102,8 @@ export function RegistraturaModule() {
|
|||||||
const contact: AddressContact = {
|
const contact: AddressContact = {
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
name: data.name,
|
name: data.name,
|
||||||
company: "",
|
company: data.company,
|
||||||
type: "collaborator",
|
type: data.company && !data.name ? "institution" : "collaborator",
|
||||||
email: data.email,
|
email: data.email,
|
||||||
email2: "",
|
email2: "",
|
||||||
phone: data.phone,
|
phone: data.phone,
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ interface RegistryEntryFormProps {
|
|||||||
/** Callback to create a new Address Book contact */
|
/** Callback to create a new Address Book contact */
|
||||||
onCreateContact?: (data: {
|
onCreateContact?: (data: {
|
||||||
name: string;
|
name: string;
|
||||||
|
company: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
email: string;
|
email: string;
|
||||||
}) => Promise<AddressContact | undefined>;
|
}) => Promise<AddressContact | undefined>;
|
||||||
@@ -513,15 +514,17 @@ export function RegistryEntryForm({
|
|||||||
|
|
||||||
const handleQuickContactConfirm = async (data: {
|
const handleQuickContactConfirm = async (data: {
|
||||||
name: string;
|
name: string;
|
||||||
|
company: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
email: string;
|
email: string;
|
||||||
}) => {
|
}) => {
|
||||||
if (!onCreateContact) return;
|
if (!onCreateContact) return;
|
||||||
const contact = await onCreateContact(data);
|
const contact = await onCreateContact(data);
|
||||||
if (contact) {
|
if (contact) {
|
||||||
const displayName = contact.company
|
const displayName =
|
||||||
? `${contact.name} (${contact.company})`
|
contact.name && contact.company
|
||||||
: contact.name;
|
? `${contact.name} (${contact.company})`
|
||||||
|
: contact.name || contact.company;
|
||||||
if (quickContactField === "sender") {
|
if (quickContactField === "sender") {
|
||||||
setSender(displayName);
|
setSender(displayName);
|
||||||
setSenderContactId(contact.id);
|
setSenderContactId(contact.id);
|
||||||
|
|||||||
Reference in New Issue
Block a user