mirror of
https://github.com/johndoe6345789/strategy-execution-p.git
synced 2026-04-24 13:14:56 +00:00
Generated by Spark: menu can have folding
This commit is contained in:
7
PRD.md
7
PRD.md
@@ -123,7 +123,7 @@ Animations should reinforce the sense of **authoritative transitions and data re
|
||||
|
||||
- **Components**:
|
||||
- `Card` - Primary container for Strategy Cards, initiative summaries, and portfolio groups; add subtle shadow and border treatment for depth
|
||||
- **Sidebar Navigation** - Hierarchical left sidebar with grouped sections (Planning, Execution, Hoshin Kanri, Roadmaps, Reporting) replacing horizontal tabs for better scalability and visual organization
|
||||
- **Sidebar Navigation** - Hierarchical left sidebar with grouped sections (Planning, Execution, Hoshin Kanri, Roadmaps, Reporting) with collapsible/expandable sections for better space management and visual organization
|
||||
- `Dialog` - Full-screen modals for creating/editing Strategy Cards and initiatives with structured forms
|
||||
- `Table` - Initiative lists, KPI scorecards, and portfolio views with sortable columns and row hover states
|
||||
- `Badge` - Status indicators (On Track, At Risk, Blocked), priority levels, and portfolio tags with semantic colors
|
||||
@@ -146,11 +146,13 @@ Animations should reinforce the sense of **authoritative transitions and data re
|
||||
- Buttons: Navy default → Gold hover → Pressed with slight scale → Disabled with reduced opacity
|
||||
- Cards: Subtle hover elevation; active state with gold left border; selected state with gold outline
|
||||
- **Sidebar Items**: Default state with regular icon weight → Hover with accent background → Active with filled icon, primary background and shadow
|
||||
- **Sidebar Section Headers**: Clickable with caret icon → Hover with accent background → Collapsed state rotates caret 90° with smooth animation
|
||||
- Table rows: Hover with light slate background; selected with stronger slate background
|
||||
- Status badges: Green (On Track), Amber (At Risk), Red (Blocked), Gray (Not Started)
|
||||
|
||||
- **Icon Selection**:
|
||||
- `House` - Home dashboard navigation
|
||||
- `CaretDown` - Collapsible section indicators in sidebar
|
||||
- `Strategy` - Use structured grid or layers icon for Strategy Cards
|
||||
- `ChartBar` - Workbench and execution tracking
|
||||
- `FolderOpen` - Portfolio management
|
||||
@@ -171,7 +173,8 @@ Animations should reinforce the sense of **authoritative transitions and data re
|
||||
- Card padding: `p-6` for substantial internal space
|
||||
- Section gaps: `gap-6` between major sections; `gap-4` between related elements
|
||||
- List items: `py-3` for comfortable touch targets and scannability
|
||||
- **Sidebar spacing**: `p-4` container padding; `space-y-6` between sections; `space-y-1` within sections
|
||||
- **Sidebar spacing**: `p-4` container padding; `space-y-6` between sections; `space-y-1` within sections and for collapsible content
|
||||
- **Sidebar section headers**: `py-2` for clickable area; smooth 200ms transition for collapse/expand animations
|
||||
|
||||
- **Mobile**:
|
||||
- Sidebar converts to collapsible drawer with hamburger menu
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
{
|
||||
"dbType": null
|
||||
|
||||
{
|
||||
"templateVersion": 0,
|
||||
"dbType": null
|
||||
}
|
||||
85
src/App.tsx
85
src/App.tsx
@@ -14,7 +14,8 @@ import {
|
||||
Tree,
|
||||
GridFour,
|
||||
Circle,
|
||||
House
|
||||
House,
|
||||
CaretDown
|
||||
} from '@phosphor-icons/react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import StrategyCards from './components/StrategyCards'
|
||||
@@ -94,6 +95,7 @@ function App() {
|
||||
const [initiatives] = useKV<Initiative[]>('initiatives', [])
|
||||
const [activeView, setActiveView] = useState('strategy')
|
||||
const [showHome, setShowHome] = useState(true)
|
||||
const [collapsedSections, setCollapsedSections] = useKV<string[]>('sidebar-collapsed-sections', [])
|
||||
|
||||
const handleNavigate = (viewId: string) => {
|
||||
setActiveView(viewId)
|
||||
@@ -104,6 +106,16 @@ function App() {
|
||||
setShowHome(true)
|
||||
}
|
||||
|
||||
const toggleSection = (sectionId: string) => {
|
||||
setCollapsedSections((current) => {
|
||||
const sections = current || []
|
||||
if (sections.includes(sectionId)) {
|
||||
return sections.filter(id => id !== sectionId)
|
||||
}
|
||||
return [...sections, sectionId]
|
||||
})
|
||||
}
|
||||
|
||||
const activeItem = navigationSections
|
||||
.flatMap(section => section.items)
|
||||
.find(item => item.id === activeView)
|
||||
@@ -152,33 +164,52 @@ function App() {
|
||||
<span className="font-semibold text-sm">Home</span>
|
||||
</button>
|
||||
|
||||
{navigationSections.map(section => (
|
||||
<div key={section.id} className="space-y-1">
|
||||
<h3 className="px-4 text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-2">
|
||||
{section.label}
|
||||
</h3>
|
||||
<div className="space-y-1">
|
||||
{section.items.map(item => {
|
||||
const Icon = item.icon
|
||||
const isActive = activeView === item.id && !showHome
|
||||
return (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => handleNavigate(item.id)}
|
||||
className={cn(
|
||||
"w-full flex items-center gap-3 px-4 py-2.5 rounded-md transition-all text-left",
|
||||
"hover:bg-accent hover:text-accent-foreground",
|
||||
isActive && "bg-primary text-primary-foreground shadow-sm"
|
||||
)}
|
||||
>
|
||||
<Icon size={18} weight={isActive ? "fill" : "regular"} />
|
||||
<span className="font-medium text-sm">{item.label}</span>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
{navigationSections.map(section => {
|
||||
const isCollapsed = collapsedSections?.includes(section.id)
|
||||
return (
|
||||
<div key={section.id} className="space-y-1">
|
||||
<button
|
||||
onClick={() => toggleSection(section.id)}
|
||||
className="w-full flex items-center justify-between px-4 py-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground hover:text-foreground transition-colors rounded-md hover:bg-accent/50"
|
||||
>
|
||||
<span>{section.label}</span>
|
||||
<CaretDown
|
||||
size={14}
|
||||
weight="bold"
|
||||
className={cn(
|
||||
"transition-transform duration-200",
|
||||
isCollapsed && "-rotate-90"
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
className={cn(
|
||||
"space-y-1 overflow-hidden transition-all duration-200",
|
||||
isCollapsed ? "max-h-0 opacity-0" : "max-h-[500px] opacity-100"
|
||||
)}
|
||||
>
|
||||
{section.items.map(item => {
|
||||
const Icon = item.icon
|
||||
const isActive = activeView === item.id && !showHome
|
||||
return (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => handleNavigate(item.id)}
|
||||
className={cn(
|
||||
"w-full flex items-center gap-3 px-4 py-2.5 rounded-md transition-all text-left",
|
||||
"hover:bg-accent hover:text-accent-foreground",
|
||||
isActive && "bg-primary text-primary-foreground shadow-sm"
|
||||
)}
|
||||
>
|
||||
<Icon size={18} weight={isActive ? "fill" : "regular"} />
|
||||
<span className="font-medium text-sm">{item.label}</span>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user