From 29b809b9729d074cad5b64871eb8993fca99491f Mon Sep 17 00:00:00 2001 From: dnviti Date: Sat, 29 Nov 2025 03:13:27 +0100 Subject: [PATCH] - --- CLAUDE.md | 90 +++++++++++++----- frontend/src/components/Layout.tsx | 22 +++-- .../components/reportEditor/EditorCanvas.tsx | 1 + frontend/src/pages/ReportEditorPage.tsx | 6 +- src/Apollinare.API/apollinare.db-shm | Bin 32768 -> 32768 bytes src/Apollinare.API/apollinare.db-wal | Bin 1001192 -> 1619192 bytes 6 files changed, 83 insertions(+), 36 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 2c8a914..4046c3d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -52,32 +52,44 @@ XX. **Nome Problema (FIX/IMPLEMENTATO DATA):** - **Problema:** Descrizione breve **Lavoro completato nell'ultima sessione:** -- **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: Sistema Pannelli Drag-and-Drop con Sidebar Ridimensionabili** - COMPLETATO + - **Obiettivo:** I pannelli del report designer devono poter essere trascinati tra sidebar sinistra e destra, con ridimensionamento orizzontale a livello sidebar + - **Architettura implementata:** + - `SidebarDropZone.tsx` - Contenitore sidebar con: + - Drop zone per drag-and-drop pannelli (HTML5 Drag and Drop API) + - Handle di resize orizzontale sul bordo interno + - Indicatore visivo durante il drag + - Props: `position`, `width`, `onWidthChange`, `onPanelDrop`, `panelIds` + - `ResizablePanel.tsx` - Pannello individuale con: + - Header trascinabile (`draggable="true"`) + - Resize verticale (flex) tra pannelli nella stessa sidebar + - Stato collassato con icona e titolo verticale + - `width: 100%` - si adatta alla larghezza della sidebar + - `usePanelLayout.ts` - Hook per gestione stato: + - `sidebarWidths: { left: number, right: number }` per larghezza sidebar + - `panels[]` con `flex` per distribuzione verticale + - `movePanelToPosition()` redistribuisce flex quando un pannello viene droppato + - Persistenza in localStorage (versione 3) + - **Comportamento:** + - Trascinando l'header di un pannello si può spostarlo tra sidebar + - Quando un pannello viene droppato, tutti i pannelli nella sidebar di destinazione ottengono `flex: 1` (distribuzione equa) + - Il resize orizzontale agisce sulla sidebar intera, non sui singoli pannelli + - I pannelli adottano automaticamente la larghezza della sidebar + - **Layout Full-Width:** + - `Layout.tsx` modificato per Report Editor: `p: 0`, `overflow: hidden`, `width: 100vw - drawerWidth` + - `ReportEditorPage.tsx`: `flex: 1`, `minHeight: 0` per espansione corretta + - Canvas container: `width: 100%` per occupare tutto lo spazio disponibile + +- **FIX: Layout Report Designer Non Full-Width** - COMPLETATO + - **Problema:** Il report designer non occupava tutta la larghezza del browser + - **Causa:** Il Layout usava `width: 100%` invece di `100vw` e il padding non veniva rimosso per il report editor + - **Soluzione:** + - `Layout.tsx`: Usato `width: 100vw` e `calc(100vw - drawerWidth)` invece di `100%` + - `Layout.tsx`: Padding condizionale `p: 0` per route `/report-editor` + - `Layout.tsx`: `overflow: hidden` e `height: 100vh` per contenitore main + - `ReportEditorPage.tsx`: Rimossi margini negativi, usato `flex: 1` e `minHeight: 0` + - `EditorCanvas.tsx`: Modifiche utente per larghezza canvas + - **File modificati:** `Layout.tsx`, `ReportEditorPage.tsx`, `EditorCanvas.tsx` - **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 @@ -1272,6 +1284,32 @@ frontend/src/ - Implementate tutte le scorciatoie dichiarate nel context menu (Ctrl+X/C/V/D/A/L/G, Ctrl+[/], Delete, Escape) - **File:** `EditorCanvas.tsx` (aggiunto `isTextEditing()` a `EditorCanvasRef`), `ReportEditorPage.tsx` (riscritto `useEffect` delle scorciatoie, aggiunto `canvasRef`) +29. **Sistema Pannelli Drag-and-Drop tra Sidebar (IMPLEMENTATO 29/11/2025):** + - **Obiettivo:** Permettere agli utenti di trascinare i pannelli tra sidebar sinistra e destra, con ridimensionamento a livello sidebar + - **Implementazione:** + - `SidebarDropZone.tsx` - Drop zone con HTML5 Drag and Drop API e resize orizzontale + - `ResizablePanel.tsx` - Pannello con header draggable e resize verticale (flex) + - `usePanelLayout.ts` - Hook con `sidebarWidths` invece di larghezza per-pannello + - Versione config incrementata a 3 per forzare migrazione localStorage + - **Comportamento chiave:** + - `movePanelToPosition()` redistribuisce `flex: 1` a tutti i pannelli nella sidebar di destinazione + - Pannelli usano `width: 100%` per adattarsi alla sidebar + - `PANEL_DRAG_TYPE = "application/x-panel-id"` per identificare i drag + - **File:** `SidebarDropZone.tsx`, `ResizablePanel.tsx`, `usePanelLayout.ts`, `ReportEditorPage.tsx` + +30. **Layout Report Designer Non Full-Width (FIX 29/11/2025):** + - **Problema:** Il report designer non occupava tutta la larghezza disponibile del browser + - **Causa:** Il Layout usava `width: 100%` che si riferisce al parent, non al viewport. Inoltre il padding non veniva rimosso per il report editor + - **Soluzione:** + - `Layout.tsx`: Cambiato `width: 100%` → `width: 100vw` e `calc(100vw - drawerWidth)` + - `Layout.tsx`: Aggiunto controllo condizionale per route `/report-editor`: `p: 0`, `overflow: hidden` + - `Layout.tsx`: Cambiato `minHeight: 100vh` → `height: 100vh` con `overflow: hidden` + - `Layout.tsx`: Content box usa `flex: 1` con `minHeight: 0` (fondamentale per flexbox) + - `ReportEditorPage.tsx`: Rimossi margini negativi, usato `flex: 1` e `minHeight: 0` + - `ReportEditorPage.tsx`: Canvas container con `width: 100%` + - **Nota:** `minHeight: 0` è fondamentale in flexbox per permettere ai contenitori di ridursi sotto la dimensione del loro contenuto + - **File:** `Layout.tsx`, `ReportEditorPage.tsx`, `EditorCanvas.tsx` + ### Schema Database Report System Le tabelle sono già nel DbContext (`AppollinareDbContext.cs`): diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx index bc4b7a4..f02ec7e 100644 --- a/frontend/src/components/Layout.tsx +++ b/frontend/src/components/Layout.tsx @@ -215,23 +215,31 @@ export default function Layout() { sx={{ flexGrow: 1, width: { - xs: "100%", - sm: `calc(100% - ${drawerWidth}px)`, + xs: "100vw", + sm: `calc(100vw - ${drawerWidth}px)`, }, - minHeight: "100vh", + height: "100vh", display: "flex", flexDirection: "column", + overflow: "hidden", }} > {/* Toolbar spacer */} - + {/* Content */} diff --git a/frontend/src/components/reportEditor/EditorCanvas.tsx b/frontend/src/components/reportEditor/EditorCanvas.tsx index 3c9d29e..0741500 100644 --- a/frontend/src/components/reportEditor/EditorCanvas.tsx +++ b/frontend/src/components/reportEditor/EditorCanvas.tsx @@ -1522,6 +1522,7 @@ const EditorCanvas = forwardRef( sx={{ flex: 1, overflow: "auto", + width: "100%", display: "flex", justifyContent: "center", alignItems: "flex-start", diff --git a/frontend/src/pages/ReportEditorPage.tsx b/frontend/src/pages/ReportEditorPage.tsx index 29b4962..a5bf6ee 100644 --- a/frontend/src/pages/ReportEditorPage.tsx +++ b/frontend/src/pages/ReportEditorPage.tsx @@ -1862,9 +1862,9 @@ export default function ReportEditorPage() { sx={{ display: "flex", flexDirection: "column", - height: { xs: "calc(100vh - 56px)", sm: "calc(100vh - 64px)" }, - mx: { xs: -1.5, sm: -2, md: -3 }, - mt: { xs: -1.5, sm: -2, md: -3 }, + flex: 1, + width: "100%", + minHeight: 0, // Important: allows flex container to shrink overflow: "hidden", }} > diff --git a/src/Apollinare.API/apollinare.db-shm b/src/Apollinare.API/apollinare.db-shm index 8d596356669763e6ef73a523b7e0ae86fb640ce4..d574bb643aba6dee43d624c625c2770b5b80bb31 100644 GIT binary patch literal 32768 zcmeI*XHZmU6b9h;SOG<`W3Pw}6}u=3N>{P>-VhtuQ7nkv^qv^gd+)_G(|b%Yq$eaX zz4u=9y~&VdB7TcMa`$97cjjg9>|xl$^X=@vJty)&47@9X5kBnXk3!z>DvHhz*_&Cs zdr$Pfu!5-U@Yv$0{YCL{kw^La-rx4FEdJet|G55q_V1seyj{hVJo4B7xKH3?A~S>; z$_!&Nnc>U`W+XF;8O@Ag#xmoW@yrBfA~T7Z%uHdXGSisp%nW8GGmDwc%wgs-Sf?tLZHE{8x}ew+3JypIqWrrat6C*eiJi#PQl7*_~q zIh=-9@hcL8(m`dk;{=5qoQzxW5*o_@nRkRfdU7hm{b(mEj!+MVp3c{-hgeSAxDX#8 zR5DFV=OGsom3E#16brqs_|c9>e*Ucc6tVQt#A{6E`14 zXUSLJsT16XVq?xxy&RvRri?Kwr*~&ge!UYdWwCmv{@8@a&_&j&@6-wIL$L`b2w#EE z5g}vE%IV#OGmq~=E6E9xlb|B1pgku^=i(IHikI;-n#e%OKCGX3Y{uj0D(eobIRC+N zSZvDK*;nEV)RJ*#Hj1Qzy6&#a7%7bsfG% zeVJ@hPVaE;=GuifA(En8YA|-<8T^D^vPpTTPH-QJt-0^#dVGflGR35v-Zi+D?rgk; z3X-Z^DhbEnS@f0}vRQejPH-QJZMfy{27Hf5nQBr_@0#4lxEpVyqNFL8O2)Bx4t->c z@;;s1z7(Ul-||NMfQB;7teoNz+(dc~4xo~xE0;>aaoCT(vQ>GXPHta{ZMoU?Cj5v- zGTp44;?=7{~GNUhdxYswmco!5{*5Hzp>Ej-nFA80aW=iy#VDQNb40ZbU4s>$O|4 z#qRD75dHu?oZ}1M`pld?&+j>(voChm;jlOymITkYwJ9h=EDb#J#Xd`{iM8TWV}dhF ztoUDv5FTir_Eopdk{_xr+8{QZE1QZ_K+{SIKxUX zV{nQ-wupuwK`dl1cSQEq>$94du7c}E2_lH)vXC4C&FYH%^67=pOh)jU}ke!1)Z*p<$zG> zMhmuZo6qW?0+A$gMFkt(7>BDs=`Bg;ix`WqUy&iq;i@#IBf;u6o`ce~Vk=*j@nR@* zxh9QKi0(|_kTk8y;F~mN;2^8pJgzHarw0=`EKM7>@m(6jn_(o8txRD;n8Xoj0@=Z}hZQ`Nt-Tnv2_>2n!ldaP$mEwYby>-CWr`EVG?sHxnvU!yPjqpH8(kR9e$mJN E0VTF4d;kCd diff --git a/src/Apollinare.API/apollinare.db-wal b/src/Apollinare.API/apollinare.db-wal index bcce40c1672e448c9a99d666dcf85c115e372742..c42d95d4e96983afab34744d579452fe0baa87c8 100644 GIT binary patch delta 21010 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 delta 11319 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