@@ -13,6 +13,8 @@ import VisibilitySensor from "@/components/VisibilitySensor";
1313import { useTranslation } from "@/i18n/use-translation" ;
1414import { actionPromisify , formattedTime } from "@/lib/utils" ;
1515import {
16+ ArrowDownIcon ,
17+ ArrowUpIcon ,
1618 CardStackIcon ,
1719 DotsHorizontalIcon ,
1820 Pencil1Icon ,
@@ -29,15 +31,31 @@ import clsx from "clsx";
2931import { addDays , differenceInHours } from "date-fns" ;
3032import { TrashIcon } from "lucide-react" ;
3133import Link from "next/link" ;
34+ import { useState } from "react" ;
35+
36+ type SortBy = "created_at" | "title" | "published_at" ;
37+ type SortOrder = "asc" | "desc" ;
38+ type StatusFilter = "all" | "published" | "draft" ;
3239
3340const DashboardArticleList = ( ) => {
3441 const { _t } = useTranslation ( ) ;
3542 const queryClient = useQueryClient ( ) ;
3643 const appConfirm = useAppConfirm ( ) ;
44+
45+ const [ sortBy , setSortBy ] = useState < SortBy > ( "created_at" ) ;
46+ const [ sortOrder , setSortOrder ] = useState < SortOrder > ( "desc" ) ;
47+ const [ status , setStatus ] = useState < StatusFilter > ( "all" ) ;
48+
3749 const feedInfiniteQuery = useInfiniteQuery ( {
38- queryKey : [ "dashboard-articles" ] ,
50+ queryKey : [ "dashboard-articles" , sortBy , sortOrder , status ] ,
3951 queryFn : ( { pageParam } ) =>
40- articleActions . myArticles ( { limit : 10 , page : pageParam } ) ,
52+ articleActions . myArticles ( {
53+ limit : 10 ,
54+ page : pageParam ,
55+ sort_by : sortBy ,
56+ sort_order : sortOrder ,
57+ status,
58+ } ) ,
4159 initialPageParam : 1 ,
4260 getNextPageParam : ( lastPage ) => {
4361 const _page = lastPage ?. meta ?. currentPage ?? 1 ;
@@ -54,9 +72,9 @@ const DashboardArticleList = () => {
5472 onMutate : async ( article_id : string ) => {
5573 await queryClient . cancelQueries ( { queryKey : [ "dashboard-articles" ] } ) ;
5674
57- const previousData = queryClient . getQueryData ( [ "dashboard-articles" ] ) ;
75+ const previousData = queryClient . getQueryData ( [ "dashboard-articles" , sortBy , sortOrder , status ] ) ;
5876
59- queryClient . setQueryData ( [ "dashboard-articles" ] , ( old : any ) => {
77+ queryClient . setQueryData ( [ "dashboard-articles" , sortBy , sortOrder , status ] , ( old : any ) => {
6078 if ( ! old ) return old ;
6179
6280 return {
@@ -80,7 +98,7 @@ const DashboardArticleList = () => {
8098 } ,
8199 onError : ( err , variables , context ) => {
82100 if ( context ?. previousData ) {
83- queryClient . setQueryData ( [ "dashboard-articles" ] , context . previousData ) ;
101+ queryClient . setQueryData ( [ "dashboard-articles" , sortBy , sortOrder , status ] , context . previousData ) ;
84102 }
85103 } ,
86104 } ) ;
@@ -94,9 +112,9 @@ const DashboardArticleList = () => {
94112 onMutate : async ( article_id : string ) => {
95113 await queryClient . cancelQueries ( { queryKey : [ "dashboard-articles" ] } ) ;
96114
97- const previousData = queryClient . getQueryData ( [ "dashboard-articles" ] ) ;
115+ const previousData = queryClient . getQueryData ( [ "dashboard-articles" , sortBy , sortOrder , status ] ) ;
98116
99- queryClient . setQueryData ( [ "dashboard-articles" ] , ( old : any ) => {
117+ queryClient . setQueryData ( [ "dashboard-articles" , sortBy , sortOrder , status ] , ( old : any ) => {
100118 if ( ! old ) return old ;
101119
102120 return {
@@ -116,11 +134,23 @@ const DashboardArticleList = () => {
116134 } ,
117135 onError : ( _ , __ , context ) => {
118136 if ( context ?. previousData ) {
119- queryClient . setQueryData ( [ "dashboard-articles" ] , context . previousData ) ;
137+ queryClient . setQueryData ( [ "dashboard-articles" , sortBy , sortOrder , status ] , context . previousData ) ;
120138 }
121139 } ,
122140 } ) ;
123141
142+ const sortByLabels : Record < SortBy , string > = {
143+ created_at : _t ( "Created at" ) ,
144+ title : _t ( "Title" ) ,
145+ published_at : _t ( "Published at" ) ,
146+ } ;
147+
148+ const statusFilters : { value : StatusFilter ; label : string } [ ] = [
149+ { value : "all" , label : _t ( "All" ) } ,
150+ { value : "published" , label : _t ( "Published" ) } ,
151+ { value : "draft" , label : _t ( "Draft" ) } ,
152+ ] ;
153+
124154 return (
125155 < div >
126156 < div className = "flex items-center gap-2 justify-between" >
@@ -134,6 +164,58 @@ const DashboardArticleList = () => {
134164 </ Button >
135165 </ div >
136166
167+ { /* Sorter toolbar */ }
168+ < div className = "flex flex-wrap items-center gap-2 mt-3" >
169+ { /* Status filter */ }
170+ < div className = "flex items-center rounded-md border border-border overflow-hidden" >
171+ { statusFilters . map ( ( filter ) => (
172+ < button
173+ key = { filter . value }
174+ onClick = { ( ) => setStatus ( filter . value ) }
175+ className = { clsx (
176+ "px-3 py-1.5 text-sm transition-colors" ,
177+ status === filter . value
178+ ? "bg-primary text-primary-foreground"
179+ : "hover:bg-muted"
180+ ) }
181+ >
182+ { filter . label }
183+ </ button >
184+ ) ) }
185+ </ div >
186+
187+ { /* Sort by */ }
188+ < DropdownMenu >
189+ < DropdownMenuTrigger asChild >
190+ < Button variant = "outline" size = "sm" className = "text-sm" >
191+ { sortByLabels [ sortBy ] }
192+ </ Button >
193+ </ DropdownMenuTrigger >
194+ < DropdownMenuContent >
195+ { ( Object . keys ( sortByLabels ) as SortBy [ ] ) . map ( ( key ) => (
196+ < DropdownMenuItem key = { key } onClick = { ( ) => setSortBy ( key ) } >
197+ { sortByLabels [ key ] }
198+ </ DropdownMenuItem >
199+ ) ) }
200+ </ DropdownMenuContent >
201+ </ DropdownMenu >
202+
203+ { /* Sort order toggle */ }
204+ < Button
205+ variant = "outline"
206+ size = "sm"
207+ onClick = { ( ) => setSortOrder ( ( prev ) => ( prev === "asc" ? "desc" : "asc" ) ) }
208+ className = "flex items-center gap-1"
209+ >
210+ { sortOrder === "asc" ? (
211+ < ArrowUpIcon className = "h-4 w-4" />
212+ ) : (
213+ < ArrowDownIcon className = "h-4 w-4" />
214+ ) }
215+ < span className = "text-sm" > { sortOrder === "asc" ? _t ( "Ascending" ) : _t ( "Descending" ) } </ span >
216+ </ Button >
217+ </ div >
218+
137219 < div className = "flex flex-col divide-y divide-dashed divide-border-color mt-2" >
138220 { feedInfiniteQuery . isFetching &&
139221 Array . from ( { length : 10 } ) . map ( ( _ , i ) => (
@@ -176,18 +258,6 @@ const DashboardArticleList = () => {
176258
177259 < div className = "flex items-center gap-10 justify-between" >
178260 < div className = "flex gap-4 items-center" >
179- { /* {!article.approved_at && (
180- <p className="bg-yellow-400/30 rounded-sm px-2 py-1 text-sm">
181- 🚧 {_t("অনুমোদনাধীন")}
182- </p>
183- )} */ }
184-
185- { /* {article.approved_at && (
186- <p className="bg-green-400/30 rounded-sm px-2 py-1 text-sm">
187- ✅ {_t("অনুমোদিত")}
188- </p>
189- )} */ }
190-
191261 { ! Boolean ( article ?. published_at ) && (
192262 < p className = "bg-yellow-400/30 rounded-sm px-2 py-1 text-sm" >
193263 🚧 { _t ( "Draft" ) }
@@ -199,16 +269,6 @@ const DashboardArticleList = () => {
199269 ✅ { _t ( "Published" ) }
200270 </ p >
201271 ) }
202-
203- { /* <div className="text-forground-muted flex items-center gap-1">
204- <ChatBubbleIcon className="h-4 w-4" />
205- <p>{article?.comments_count || 0} </p>
206- </div> */ }
207-
208- { /* <div className="text-forground-muted flex items-center gap-1">
209- <ThickArrowUpIcon className="h-4 w-4" />
210- <p>{article?.votes?.score || 0} </p>
211- </div> */ }
212272 </ div >
213273 < DropdownMenu >
214274 < DropdownMenuTrigger className = "flex items-center gap-2" >
0 commit comments