'use client'
import React, { useEffect, useState } from 'react'
import { cn } from '../../utils/cn'
import { useDebounce } from '../../hooks/ui/use-debounce'
import { Filter02Icon, SearchIcon } from '../icons-v2-generated'
import { Button, Input, PageError } from '../ui'
import { FilterModal, type FilterGroup, type SortConfig, type SortDirection } from '../ui/filter-modal'
import type { TableFilters } from '../ui/table/types'
import { ListPageContainer, type PageActionButton } from './page-container'
export interface ListPageLayoutProps {
title: string
headerActions?: React.ReactNode
actions?: PageActionButton[]
searchPlaceholder: string
searchValue: string
onSearch: (term: string) => void
children: React.ReactNode
error?: string | null
className?: string
padding?: 'none' | 'sm' | 'md' | 'lg'
background?: 'default' | 'card' | 'transparent'
mobileFilterGroups?: FilterGroup[]
onMobileFilterChange?: (filters: TableFilters) => void
currentMobileFilters?: TableFilters
mobileSortConfig?: SortConfig
onMobileSort?: (column: string, direction: SortDirection) => void
mobileFilterTitle?: string
stickyHeader?: boolean
}
/**
* @deprecated Use `PageLayout` from `'../layout/page-layout'` instead.
*
* Standardized Layout for List Pages
*
* A comprehensive layout component that provides 100% consistent structure
* for all list-based pages throughout the OpenFrame application.
*
* ## Layout Structure:
* ```
* ┌─────────────────────────────────────────────────────────────┐
* │ Title (left aligned) │ Header Actions (right aligned) │
* ├─────────────────────────────────────────────────────────────┤
* │ Search Bar (full width) │
* ├─────────────────────────────────────────────────────────────┤
* │ Table/Grid with Filters │
* │ (main content) │
* └─────────────────────────────────────────────────────────────┘
* ```
*
* ## Key Features:
* - **Consistent Spacing**: All pages use identical padding and gaps
* - **Responsive Design**: Works seamlessly across all screen sizes
* - **Accessibility**: Proper semantic HTML and ARIA support
* - **Error Handling**: Built-in error state display
* - **Flexible Actions**: Supports any combination of buttons/controls
* - **Search Integration**: Standardized search bar positioning
*
* ## Currently Used By:
* - `/devices` - Device management with table/grid toggle
* - `/logs-page` - Log analysis with refresh functionality
* - `/scripts` - Script management with new/refresh actions
* - `/mingo` tabs - Archive/current chats with filtering
* - `/policies-and-queries` tabs - Policy management with refresh/new actions
*
* ## Design Tokens:
* - Uses ODS design system tokens for consistent theming
* - Maintains proper contrast ratios and accessibility standards
* - Supports both light and dark mode themes
*
* ## Performance:
* - Minimal re-renders through proper prop drilling
* - Optimized for large datasets with virtualization support
* - Built-in debouncing for search operations
*
* @example
* ```tsx
* Refresh}
* searchPlaceholder="Search items..."
* searchValue={searchTerm}
* onSearch={setSearchTerm}
* error={error}
* >
*
*
* ```
*/
export function ListPageLayout({
title,
headerActions,
actions,
searchPlaceholder,
searchValue,
onSearch,
children,
error,
className,
padding = 'sm',
background = 'default',
mobileFilterGroups,
onMobileFilterChange,
currentMobileFilters,
mobileSortConfig,
onMobileSort,
mobileFilterTitle,
stickyHeader = false,
}: ListPageLayoutProps) {
const [mobileFilterOpen, setMobileFilterOpen] = useState(false)
const [localSearchValue, setLocalSearchValue] = useState(searchValue)
const debouncedSearchValue = useDebounce(localSearchValue, 500)
// Sync local value when controlled value changes externally
useEffect(() => {
setLocalSearchValue(searchValue)
}, [searchValue])
// Call onSearch when debounced value changes
useEffect(() => {
if (debouncedSearchValue !== searchValue) {
onSearch(debouncedSearchValue)
}
}, [debouncedSearchValue, onSearch, searchValue])
// Check if mobile filter is enabled (has filter groups or sort config)
const hasMobileFilter = (mobileFilterGroups && mobileFilterGroups.length > 0) ||
(mobileSortConfig && mobileSortConfig.columns.length > 0)
if (error) {
return
}
return (
{/* Search Bar with Mobile Filter Button */}
setLocalSearchValue(e.target.value)}
value={localSearchValue}
className="flex-1"
startAdornment={}
/>
{/* Mobile Filter Button - only visible on mobile when filter is enabled */}
{hasMobileFilter && (
)}
)
}
// Re-export PageActionButton type for convenience
export type { PageActionButton } from './page-container'
/** @deprecated Use `PageLayout` from `'../layout/page-layout'` instead. */
export default ListPageLayout