diff --git a/CLAUDE.md b/CLAUDE.md index 6c2910f..2c8a914 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -52,7 +52,34 @@ XX. **Nome Problema (FIX/IMPLEMENTATO DATA):** - **Problema:** Descrizione breve **Lavoro completato nell'ultima sessione:** -- **NUOVA FEATURE: Scorciatoie da Tastiera Complete per Report Designer** - COMPLETATO +- **NUOVA FEATURE: Pannelli Ridimensionabili e Collassabili nel Report Designer** - COMPLETATO + - **Obiettivo:** I pannelli del report designer devono essere collassabili, ridimensionabili, riposizionabili e la configurazione deve essere persistita + - **Nuovi file creati:** + - `frontend/src/hooks/useLocalStorage.ts` - Hook generico per persistenza in localStorage + - `frontend/src/hooks/usePanelLayout.ts` - Hook per gestire configurazione pannelli (larghezza, collapsed, posizione) + - `frontend/src/components/reportEditor/ResizablePanel.tsx` - Componente pannello ridimensionabile con: + - Handle di resize trascinabile sul bordo + - Stato collassato con icona, badge e titolo verticale + - Header compatto con titolo, icona e pulsante collasso + - Nessuna scrollbar visibile (hidden ma funzionale) + - **Configurazione pannelli:** + - Salvata in localStorage con chiave `apollinare-report-editor-panels` + - Default: Pages (sinistra, 220px), Data (destra, 280px), Properties (destra, 280px) + - Ogni pannello ha: id, width, collapsed, position (left/right), order + - **Scrollbar nascoste:** Usato CSS per nascondere scrollbar mantenendo funzionalità scroll + - `&::-webkit-scrollbar: { width: 0 }` per Chrome/Safari + - `scrollbarWidth: "none"` per Firefox + - `msOverflowStyle: "none"` per IE/Edge + - **Canvas centrato:** La viewport del report rimane sempre al centro dell'area disponibile + - **File modificati:** + - `ReportEditorPage.tsx` - Integrato `usePanelLayout`, usato `ResizablePanel` per tutti i pannelli + - `PageNavigator.tsx` - Aggiunto CSS per nascondere scrollbar + - `DataBindingPanel.tsx` - Aggiunto CSS per nascondere scrollbar + - `PropertiesPanel.tsx` - Aggiunto CSS per nascondere scrollbar + - **File rimossi:** + - `CollapsiblePanel.tsx` - Sostituito da `ResizablePanel` + +- **NUOVA FEATURE: Panning e Zoom stile Draw.io nel Report Designer** - COMPLETATO (sessione precedente) - **Problema:** Le scorciatoie da tastiera (Ctrl+X, Ctrl+C, etc.) venivano intercettate dal browser invece che dalla pagina - **Soluzione:** Riscritto completamente l'handler delle scorciatoie con: - Controllo se il focus è su campi input/textarea/contenteditable diff --git a/frontend/src/components/reportEditor/DataBindingPanel.tsx b/frontend/src/components/reportEditor/DataBindingPanel.tsx index cc9413b..8f9ad94 100644 --- a/frontend/src/components/reportEditor/DataBindingPanel.tsx +++ b/frontend/src/components/reportEditor/DataBindingPanel.tsx @@ -53,6 +53,8 @@ interface DataBindingPanelProps { selectedDatasets: DatasetTypeDto[]; onInsertBinding: (binding: string) => void; onRemoveDataset: (datasetId: string) => void; + /** When true, hides borders (used inside CollapsiblePanel) */ + embedded?: boolean; } export default function DataBindingPanel({ @@ -60,6 +62,7 @@ export default function DataBindingPanel({ selectedDatasets, onInsertBinding, onRemoveDataset, + embedded = false, }: DataBindingPanelProps) { const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down("sm")); @@ -205,25 +208,25 @@ export default function DataBindingPanel({ return count; }, [search, schemas]); - // Panel width based on context (full width in mobile drawer) - const panelWidth = isMobile ? "100%" : 300; + // Panel width based on context (full width when embedded or in mobile drawer) + const panelWidth = embedded ? "100%" : isMobile ? "100%" : 300; if (selectedDatasets.length === 0) { return ( @@ -242,18 +245,20 @@ export default function DataBindingPanel({ {/* Header con ricerca */} - - {!isMobile && ( + + {!isMobile && !embedded && ( {/* Lista campi */} - + {/* Dataset Sections */} {schemas.map((schema) => { const dataset = selectedDatasets.find( diff --git a/frontend/src/components/reportEditor/PageNavigator.tsx b/frontend/src/components/reportEditor/PageNavigator.tsx index d92ec14..3caa340 100644 --- a/frontend/src/components/reportEditor/PageNavigator.tsx +++ b/frontend/src/components/reportEditor/PageNavigator.tsx @@ -43,6 +43,8 @@ interface PageNavigatorProps { onDeletePage: (pageId: string) => void; onRenamePage: (pageId: string, newName: string) => void; onMovePage: (pageId: string, direction: "up" | "down") => void; + /** When true, hides header and borders (used inside CollapsiblePanel) */ + embedded?: boolean; } export default function PageNavigator({ @@ -55,6 +57,7 @@ export default function PageNavigator({ onDeletePage, onRenamePage, onMovePage, + embedded = false, }: PageNavigatorProps) { const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down("sm")); @@ -158,42 +161,48 @@ export default function PageNavigator({ const currentMenuIndex = menuAnchor ? getPageIndex(menuAnchor.pageId) : -1; - // Width based on context - const panelWidth = isMobile ? "100%" : 220; + // Width based on context - full width when embedded in CollapsiblePanel + const panelWidth = embedded ? "100%" : isMobile ? "100%" : 220; return ( - {/* Header */} - - - Pagine ({pages.length}) - - - - - - - + {/* Header - hide when embedded */} + {!embedded && ( + + + Pagine ({pages.length}) + + + + + + + + )} {/* Page List */} {pages.map((page, index) => { diff --git a/frontend/src/components/reportEditor/PropertiesPanel.tsx b/frontend/src/components/reportEditor/PropertiesPanel.tsx index a9df476..e8332df 100644 --- a/frontend/src/components/reportEditor/PropertiesPanel.tsx +++ b/frontend/src/components/reportEditor/PropertiesPanel.tsx @@ -78,6 +78,8 @@ interface PropertiesPanelProps { // Current page for page-specific settings currentPage?: AprtPage; onUpdateCurrentPage?: (updates: Partial) => void; + /** When true, hides borders (used inside CollapsiblePanel) */ + embedded?: boolean; } export default function PropertiesPanel({ @@ -93,6 +95,7 @@ export default function PropertiesPanel({ onOpenImageUpload, currentPage, onUpdateCurrentPage, + embedded = false, }: PropertiesPanelProps) { const theme = useTheme(); const isMobile = useMediaQuery(theme.breakpoints.down("sm")); @@ -180,8 +183,8 @@ export default function PropertiesPanel({ } }; - // Panel width based on context - const panelWidth = isMobile ? "100%" : 280; + // Panel width based on context (full width when embedded) + const panelWidth = embedded ? "100%" : isMobile ? "100%" : 280; if (!element) { // Show page settings when no element selected @@ -189,15 +192,22 @@ export default function PropertiesPanel({ - {!isMobile && ( + {!isMobile && !embedded && ( {currentPage ? `Pagina: ${currentPage.name}` @@ -374,21 +384,31 @@ export default function PropertiesPanel({ - - - {element.name || `Elemento ${element.type}`} - - - {element.type.toUpperCase()} - ID: {element.id.slice(0, 8)} - - + {/* Element header - show only when not embedded (CollapsiblePanel has its own header) */} + {!embedded && ( + + + {element.name || `Elemento ${element.type}`} + + + {element.type.toUpperCase()} - ID: {element.id.slice(0, 8)} + + + )} {/* Position */} void; + /** Callback when collapse state changes */ + onToggleCollapse: () => void; + /** Panel content */ + children: ReactNode; + /** Optional badge content (e.g., count) */ + badge?: ReactNode; + /** Enable drag handle for reordering */ + draggable?: boolean; + /** Drag start handler */ + onDragStart?: (e: React.DragEvent) => void; + /** Drag end handler */ + onDragEnd?: (e: React.DragEvent) => void; +} + +export default function ResizablePanel({ + id, + title, + icon, + position, + width, + minWidth = 200, + maxWidth = 500, + collapsed, + collapsedWidth = 44, + onWidthChange, + onToggleCollapse, + children, + badge, + draggable = false, + onDragStart, + onDragEnd, +}: ResizablePanelProps) { + const [isResizing, setIsResizing] = useState(false); + const panelRef = useRef(null); + const startXRef = useRef(0); + const startWidthRef = useRef(width); + + const CollapseIcon = position === "left" ? CollapseLeftIcon : CollapseRightIcon; + const ExpandIcon = position === "left" ? CollapseRightIcon : CollapseLeftIcon; + + // Handle mouse down on resize handle + const handleResizeStart = useCallback( + (e: React.MouseEvent) => { + e.preventDefault(); + setIsResizing(true); + startXRef.current = e.clientX; + startWidthRef.current = width; + }, + [width] + ); + + // Handle mouse move during resize + useEffect(() => { + if (!isResizing) return; + + const handleMouseMove = (e: MouseEvent) => { + const delta = position === "left" + ? e.clientX - startXRef.current + : startXRef.current - e.clientX; + + const newWidth = Math.min(maxWidth, Math.max(minWidth, startWidthRef.current + delta)); + onWidthChange(newWidth); + }; + + const handleMouseUp = () => { + setIsResizing(false); + }; + + document.addEventListener("mousemove", handleMouseMove); + document.addEventListener("mouseup", handleMouseUp); + + return () => { + document.removeEventListener("mousemove", handleMouseMove); + document.removeEventListener("mouseup", handleMouseUp); + }; + }, [isResizing, position, minWidth, maxWidth, onWidthChange]); + + // Collapsed state - show icon strip + if (collapsed) { + return ( + alpha(theme.palette.primary.main, 0.03), + borderLeft: position === "right" ? 1 : 0, + borderRight: position === "left" ? 1 : 0, + borderColor: "divider", + py: 1, + flexShrink: 0, + }} + > + {/* Expand button */} + + alpha(theme.palette.primary.main, 0.08), + "&:hover": { + bgcolor: (theme) => alpha(theme.palette.primary.main, 0.15), + }, + }} + > + + + + + {/* Panel icon with tooltip */} + + alpha(theme.palette.primary.main, 0.08), + }, + }} + onClick={onToggleCollapse} + > + + {icon} + + {badge && ( + + {badge} + + )} + + + + {/* Vertical title */} + + {title} + + + ); + } + + // Expanded state + return ( + + {/* Resize handle */} + + + + + {/* Header with collapse button */} + alpha(theme.palette.primary.main, 0.03), + minHeight: 40, + }} + > + + {/* Drag handle */} + {draggable && ( + + + + )} + {icon} + + {title} + + {badge && ( + + {badge} + + )} + + + + + + + + + {/* Content - no internal scrollbar, content must handle its own overflow */} + + {children} + + + ); +} diff --git a/frontend/src/hooks/useLocalStorage.ts b/frontend/src/hooks/useLocalStorage.ts new file mode 100644 index 0000000..a84abf3 --- /dev/null +++ b/frontend/src/hooks/useLocalStorage.ts @@ -0,0 +1,79 @@ +import { useState, useEffect, useCallback } from "react"; + +/** + * Hook to persist state in localStorage with automatic serialization/deserialization + * @param key - localStorage key + * @param initialValue - default value if nothing in storage + * @returns [value, setValue, removeValue] + */ +export function useLocalStorage( + key: string, + initialValue: T +): [T, (value: T | ((prev: T) => T)) => void, () => void] { + // Get stored value or use initial + const readValue = useCallback((): T => { + if (typeof window === "undefined") { + return initialValue; + } + + try { + const item = window.localStorage.getItem(key); + return item ? (JSON.parse(item) as T) : initialValue; + } catch (error) { + console.warn(`Error reading localStorage key "${key}":`, error); + return initialValue; + } + }, [initialValue, key]); + + const [storedValue, setStoredValue] = useState(readValue); + + // Return a wrapped version of useState's setter function that persists to localStorage + const setValue = useCallback( + (value: T | ((prev: T) => T)) => { + try { + // Allow value to be a function so we have the same API as useState + const valueToStore = + value instanceof Function ? value(storedValue) : value; + setStoredValue(valueToStore); + if (typeof window !== "undefined") { + window.localStorage.setItem(key, JSON.stringify(valueToStore)); + } + } catch (error) { + console.warn(`Error setting localStorage key "${key}":`, error); + } + }, + [key, storedValue] + ); + + // Remove from localStorage + const removeValue = useCallback(() => { + try { + if (typeof window !== "undefined") { + window.localStorage.removeItem(key); + } + setStoredValue(initialValue); + } catch (error) { + console.warn(`Error removing localStorage key "${key}":`, error); + } + }, [initialValue, key]); + + // Listen to storage changes from other tabs + useEffect(() => { + const handleStorageChange = (e: StorageEvent) => { + if (e.key === key && e.newValue !== null) { + try { + setStoredValue(JSON.parse(e.newValue) as T); + } catch { + // Ignore parse errors + } + } + }; + + window.addEventListener("storage", handleStorageChange); + return () => window.removeEventListener("storage", handleStorageChange); + }, [key]); + + return [storedValue, setValue, removeValue]; +} + +export default useLocalStorage; diff --git a/frontend/src/hooks/usePanelLayout.ts b/frontend/src/hooks/usePanelLayout.ts new file mode 100644 index 0000000..8d8cc48 --- /dev/null +++ b/frontend/src/hooks/usePanelLayout.ts @@ -0,0 +1,158 @@ +import { useCallback, useMemo } from "react"; +import { useLocalStorage } from "./useLocalStorage"; + +export interface PanelState { + id: string; + width: number; + collapsed: boolean; + position: "left" | "right"; + order: number; +} + +export interface PanelLayoutConfig { + panels: PanelState[]; + version: number; +} + +const STORAGE_KEY = "apollinare-report-editor-panels"; +const CONFIG_VERSION = 1; + +// Default panel configuration +const defaultConfig: PanelLayoutConfig = { + version: CONFIG_VERSION, + panels: [ + { id: "pages", width: 220, collapsed: false, position: "left", order: 0 }, + { id: "data", width: 280, collapsed: false, position: "right", order: 0 }, + { id: "properties", width: 280, collapsed: false, position: "right", order: 1 }, + ], +}; + +export function usePanelLayout() { + const [config, setConfig] = useLocalStorage( + STORAGE_KEY, + defaultConfig + ); + + // Ensure config is up to date (handle version migrations) + const normalizedConfig = useMemo(() => { + if (!config || config.version !== CONFIG_VERSION) { + return defaultConfig; + } + return config; + }, [config]); + + // Get panel state by id + const getPanelState = useCallback( + (panelId: string): PanelState | undefined => { + return normalizedConfig.panels.find((p) => p.id === panelId); + }, + [normalizedConfig.panels] + ); + + // Update a single panel's state + const updatePanel = useCallback( + (panelId: string, updates: Partial>) => { + setConfig((prev) => ({ + ...prev, + panels: prev.panels.map((p) => + p.id === panelId ? { ...p, ...updates } : p + ), + })); + }, + [setConfig] + ); + + // Toggle panel collapse state + const togglePanelCollapse = useCallback( + (panelId: string) => { + setConfig((prev) => ({ + ...prev, + panels: prev.panels.map((p) => + p.id === panelId ? { ...p, collapsed: !p.collapsed } : p + ), + })); + }, + [setConfig] + ); + + // Update panel width + const setPanelWidth = useCallback( + (panelId: string, width: number) => { + setConfig((prev) => ({ + ...prev, + panels: prev.panels.map((p) => + p.id === panelId ? { ...p, width } : p + ), + })); + }, + [setConfig] + ); + + // Move panel to a different position (left/right sidebar) + const movePanelToPosition = useCallback( + (panelId: string, newPosition: "left" | "right", newOrder?: number) => { + setConfig((prev) => { + const panelsInNewPosition = prev.panels.filter( + (p) => p.position === newPosition && p.id !== panelId + ); + const maxOrder = panelsInNewPosition.length > 0 + ? Math.max(...panelsInNewPosition.map((p) => p.order)) + 1 + : 0; + + return { + ...prev, + panels: prev.panels.map((p) => + p.id === panelId + ? { ...p, position: newPosition, order: newOrder ?? maxOrder } + : p + ), + }; + }); + }, + [setConfig] + ); + + // Reorder panels within a sidebar + const reorderPanels = useCallback( + (position: "left" | "right", orderedPanelIds: string[]) => { + setConfig((prev) => ({ + ...prev, + panels: prev.panels.map((p) => { + if (p.position !== position) return p; + const newOrder = orderedPanelIds.indexOf(p.id); + return newOrder >= 0 ? { ...p, order: newOrder } : p; + }), + })); + }, + [setConfig] + ); + + // Get panels for a specific position, sorted by order + const getPanelsForPosition = useCallback( + (position: "left" | "right"): PanelState[] => { + return normalizedConfig.panels + .filter((p) => p.position === position) + .sort((a, b) => a.order - b.order); + }, + [normalizedConfig.panels] + ); + + // Reset to default configuration + const resetToDefault = useCallback(() => { + setConfig(defaultConfig); + }, [setConfig]); + + return { + config: normalizedConfig, + getPanelState, + updatePanel, + togglePanelCollapse, + setPanelWidth, + movePanelToPosition, + reorderPanels, + getPanelsForPosition, + resetToDefault, + }; +} + +export default usePanelLayout; diff --git a/frontend/src/pages/ReportEditorPage.tsx b/frontend/src/pages/ReportEditorPage.tsx index 947c890..bee3385 100644 --- a/frontend/src/pages/ReportEditorPage.tsx +++ b/frontend/src/pages/ReportEditorPage.tsx @@ -8,6 +8,7 @@ import { } from "@tanstack/react-query"; import { v4 as uuidv4 } from "uuid"; import { useHistory } from "../hooks/useHistory"; +import { usePanelLayout } from "../hooks/usePanelLayout"; import { useCollaborationRoom } from "../contexts/CollaborationContext"; import type { DataChangeMessage, @@ -43,6 +44,7 @@ import { Settings as SettingsIcon, Description as PageIcon, Close as CloseIcon, + Layers as LayersIcon, } from "@mui/icons-material"; import EditorCanvas, { type ContextMenuEvent, @@ -61,6 +63,7 @@ import ImageUploadDialog, { type ImageData, } from "../components/reportEditor/ImageUploadDialog"; import PageNavigator from "../components/reportEditor/PageNavigator"; +import ResizablePanel from "../components/reportEditor/ResizablePanel"; import { reportTemplateService, reportFontService, @@ -101,7 +104,6 @@ export default function ReportEditorPage() { // Responsive breakpoints const isMobile = useMediaQuery(theme.breakpoints.down("sm")); // < 600px const isTablet = useMediaQuery(theme.breakpoints.between("sm", "lg")); // 600-1200px - const isDesktop = useMediaQuery(theme.breakpoints.up("lg")); // > 1200px // Template state with robust undo/redo (100 states history) const [templateHistory, historyActions] = useHistory( @@ -144,6 +146,9 @@ export default function ReportEditorPage() { // Mobile panel state const [mobilePanel, setMobilePanel] = useState(null); + // Panel layout configuration (persisted to localStorage) + const panelLayout = usePanelLayout(); + // UI state const [saveDialog, setSaveDialog] = useState(false); const [previewDialog, setPreviewDialog] = useState(false); @@ -1756,7 +1761,8 @@ export default function ReportEditorPage() { } // Render panels based on screen size - const renderPageNavigator = () => ( + // embedded=true when used inside CollapsiblePanel (removes internal borders/headers) + const renderPageNavigator = (embedded = false) => ( ); - const renderDataBindingPanel = () => ( + const renderDataBindingPanel = (embedded = false) => ( ); - const renderPropertiesPanel = () => ( + const renderPropertiesPanel = (embedded = false) => ( ); @@ -1891,66 +1900,222 @@ export default function ReportEditorPage() { {/* Main Editor Area */} - {/* Desktop: Show all panels */} - {isDesktop && ( - <> - {renderPageNavigator()} - {renderDataBindingPanel()} - - )} - - {/* Tablet: Show page navigator and data panel in collapsible sidebars */} - {isTablet && ( - - {renderPageNavigator()} + {/* Left Sidebar - Panels on left side */} + {!isMobile && ( + + {panelLayout.getPanelsForPosition("left").map((panelState) => { + if (panelState.id === "pages") { + return ( + } + position="left" + width={panelState.width} + minWidth={180} + maxWidth={350} + collapsed={panelState.collapsed} + onWidthChange={(w) => + panelLayout.setPanelWidth(panelState.id, w) + } + onToggleCollapse={() => + panelLayout.togglePanelCollapse(panelState.id) + } + badge={template.pages.length} + > + {renderPageNavigator(true)} + + ); + } + if (panelState.id === "data") { + return ( + } + position="left" + width={panelState.width} + minWidth={220} + maxWidth={400} + collapsed={panelState.collapsed} + onWidthChange={(w) => + panelLayout.setPanelWidth(panelState.id, w) + } + onToggleCollapse={() => + panelLayout.togglePanelCollapse(panelState.id) + } + badge={ + selectedDatasets.length > 0 + ? selectedDatasets.length + : undefined + } + > + {renderDataBindingPanel(true)} + + ); + } + if (panelState.id === "properties") { + return ( + } + position="left" + width={panelState.width} + minWidth={220} + maxWidth={400} + collapsed={panelState.collapsed} + onWidthChange={(w) => + panelLayout.setPanelWidth(panelState.id, w) + } + onToggleCollapse={() => + panelLayout.togglePanelCollapse(panelState.id) + } + > + {renderPropertiesPanel(true)} + + ); + } + return null; + })} )} - {/* Canvas - show only elements for current page */} - + theme.palette.mode === "dark" ? "#1a1a1a" : "#e0e0e0", + position: "relative", }} - selectedElementIds={selectedElementIds} - onSelectElement={(ids) => { - setSelectedElementIds(ids); - // On mobile, auto-open properties when selecting element - if (isMobile && ids.length > 0) { - setMobilePanel("properties"); - } - }} - onUpdateElement={handleUpdateElementWithoutHistory} - onUpdateElementComplete={historyActions.commit} - zoom={zoom} - showGrid={showGrid} - gridSize={gridSize} - snapOptions={snapOptions} - onContextMenu={handleContextMenu} - /> + > + {/* Canvas - show only elements for current page */} + { + setSelectedElementIds(ids); + // On mobile, auto-open properties when selecting element + if (isMobile && ids.length > 0) { + setMobilePanel("properties"); + } + }} + onUpdateElement={handleUpdateElementWithoutHistory} + onUpdateElementComplete={historyActions.commit} + zoom={zoom} + showGrid={showGrid} + gridSize={gridSize} + snapOptions={snapOptions} + onContextMenu={handleContextMenu} + /> + - {/* Desktop/Tablet: Properties Panel */} - {!isMobile && renderPropertiesPanel()} + {/* Right Sidebar - Panels on right side */} + {!isMobile && ( + + {panelLayout.getPanelsForPosition("right").map((panelState) => { + if (panelState.id === "pages") { + return ( + } + position="right" + width={panelState.width} + minWidth={180} + maxWidth={350} + collapsed={panelState.collapsed} + onWidthChange={(w) => + panelLayout.setPanelWidth(panelState.id, w) + } + onToggleCollapse={() => + panelLayout.togglePanelCollapse(panelState.id) + } + badge={template.pages.length} + > + {renderPageNavigator(true)} + + ); + } + if (panelState.id === "data") { + return ( + } + position="right" + width={panelState.width} + minWidth={220} + maxWidth={400} + collapsed={panelState.collapsed} + onWidthChange={(w) => + panelLayout.setPanelWidth(panelState.id, w) + } + onToggleCollapse={() => + panelLayout.togglePanelCollapse(panelState.id) + } + badge={ + selectedDatasets.length > 0 + ? selectedDatasets.length + : undefined + } + > + {renderDataBindingPanel(true)} + + ); + } + if (panelState.id === "properties") { + return ( + } + position="right" + width={panelState.width} + minWidth={220} + maxWidth={400} + collapsed={panelState.collapsed} + onWidthChange={(w) => + panelLayout.setPanelWidth(panelState.id, w) + } + onToggleCollapse={() => + panelLayout.togglePanelCollapse(panelState.id) + } + > + {renderPropertiesPanel(true)} + + ); + } + return null; + })} + + )} {/* Mobile Bottom Navigation */} diff --git a/src/Apollinare.API/apollinare.db-shm b/src/Apollinare.API/apollinare.db-shm deleted file mode 100644 index 8801633..0000000 Binary files a/src/Apollinare.API/apollinare.db-shm and /dev/null differ diff --git a/src/Apollinare.API/apollinare.db-wal b/src/Apollinare.API/apollinare.db-wal deleted file mode 100644 index 0354802..0000000 Binary files a/src/Apollinare.API/apollinare.db-wal and /dev/null differ