@@ -9,13 +9,7 @@ import { useEffect, useMemo, useRef, useState } from 'react';
99import { Settings2 } from 'lucide-react' ;
1010import { Link , useHref , useNavigate , useParams , useSearchParams } from 'react-router-dom' ;
1111
12- import {
13- useApiClient ,
14- useList ,
15- type ActionDescriptor ,
16- type DateHierarchy ,
17- type ListRow ,
18- } from '@dar/data' ;
12+ import { useApiClient , useList , type ActionDescriptor , type ListRow } from '@dar/data' ;
1913import {
2014 columnsKey ,
2115 columnWidthsKey ,
@@ -28,6 +22,7 @@ import {
2822} from '@dar/customization' ;
2923import { Breadcrumb , Button , Card , EmptyState , Modal , Skeleton , Table } from '@dar/ui' ;
3024import { FieldValueView } from '@dar/details' ;
25+ import { DateHierarchyBar } from '@dar/list' ;
3126import { FilterBar } from '@dar/search' ;
3227
3328import { useToast } from '../toast' ;
@@ -44,21 +39,6 @@ const RESERVED_PARAMS = new Set(['q', 'page', 'all', CHANGELIST_FILTERS_PARAM]);
4439// drill doesn't silently resurrect on a later bare visit.
4540const DATE_PARAMS = new Set ( [ 'year' , 'month' , 'day' ] ) ;
4641
47- const MONTH_NAMES = [
48- 'January' ,
49- 'February' ,
50- 'March' ,
51- 'April' ,
52- 'May' ,
53- 'June' ,
54- 'July' ,
55- 'August' ,
56- 'September' ,
57- 'October' ,
58- 'November' ,
59- 'December' ,
60- ] ;
61-
6242export function ListPage ( ) {
6343 const params = useParams < { appLabel : string ; modelName : string } > ( ) ;
6444 const appLabel = params . appLabel ?? '' ;
@@ -689,94 +669,6 @@ function ListSkeleton() {
689669 ) ;
690670}
691671
692- // date_hierarchy drill-down bar (#304 — Django changelist parity). Reads
693- // `active` for the current drill path (breadcrumb, each crumb navigates
694- // up) and `buckets` for the next level's options (drill down). The
695- // backend caps the level by the field; clicking wires ?year/?month/?day.
696- function DateHierarchyBar ( {
697- dh,
698- onNavigate,
699- } : {
700- dh : DateHierarchy ;
701- onNavigate : ( path : { year ?: number | null ; month ?: number | null ; day ?: number | null } ) => void ;
702- } ) {
703- const { active, buckets } = dh ;
704- const level : 'year' | 'month' | 'day' | 'done' =
705- active . year == null
706- ? 'year'
707- : active . month == null
708- ? 'month'
709- : active . day == null
710- ? 'day'
711- : 'done' ;
712-
713- const bucketLabel = ( v : number ) : string =>
714- level === 'month' ? ( MONTH_NAMES [ v - 1 ] ?? String ( v ) ) : String ( v ) ;
715-
716- const nextPath = ( v : number ) => {
717- if ( level === 'year' ) return { year : v } ;
718- if ( level === 'month' ) return { year : active . year , month : v } ;
719- return { year : active . year , month : active . month , day : v } ;
720- } ;
721-
722- const crumb = 'rounded px-1.5 py-0.5 text-primary hover:bg-blue-50 hover:underline' ;
723-
724- return (
725- < nav aria-label = "Date hierarchy" className = "flex flex-wrap items-center gap-3 text-sm" >
726- < div className = "flex flex-wrap items-center gap-1 text-gray-500" >
727- < button type = "button" className = { crumb } onClick = { ( ) => onNavigate ( { } ) } >
728- All dates
729- </ button >
730- { active . year != null && (
731- < >
732- < span aria-hidden > /</ span >
733- < button
734- type = "button"
735- className = { crumb }
736- onClick = { ( ) => onNavigate ( { year : active . year } ) }
737- >
738- { active . year }
739- </ button >
740- </ >
741- ) }
742- { active . month != null && (
743- < >
744- < span aria-hidden > /</ span >
745- < button
746- type = "button"
747- className = { crumb }
748- onClick = { ( ) => onNavigate ( { year : active . year , month : active . month } ) }
749- >
750- { MONTH_NAMES [ active . month - 1 ] ?? active . month }
751- </ button >
752- </ >
753- ) }
754- { active . day != null && (
755- < >
756- < span aria-hidden > /</ span >
757- < span className = "px-1.5 py-0.5 font-medium text-gray-700" > { active . day } </ span >
758- </ >
759- ) }
760- </ div >
761- { level !== 'done' && buckets . length > 0 && (
762- < div className = "flex flex-wrap items-center gap-2" >
763- { buckets . map ( ( b ) => (
764- < button
765- key = { b . value }
766- type = "button"
767- onClick = { ( ) => onNavigate ( nextPath ( b . value ) ) }
768- className = "inline-flex items-center gap-1.5 rounded-full border border-gray-200 px-2.5 py-0.5 text-xs text-gray-700 hover:bg-gray-50"
769- >
770- { bucketLabel ( b . value ) }
771- < span className = "text-gray-400" > { b . count } </ span >
772- </ button >
773- ) ) }
774- </ div >
775- ) }
776- </ nav >
777- ) ;
778- }
779-
780672function capitalize ( value : string ) : string {
781673 if ( ! value ) return value ;
782674 return value . charAt ( 0 ) . toUpperCase ( ) + value . slice ( 1 ) ;
0 commit comments