feat(registratura): 3.02 bidirectional integration, simplified status, threads
- Dynamic document types: string-based DocumentType synced with Tag Manager (new types auto-create tags under 'document-type' category) - Added default types: 'Apel telefonic', 'Videoconferinta' - Bidirectional Address Book: quick-create contacts from sender/recipient/ assignee fields via QuickContactDialog popup - Simplified status: Switch toggle replaces dropdown (default open) - Responsabil (Assignee) field with contact autocomplete (ERP-ready) - Entry threads: threadParentId links entries as replies, ThreadView shows parent/current/children tree with branching support - Info tooltips on deadline, status, and assignee fields - New Resp. column and thread icon in registry table - All changes backward-compatible with existing data
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { UserPlus } from "lucide-react";
|
||||
import { Input } from "@/shared/components/ui/input";
|
||||
import { Label } from "@/shared/components/ui/label";
|
||||
import { Button } from "@/shared/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogFooter,
|
||||
} from "@/shared/components/ui/dialog";
|
||||
|
||||
interface QuickContactDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
/** Pre-filled name from the text the user typed */
|
||||
initialName: string;
|
||||
onConfirm: (data: { name: string; phone: string; email: string }) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rapid popup for creating a new Address Book contact from Registratura.
|
||||
* Only requires Name; Phone and Email are optional.
|
||||
*/
|
||||
export function QuickContactDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
initialName,
|
||||
onConfirm,
|
||||
}: QuickContactDialogProps) {
|
||||
const [name, setName] = useState(initialName);
|
||||
const [phone, setPhone] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
|
||||
// Reset when dialog opens with new name
|
||||
const handleOpenChange = (o: boolean) => {
|
||||
if (o) {
|
||||
setName(initialName);
|
||||
setPhone("");
|
||||
setEmail("");
|
||||
}
|
||||
onOpenChange(o);
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!name.trim()) return;
|
||||
onConfirm({ name: name.trim(), phone: phone.trim(), email: email.trim() });
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={handleOpenChange}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<UserPlus className="h-4 w-4" />
|
||||
Contact nou rapid
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleSubmit} className="space-y-3">
|
||||
<div>
|
||||
<Label>Nume *</Label>
|
||||
<Input
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
className="mt-1"
|
||||
required
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<div>
|
||||
<Label>Telefon</Label>
|
||||
<Input
|
||||
value={phone}
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
className="mt-1"
|
||||
placeholder="Opțional"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label>Email</Label>
|
||||
<Input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="mt-1"
|
||||
placeholder="Opțional"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={() => onOpenChange(false)}
|
||||
>
|
||||
Anulează
|
||||
</Button>
|
||||
<Button type="submit" disabled={!name.trim()}>
|
||||
Creează contact
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user