feat: window management system with minimize, fullscreen, and AI chat split
- Window manager store (Zustand) for managing open/minimized/fullscreen windows - Window component with header controls: KI-Chat toggle, fullscreen, minimize, close - AI chat panel with streaming chat via existing /ai/sessions API - WindowContainer renders all non-minimized windows - TopBar shows minimized windows as clickable pills - ContactEditForm extracted from ContactEditModal for window rendering - ContactsList and ContactDetailPage use openWindow() instead of modal state - Draggable windows with z-index management
This commit is contained in:
@@ -6,7 +6,8 @@ import React from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ContactDetail } from '@/components/contacts/ContactDetail';
|
||||
import { ContactEditModal } from '@/components/contacts/ContactEditModal';
|
||||
import { ContactEditForm } from '@/components/contacts/ContactEditForm';
|
||||
import { useWindowStore } from '@/store/windowStore';
|
||||
import { useUnifiedContact, type UnifiedContact } from '@/api/hooks';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { ChevronLeft } from 'lucide-react';
|
||||
@@ -16,13 +17,20 @@ export function ContactDetailPage() {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const navigate = useNavigate();
|
||||
const { data: contact, isLoading } = useUnifiedContact(id);
|
||||
const [editModalOpen, setEditModalOpen] = React.useState(false);
|
||||
const [editingContact, setEditingContact] = React.useState<UnifiedContact | null>(null);
|
||||
const openWindow = useWindowStore((s) => s.openWindow);
|
||||
|
||||
const handleEdit = () => {
|
||||
if (contact) {
|
||||
setEditingContact(contact);
|
||||
setEditModalOpen(true);
|
||||
openWindow({
|
||||
title: t('contacts.edit'),
|
||||
type: 'contact-edit',
|
||||
component: ContactEditForm,
|
||||
componentProps: {
|
||||
contact,
|
||||
onClose: () => {},
|
||||
onSaved: () => {},
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -30,11 +38,6 @@ export function ContactDetailPage() {
|
||||
navigate('/contacts');
|
||||
};
|
||||
|
||||
const handleSaved = () => {
|
||||
setEditModalOpen(false);
|
||||
setEditingContact(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full" data-testid="contact-detail-page">
|
||||
<div className="flex items-center gap-2 px-4 py-2 border-b border-secondary-200 bg-white">
|
||||
@@ -55,12 +58,6 @@ export function ContactDetailPage() {
|
||||
onDeleted={handleDeleted}
|
||||
/>
|
||||
</div>
|
||||
<ContactEditModal
|
||||
open={editModalOpen}
|
||||
onClose={() => setEditModalOpen(false)}
|
||||
contact={editingContact}
|
||||
onSaved={handleSaved}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ import { usePluginToolbarStore } from '@/store/pluginToolbarStore';
|
||||
import { ContactFolderTree, type ContactFilter } from '@/components/contacts/ContactFolderTree';
|
||||
import { ContactList, type ContactViewMode } from '@/components/contacts/ContactList';
|
||||
import { ContactDetail } from '@/components/contacts/ContactDetail';
|
||||
import { ContactEditModal } from '@/components/contacts/ContactEditModal';
|
||||
import { ContactEditForm } from '@/components/contacts/ContactEditForm';
|
||||
import { useWindowStore } from '@/store/windowStore';
|
||||
import { SavedFilters } from '@/components/SavedFilters';
|
||||
import { ArrowDownAZ, ArrowUpZA, ChevronLeft, LayoutGrid, List, Plus } from 'lucide-react';
|
||||
import {
|
||||
@@ -39,8 +40,7 @@ export function ContactsListPage() {
|
||||
const [viewMode, setViewMode] = useState<ContactViewMode>('list');
|
||||
const [selectedContactId, setSelectedContactId] = useState<string | null>(null);
|
||||
const [activeView, setActiveView] = useState<'folders' | 'list' | 'detail'>('folders');
|
||||
const [editModalOpen, setEditModalOpen] = useState(false);
|
||||
const [editingContact, setEditingContact] = useState<UnifiedContact | null>(null);
|
||||
const openWindow = useWindowStore((s) => s.openWindow);
|
||||
|
||||
// Debounce search
|
||||
useEffect(() => {
|
||||
@@ -134,19 +134,40 @@ export function ContactsListPage() {
|
||||
setPage(1);
|
||||
}, []);
|
||||
|
||||
// Handle edit modal saved
|
||||
const handleSaved = useCallback((id: string) => {
|
||||
setSelectedContactId(id);
|
||||
setActiveView('detail');
|
||||
}, []);
|
||||
|
||||
// Handle create
|
||||
const handleCreate = useCallback(() => {
|
||||
setEditingContact(null);
|
||||
setEditModalOpen(true);
|
||||
}, []);
|
||||
openWindow({
|
||||
title: t('contacts.create'),
|
||||
type: 'contact-create',
|
||||
component: ContactEditForm,
|
||||
componentProps: {
|
||||
onClose: () => {},
|
||||
onSaved: handleSaved,
|
||||
},
|
||||
});
|
||||
}, [openWindow, t, handleSaved]);
|
||||
|
||||
// Handle edit
|
||||
const handleEdit = useCallback(() => {
|
||||
if (selectedContact) {
|
||||
setEditingContact(selectedContact);
|
||||
setEditModalOpen(true);
|
||||
openWindow({
|
||||
title: t('contacts.edit'),
|
||||
type: 'contact-edit',
|
||||
component: ContactEditForm,
|
||||
componentProps: {
|
||||
contact: selectedContact,
|
||||
onClose: () => {},
|
||||
onSaved: handleSaved,
|
||||
},
|
||||
});
|
||||
}
|
||||
}, [selectedContact]);
|
||||
}, [selectedContact, openWindow, t, handleSaved]);
|
||||
|
||||
// Handle delete (from detail)
|
||||
const handleDeleted = useCallback(() => {
|
||||
@@ -154,12 +175,6 @@ export function ContactsListPage() {
|
||||
setActiveView('list');
|
||||
}, []);
|
||||
|
||||
// Handle edit modal saved
|
||||
const handleSaved = useCallback((id: string) => {
|
||||
setSelectedContactId(id);
|
||||
setActiveView('detail');
|
||||
}, []);
|
||||
|
||||
// Register toolbar items
|
||||
const registerItems = usePluginToolbarStore((s) => s.registerItems);
|
||||
const unregisterPlugin = usePluginToolbarStore((s) => s.unregisterPlugin);
|
||||
@@ -458,13 +473,7 @@ export function ContactsListPage() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Edit/Create Modal */}
|
||||
<ContactEditModal
|
||||
open={editModalOpen}
|
||||
onClose={() => setEditModalOpen(false)}
|
||||
contact={editingContact}
|
||||
onSaved={handleSaved}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user