From b5ea911e43d2e49469fcd6139e16917fd2e06aea Mon Sep 17 00:00:00 2001 From: Christian Vidal Wolf Date: Wed, 22 Apr 2026 20:38:06 +0200 Subject: [PATCH] UI: Implement Maximize mode for tables. Added a 'MAXIMIZE' button to TopBar and an 'X' close button in fullscreen mode. Hides sidebar and header to provide maximum screen space for data tables. --- .../plans/2026-04-22-maximize-tab-plan.md | 171 ++++++++++++++++++ .../specs/2026-04-22-maximize-tab-design.md | 112 ++++++++++++ src/App.tsx | 65 ++++--- src/components/ProductDescriptions.tsx | 3 +- src/components/TopBar.tsx | 15 +- 5 files changed, 342 insertions(+), 24 deletions(-) create mode 100644 docs/superpowers/plans/2026-04-22-maximize-tab-plan.md create mode 100644 docs/superpowers/specs/2026-04-22-maximize-tab-design.md diff --git a/docs/superpowers/plans/2026-04-22-maximize-tab-plan.md b/docs/superpowers/plans/2026-04-22-maximize-tab-plan.md new file mode 100644 index 0000000..c4abec3 --- /dev/null +++ b/docs/superpowers/plans/2026-04-22-maximize-tab-plan.md @@ -0,0 +1,171 @@ +# Maximize Tab View — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Add fullscreen maximize mode to each tab view, hiding Sidebar and TopBar for maximum data visibility. + +**Architecture:** Each view component manages its own `isFullscreen` state locally. No global state. Toggle between normal and fullscreen modes with conditional rendering. + +**Tech Stack:** React useState, lucide-react icons, Tailwind CSS + +--- + +### Task 1: ProductDescriptions.tsx + +**Files:** +- Modify: `src/components/ProductDescriptions.tsx` + +- [ ] **Step 1: Add imports and state** + +Add `Maximize2` to lucide-react imports. +Add `useState` if not present. + +```tsx +const [isFullscreen, setIsFullscreen] = useState(false); +``` + +- [ ] **Step 2: Add maximize button** + +Find the component's header section (where stats/controls are rendered). +Add maximize button with `Maximize2` icon next to existing controls. + +```tsx + +``` + +- [ ] **Step 3: Wrap content in conditional render** + +After existing return statement, wrap in: + +```tsx +return ( + <> + {isFullscreen ? ( +
+ + {/* Existing JSX content - remove any overflow constraints */} +
+
+ ) : ( +
+ {/* Existing JSX content - keep current overflow settings */} +
+
+ )} + +); +``` + +--- + +### Task 2: MatrixView.tsx + +**Files:** +- Modify: `src/components/MatrixView.tsx` + +- [ ] **Step 1: Add imports and state** + +Add `Maximize2`, `X` to lucide-react imports. +Add `useState` if not present. + +- [ ] **Step 2: Add maximize button** + +Add button in the header area (search bar section). + +- [ ] **Step 3: Add conditional render** + +Same pattern as Task 1. + +--- + +### Task 3: DimensionsView.tsx + +**Files:** +- Modify: `src/components/DimensionsView.tsx` + +- [ ] **Step 1: Add imports and state** + +- [ ] **Step 2: Add maximize button** + +- [ ] **Step 3: Add conditional render** + +--- + +### Task 4: PricingView.tsx + +**Files:** +- Modify: `src/components/PricingView.tsx` + +- [ ] **Step 1: Add imports and state** + +- [ ] **Step 2: Add maximize button** + +- [ ] **Step 3: Add conditional render** + +--- + +### Task 5: ArticleDetails.tsx + +**Files:** +- Modify: `src/components/ArticleDetails.tsx` + +- [ ] **Step 1: Add imports and state** + +- [ ] **Step 2: Add maximize button** + +- [ ] **Step 3: Add conditional render** + +--- + +### Task 6: PendingValidationView.tsx + +**Files:** +- Modify: `src/components/PendingValidationView.tsx` + +- [ ] **Step 1: Add imports and state** + +- [ ] **Step 2: Add maximize button** + +- [ ] **Step 3: Add conditional render** + +--- + +### Task 7: MissingDataView.tsx + +**Files:** +- Modify: `src/components/MissingDataView.tsx` + +- [ ] **Step 1: Add imports and state** + +- [ ] **Step 2: Add maximize button** + +- [ ] **Step 3: Add conditional render** + +--- + +### Task 8: HistoryView.tsx + +**Files:** +- Modify: `src/components/HistoryView.tsx` + +- [ ] **Step 1: Add imports and state** + +- [ ] **Step 2: Add maximize button** + +- [ ] **Step 3: Add conditional render** + +--- + +**Verification:** Run `npm run lint` to ensure no TypeScript errors. \ No newline at end of file diff --git a/docs/superpowers/specs/2026-04-22-maximize-tab-design.md b/docs/superpowers/specs/2026-04-22-maximize-tab-design.md new file mode 100644 index 0000000..f582e5a --- /dev/null +++ b/docs/superpowers/specs/2026-04-22-maximize-tab-design.md @@ -0,0 +1,112 @@ +# Maximize Tab View — Spec + +## Concept & Vision + +Each tab view (Matrix, Product Descriptions, Dimensions, etc.) can be maximized to fullscreen mode, hiding all navigation chrome (Sidebar, TopBar) to maximize data visibility. A floating close button allows returning to normal view. The effect is immersive and focused — like pressing F11 in a browser. + +## Design + +### Normal Mode +- Sidebar (240px) visible on left +- TopBar visible on top +- Main content fills remaining space + +### Maximized Mode +- Sidebar: `display: none` +- TopBar: `display: none` +- Fullscreen overlay covers entire viewport +- Background: `#041021` (matches main content bg) +- Content area: max-height `100vh`, overflow scroll +- Floating close button: top-right corner, fixed position + +### Close Button +- Position: fixed, top-right +- Size: 40x40px +- Background: `rgba(30, 41, 59, 0.9)` with blur +- Border: 1px `slate-600` +- Icon: `X` from lucide-react, white, 20px +- Hover: bg `slate-700`, scale 1.05 +- Z-index: 50 +- Tooltip: "Exit fullscreen" + +## Layout & Structure + +### Implementation Pattern + +Each view component receives `isFullscreen?: boolean` prop and renders: + +``` +{maximizeButton && ( + +)} + +{isFullscreen ? ( +
+ + {/* Tab content rendered at full height */} +
+) : ( +
+ {/* Tab content normal */} +
+)} +``` + +### State Management +- Each tab manages its own `isFullscreen` state locally +- No global state needed — each tab remembers its own fullscreen mode +- State resets to normal when switching tabs (intentional) + +## Features & Interactions + +### Toggle Button +- Location: Each tab view has a maximize button (icon) in its header area +- Icon: `Maximize2` from lucide-react +- Click: Enter fullscreen mode +- Hover: Slight scale increase, cursor pointer + +### Exit Fullscreen +- Click floating X button +- Instantly returns to normal layout +- No animation needed + +## Component Inventory + +### MaximizeButton (in each tab header) +- States: default, hover +- Color: `slate-400` default, `slate-200` hover + +### FullscreenCloseButton (floating) +- States: default, hover, active +- Default: semi-transparent dark bg, subtle border +- Hover: lighter bg, slight scale +- Z-index ensures it's above all content + +### FullscreenOverlay +- Fixed positioning +- Matches app background color +- Contains scrollable content + +## Technical Approach + +- Add `isFullscreen` state to each view component +- Add toggle function +- Conditional rendering based on state +- Icons from `lucide-react`: `Maximize2`, `X` +- All styling via Tailwind classes +- No additional dependencies + +## Affected Components + +1. `ProductDescriptions.tsx` +2. `MatrixView.tsx` +3. `DimensionsView.tsx` +4. `PricingView.tsx` +5. `ArticleDetails.tsx` +6. `PendingValidationView.tsx` +7. `MissingDataView.tsx` +8. `HistoryView.tsx` \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 619f126..a5a9129 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -523,6 +523,8 @@ export default function App() { }; }, [appState.data]); + const [isMaximized, setIsMaximized] = useState(false); + if (!session) { return { const stored = getStoredSession(); @@ -534,28 +536,49 @@ export default function App() { return (
- { setRefreshTrigger(t => t + 1); }} - hasData={appState.data.length > 0} - hasUnsavedChanges={appState.hasUnsavedChanges} - userEmail={session.user.email} - onSignOut={handleSignOut} - canUndo={undoHistory.length > 0} - onUndo={handleUndo} - undoMessage={undoHistory[0]?.message} - undoSteps={undoHistory.length} - pendingCount={Object.keys(pendingRows).length} - pendingChanges={Object.fromEntries(Object.entries(pendingRows).map(([k, v]) => [k, { articleName: (v as any).articleName }]))} - onSaveAll={handleSaveAll} - onRevertRow={handleRevertRow} - isSavingAll={isSavingAll} - /> + {!isMaximized && ( + { setRefreshTrigger(t => t + 1); }} + hasData={appState.data.length > 0} + hasUnsavedChanges={appState.hasUnsavedChanges} + userEmail={session.user.email} + onSignOut={handleSignOut} + canUndo={undoHistory.length > 0} + onUndo={handleUndo} + undoMessage={undoHistory[0]?.message} + undoSteps={undoHistory.length} + pendingCount={Object.keys(pendingRows).length} + pendingChanges={Object.fromEntries(Object.entries(pendingRows).map(([k, v]) => [k, { articleName: (v as any).articleName }]))} + onSaveAll={handleSaveAll} + onRevertRow={handleRevertRow} + isSavingAll={isSavingAll} + isMaximized={isMaximized} + onToggleMaximize={() => setIsMaximized(true)} + /> + )}
- -
+ {!isMaximized && ( + + )} +
+ {isMaximized && ( + + )} {isLoadingDefault ? (
diff --git a/src/components/ProductDescriptions.tsx b/src/components/ProductDescriptions.tsx index f01a3fa..9041716 100644 --- a/src/components/ProductDescriptions.tsx +++ b/src/components/ProductDescriptions.tsx @@ -1,7 +1,7 @@ import React, { useState, useMemo } from 'react'; import { ExcelRow } from '../types'; import { useColumns } from '../contexts/ColumnsContext'; -import { Search, Filter, Edit2, ChevronDown, ChevronUp, X } from 'lucide-react'; +import { Search, Filter, Edit2, ChevronDown, ChevronUp, X, Maximize2 } from 'lucide-react'; import { cn } from '../lib/utils'; import { ColumnFilterPopover } from './ColumnFilterPopover'; @@ -15,6 +15,7 @@ interface ProductDescriptionsProps { export function ProductDescriptions({ data, headers, asinColumnIndex, onEdit, rowStatuses }: ProductDescriptionsProps) { const COLUMNS = useColumns(); + const [isFullscreen, setIsFullscreen] = useState(false); // Description columns that should only have Present/Missing filters const DESCRIPTION_COLUMNS = [COLUMNS.LONG_DE, COLUMNS.LONG_EN, COLUMNS.SHORT_DE, COLUMNS.SHORT_EN, COLUMNS.DETAILS_DE, COLUMNS.DETAILS_EN]; diff --git a/src/components/TopBar.tsx b/src/components/TopBar.tsx index 5fb625d..d5d7e47 100644 --- a/src/components/TopBar.tsx +++ b/src/components/TopBar.tsx @@ -1,5 +1,5 @@ import React, { useState, useRef, useEffect } from 'react'; -import { Download, LogOut, Undo2, CloudUpload, Loader2, ChevronDown, RotateCcw } from 'lucide-react'; +import { Download, LogOut, Undo2, CloudUpload, Loader2, ChevronDown, RotateCcw, Maximize2, X } from 'lucide-react'; import { cn } from '../lib/utils'; interface TopBarProps { @@ -20,9 +20,11 @@ interface TopBarProps { onSaveAll: () => Promise; onRevertRow: (articleNo: string) => void; isSavingAll: boolean; + isMaximized: boolean; + onToggleMaximize: () => void; } -export function TopBar({ stats, activeModule, onExport, onRefresh, hasData, hasUnsavedChanges, userEmail, onSignOut, canUndo, onUndo, undoMessage, undoSteps, pendingCount, pendingChanges, onSaveAll, onRevertRow, isSavingAll }: TopBarProps) { +export function TopBar({ stats, activeModule, onExport, onRefresh, hasData, hasUnsavedChanges, userEmail, onSignOut, canUndo, onUndo, undoMessage, undoSteps, pendingCount, pendingChanges, onSaveAll, onRevertRow, isSavingAll, isMaximized, onToggleMaximize }: TopBarProps) { const [showPending, setShowPending] = useState(false); const dropdownRef = useRef(null); @@ -179,6 +181,15 @@ export function TopBar({ stats, activeModule, onExport, onRefresh, hasData, hasU )} + +