feat: standalone buttons for all plugin pages + window system for modals
Standalone buttons: - Add ExternalLink button to Dms, Calendar, Mail, ContactsList toolbars - Create standalone pages for each (no sidebar, full screen) - Add standalone routes outside ProtectedRoute Window system for modals: - AppointmentModal -> AppointmentEditForm + openWindow() - ComposeModal -> MailComposeForm + openWindow() - FilePreviewModal -> FilePreviewContent + openWindow() - Calendar, Mail, Dms use openWindow() instead of modal state
This commit is contained in:
@@ -20,12 +20,13 @@ import { DayView } from '@/components/calendar/DayView';
|
||||
import { RangeView } from '@/components/calendar/RangeView';
|
||||
import { formatDateInput } from '@/utils/date';
|
||||
import { CalendarDetail } from '@/components/calendar/CalendarDetail';
|
||||
import { AppointmentModal } from '@/components/calendar/AppointmentModal';
|
||||
import { AppointmentEditForm } from '@/components/calendar/AppointmentEditForm';
|
||||
import { useWindowStore } from '@/store/windowStore';
|
||||
import { IcsControls } from '@/components/calendar/IcsControls';
|
||||
import { SharingSettings } from '@/components/calendar/SharingSettings';
|
||||
import { useCalendarStore, type CalendarViewMode } from '@/store/calendarStore';
|
||||
import { usePluginToolbarStore } from '@/store/pluginToolbarStore';
|
||||
import { ChevronLeft, ChevronRight, Info, Plus } from 'lucide-react';
|
||||
import { ChevronLeft, ChevronRight, ExternalLink, Info, Plus } from 'lucide-react';
|
||||
import {
|
||||
fetchCalendars,
|
||||
createCalendar,
|
||||
@@ -72,9 +73,7 @@ export function CalendarPage() {
|
||||
const [loadingCalendars, setLoadingCalendars] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [modalEntry, setModalEntry] = useState<CalendarEntry | null>(null);
|
||||
const [prefillDate, setPrefillDate] = useState<Date | null>(null);
|
||||
const openWindow = useWindowStore((s) => s.openWindow);
|
||||
|
||||
const [showSharing, setShowSharing] = useState(false);
|
||||
const [showIcs, setShowIcs] = useState(false);
|
||||
@@ -176,11 +175,44 @@ export function CalendarPage() {
|
||||
|
||||
// ??? Handlers ???????????????????????????????????????????????????????????
|
||||
|
||||
const handleSaved = useCallback(
|
||||
(saved: CalendarEntry) => {
|
||||
setEntries((cur) => {
|
||||
const exists = cur.some((e) => e.id === saved.id);
|
||||
return exists ? cur.map((e) => (e.id === saved.id ? saved : e)) : [...cur, saved];
|
||||
});
|
||||
if (selectedEntry?.id === saved.id) {
|
||||
setSelectedEntry(saved);
|
||||
}
|
||||
},
|
||||
[selectedEntry, setSelectedEntry],
|
||||
);
|
||||
|
||||
const handleDeleted = useCallback(
|
||||
(entryId: string) => {
|
||||
setEntries((cur) => cur.filter((e) => e.id !== entryId));
|
||||
if (selectedEntry?.id === entryId) {
|
||||
setSelectedEntry(null);
|
||||
}
|
||||
},
|
||||
[selectedEntry, setSelectedEntry],
|
||||
);
|
||||
|
||||
const handleCreateAt = useCallback((date: Date) => {
|
||||
setModalEntry(null);
|
||||
setPrefillDate(date);
|
||||
setModalOpen(true);
|
||||
}, []);
|
||||
openWindow({
|
||||
title: t('calendar.newAppointment'),
|
||||
type: 'appointment-create',
|
||||
component: AppointmentEditForm,
|
||||
componentProps: {
|
||||
entry: null,
|
||||
prefillDate: date,
|
||||
calendars,
|
||||
defaultCalendarId: activeCalendarId,
|
||||
onSaved: handleSaved,
|
||||
onDeleted: handleDeleted,
|
||||
},
|
||||
});
|
||||
}, [openWindow, t, calendars, activeCalendarId, handleSaved, handleDeleted]);
|
||||
|
||||
const handleEditEntry = useCallback(
|
||||
(entry: CalendarEntry) => {
|
||||
@@ -194,10 +226,20 @@ export function CalendarPage() {
|
||||
|
||||
const handleOpenEditModal = useCallback(() => {
|
||||
if (!selectedEntry) return;
|
||||
setModalEntry(selectedEntry);
|
||||
setPrefillDate(null);
|
||||
setModalOpen(true);
|
||||
}, [selectedEntry]);
|
||||
openWindow({
|
||||
title: t('calendar.editAppointment'),
|
||||
type: 'appointment-edit',
|
||||
component: AppointmentEditForm,
|
||||
componentProps: {
|
||||
entry: selectedEntry,
|
||||
prefillDate: null,
|
||||
calendars,
|
||||
defaultCalendarId: activeCalendarId,
|
||||
onSaved: handleSaved,
|
||||
onDeleted: handleDeleted,
|
||||
},
|
||||
});
|
||||
}, [selectedEntry, openWindow, t, calendars, activeCalendarId, handleSaved, handleDeleted]);
|
||||
|
||||
const handleMoveEntry = useCallback(
|
||||
async (entry: CalendarEntry, newStart: Date) => {
|
||||
@@ -223,29 +265,6 @@ export function CalendarPage() {
|
||||
[t, loadEntries, selectedEntry, setSelectedEntry],
|
||||
);
|
||||
|
||||
const handleSaved = useCallback(
|
||||
(saved: CalendarEntry) => {
|
||||
setEntries((cur) => {
|
||||
const exists = cur.some((e) => e.id === saved.id);
|
||||
return exists ? cur.map((e) => (e.id === saved.id ? saved : e)) : [...cur, saved];
|
||||
});
|
||||
if (selectedEntry?.id === saved.id) {
|
||||
setSelectedEntry(saved);
|
||||
}
|
||||
},
|
||||
[selectedEntry, setSelectedEntry],
|
||||
);
|
||||
|
||||
const handleDeleted = useCallback(
|
||||
(entryId: string) => {
|
||||
setEntries((cur) => cur.filter((e) => e.id !== entryId));
|
||||
if (selectedEntry?.id === entryId) {
|
||||
setSelectedEntry(null);
|
||||
}
|
||||
},
|
||||
[selectedEntry, setSelectedEntry],
|
||||
);
|
||||
|
||||
const handleConfirmDelete = useCallback(async () => {
|
||||
if (!deleteTarget) return;
|
||||
try {
|
||||
@@ -367,9 +386,19 @@ export function CalendarPage() {
|
||||
label: t('calendar.newAppointment'),
|
||||
group: 'actions',
|
||||
onClick: () => {
|
||||
setModalEntry(null);
|
||||
setPrefillDate(null);
|
||||
setModalOpen(true);
|
||||
openWindow({
|
||||
title: t('calendar.newAppointment'),
|
||||
type: 'appointment-create',
|
||||
component: AppointmentEditForm,
|
||||
componentProps: {
|
||||
entry: null,
|
||||
prefillDate: null,
|
||||
calendars,
|
||||
defaultCalendarId: activeCalendarId,
|
||||
onSaved: handleSaved,
|
||||
onDeleted: handleDeleted,
|
||||
},
|
||||
});
|
||||
},
|
||||
icon: (
|
||||
<Plus className="w-3.5 h-3.5" strokeWidth={2} />
|
||||
@@ -401,6 +430,17 @@ export function CalendarPage() {
|
||||
<Info className="w-3.5 h-3.5" strokeWidth={2} />
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'open-standalone',
|
||||
plugin: 'calendar',
|
||||
label: 'In neuem Fenster',
|
||||
type: 'button' as const,
|
||||
group: 'actions',
|
||||
icon: (
|
||||
<ExternalLink className="w-3.5 h-3.5" strokeWidth={2} />
|
||||
),
|
||||
onClick: () => window.open('/calendar-standalone', '_blank', 'width=1200,height=800'),
|
||||
},
|
||||
];
|
||||
|
||||
registerItems('calendar', items);
|
||||
@@ -685,17 +725,6 @@ export function CalendarPage() {
|
||||
onCancel={() => setDeleteTarget(null)}
|
||||
/>
|
||||
|
||||
{/* Appointment modal */}
|
||||
<AppointmentModal
|
||||
open={modalOpen}
|
||||
onClose={() => setModalOpen(false)}
|
||||
entry={modalEntry}
|
||||
prefillDate={prefillDate}
|
||||
calendars={calendars}
|
||||
defaultCalendarId={activeCalendarId}
|
||||
onSaved={handleSaved}
|
||||
onDeleted={handleDeleted}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user