Skip to content

Commit 7c28d99

Browse files
committed
feat: update filter image
1 parent b3fde42 commit 7c28d99

File tree

19 files changed

+204
-145
lines changed

19 files changed

+204
-145
lines changed

apps/web/@/hooks/useGetImages.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { useMemo } from "react"
22

3-
import { TImageFilter, TListImageResponse } from "database"
3+
import { IImageFilter, IListImageResponse } from "database"
44
import useSWRInfinite from "swr/infinite"
55

6-
const getImages = async (url): Promise<TListImageResponse> => {
6+
const getImages = async (url): Promise<IListImageResponse> => {
77
const response = await fetch(url, {
88
method: "GET",
99
headers: {
@@ -18,23 +18,24 @@ const getImages = async (url): Promise<TListImageResponse> => {
1818
return response.json()
1919
}
2020

21-
export function useGetImages(filter: TImageFilter) {
21+
export function useGetImages(filter: IImageFilter) {
2222
const { data, mutate, size, setSize, isLoading, error } = useSWRInfinite(
2323
(index) => {
2424
const queryParams = new URLSearchParams()
2525

2626
if (filter.search) queryParams.append("search", filter.search)
27-
// if (filter.order) queryParams.append("order", filter.order)
28-
// if (filter.orderBy) queryParams.append("orderBy", filter.orderBy)
27+
if (filter.order) queryParams.append("order", filter.order)
28+
if (filter.orderBy) queryParams.append("orderBy", filter.orderBy)
2929
queryParams.append("page", (index + 1).toString())
3030

3131
return `/api/protected/images?${queryParams.toString()}`
3232
},
3333
(url) => getImages(url)
3434
)
3535

36-
const images = useMemo(() => (data || []).flatMap((page) => page.data.data.data), [data])
36+
const images = useMemo(() => (data || []).flatMap((page) => page?.data?.data?.data), [data])
3737
const totalPages = useMemo(() => data?.[0]?.data?.data?.totalPages, [data])
38+
const total = useMemo(() => data?.[0]?.data?.data?.total, [data])
3839

3940
const fetchMore = () => {
4041
if (size >= totalPages) {
@@ -46,6 +47,7 @@ export function useGetImages(filter: TImageFilter) {
4647

4748
return {
4849
images,
50+
total,
4951
isLoading,
5052
isError: error,
5153
mutate,

apps/web/@/hooks/useInfinityScroll.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ const useInfiniteScroll = (callback: () => void, root: HTMLElement | null, isFet
88
const handleIntersection = useCallback(
99
(entries: IntersectionObserverEntry[]) => {
1010
if (entries[0].isIntersecting && !isFetching) {
11-
console.log("isIntersecting", entries[0])
1211
callback?.()
1312
}
1413
},

apps/web/@/messages/en.json

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,15 @@
111111
"turn_draft": "Turn draft",
112112
"turn_publish": "Turn publish",
113113
"post_created": "Post created successfully",
114-
"post_updated": "Post updated successfully"
114+
"post_updated": "Post updated successfully",
115+
"order_by": "Order by",
116+
"order_by_asc": "Ascending",
117+
"order_by_desc": "Descending",
118+
"order_by_created_at": "Created at",
119+
"order_by_name": "Name",
120+
"order_by_size": "Size",
121+
"order_by_type": "Type",
122+
"order_by_uploaded_at": "Uploaded at"
115123
},
116124
"uploads": {
117125
"asset_management": "Asset Management",
@@ -120,6 +128,13 @@
120128
"upload": "Upload",
121129
"has_been_selected": "has been selected",
122130
"image_uploaded_successfully": "Image uploaded successfully",
123-
"error_uploading_image": "Error uploading image"
131+
"error_uploading_image": "Error uploading image",
132+
"total_images": "{total, plural, =0 {No image found} =1 {Total 1 image} other {Total # images}}",
133+
"order_by": {
134+
"newest": "Newest",
135+
"oldest": "Oldest",
136+
"name_asc": "Name (A → Z)",
137+
"name_desc": "Name (Z → A)"
138+
}
124139
}
125140
}

apps/web/@/molecules/upload/AssetsManagement.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
import { useMemo, useRef } from "react"
1+
import { useEffect, useMemo, useRef } from "react"
22

33
import { useGetImages } from "@/hooks/useGetImages"
44
import useInfiniteScroll from "@/hooks/useInfinityScroll"
55

66
import { useFileManager } from "./FileManagerContainer"
77
import ImageList from "./ImageList"
8-
import ImageSearchBar from "./ImageSearchBar"
98

109
const AssetManagement = () => {
11-
const { search } = useFileManager()
10+
const { search, setTotal } = useFileManager()
1211
const imageListRef = useRef<HTMLDivElement>(null)
1312

1413
const filterParams = useMemo(() => {
@@ -17,9 +16,13 @@ const AssetManagement = () => {
1716
}
1817
}, [search])
1918

20-
const { images, isLoading, fetchMore } = useGetImages(filterParams)
19+
const { images, isLoading, total, fetchMore } = useGetImages(filterParams)
2120
const { setNode } = useInfiniteScroll(fetchMore, imageListRef.current, isLoading)
2221

22+
useEffect(() => {
23+
setTotal(total)
24+
}, [total, setTotal])
25+
2326
return (
2427
<div
2528
ref={imageListRef}
@@ -30,9 +33,10 @@ const AssetManagement = () => {
3033
images={images}
3134
/>
3235

33-
<div ref={setNode}>
34-
<div className="h-10 w-full bg-transparent" />
35-
</div>
36+
<div
37+
ref={setNode}
38+
className="h-10 w-full bg-transparent"
39+
/>
3640
</div>
3741
)
3842
}

apps/web/@/molecules/upload/FileManagerContainer.tsx

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,24 @@ import React, { createContext, useCallback, useContext, useReducer } from "react
22

33
import { Image, OrderBy } from "database"
44

5-
export const OrderField = {
6-
newest: "createdAt",
7-
oldest: "createdAt",
8-
nameAsc: "name",
9-
nameDesc: "name",
5+
export const OrderByField = {
6+
newest: "newest",
7+
oldest: "oldest",
8+
nameAsc: "name_asc",
9+
nameDesc: "name_desc",
1010
}
1111

1212
export type FileManagerState = {
1313
selectedFiles: Image[]
1414
search: string
15-
orderBy: OrderBy
16-
order: (typeof OrderField)[keyof typeof OrderField]
15+
order: (typeof OrderByField)[keyof typeof OrderByField]
16+
total: number
1717
}
1818
export type FileManagerContextType = FileManagerState & {
1919
setSelectedFiles: (files: Image[]) => void
2020
setSearch: (search: string) => void
21-
setOrder: (order: "asc" | "desc") => void
22-
setOrderBy: (orderBy: string) => void
21+
setOrder: (order: (typeof OrderByField)[keyof typeof OrderByField]) => void
22+
setTotal: (total: number) => void
2323
}
2424

2525
const FileManagerContext = createContext<FileManagerContextType | null>(null)
@@ -33,7 +33,7 @@ export const useFileManager = () => {
3333
}
3434

3535
type FileManagerAction = {
36-
type: "SET_SELECTED_FILES" | "SET_SEARCH" | "SET_ORDER" | "SET_ORDER_BY"
36+
type: "SET_SELECTED_FILES" | "SET_SEARCH" | "SET_ORDER" | "SET_TOTAL"
3737
payload: any
3838
}
3939

@@ -45,8 +45,8 @@ function fileManagerReducer(state: FileManagerState, action: FileManagerAction):
4545
return { ...state, search: action.payload }
4646
case "SET_ORDER":
4747
return { ...state, order: action.payload }
48-
case "SET_ORDER_BY":
49-
return { ...state, orderBy: action.payload }
48+
case "SET_TOTAL":
49+
return { ...state, total: action.payload }
5050
default:
5151
return state
5252
}
@@ -56,8 +56,8 @@ const FileManagerContainer = ({ children }: { children: React.ReactNode }) => {
5656
const [state, dispatch] = useReducer(fileManagerReducer, {
5757
selectedFiles: [],
5858
search: "",
59-
order: OrderField.newest,
60-
orderBy: "asc",
59+
order: OrderByField.newest,
60+
total: 0,
6161
})
6262

6363
const setSelectedFiles = useCallback((files: Image[]) => {
@@ -72,8 +72,8 @@ const FileManagerContainer = ({ children }: { children: React.ReactNode }) => {
7272
dispatch({ type: "SET_ORDER", payload: order })
7373
}, [])
7474

75-
const setOrderBy = useCallback((orderBy: string) => {
76-
dispatch({ type: "SET_ORDER_BY", payload: orderBy })
75+
const setTotal = useCallback((total: number) => {
76+
dispatch({ type: "SET_TOTAL", payload: total })
7777
}, [])
7878

7979
return (
@@ -83,7 +83,7 @@ const FileManagerContainer = ({ children }: { children: React.ReactNode }) => {
8383
setSelectedFiles,
8484
setSearch,
8585
setOrder,
86-
setOrderBy,
86+
setTotal,
8787
}}
8888
>
8989
{children}

apps/web/@/molecules/upload/ImageItem.tsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from "react"
22
import Image from "next/image"
33

44
import { Image as ImageType } from "database"
5-
import { Check, CheckCircle, Circle, CircleDot, TrashIcon } from "lucide-react"
5+
import { CheckCircle, Circle, TrashIcon } from "lucide-react"
66
import { Button, cn } from "ui"
77

88
import { useFileManager } from "./FileManagerContainer"
@@ -14,15 +14,16 @@ interface ImageItemProps {
1414
export default function ImageItem({ image }: ImageItemProps) {
1515
const { selectedFiles, setSelectedFiles } = useFileManager()
1616

17-
const handleSelect = (image: ImageType) => {
18-
setSelectedFiles([image])
17+
const handleSelect = () => {
18+
setSelectedFiles(selectedFiles?.at(0)?.id === image.id ? [] : [image])
1919
}
2020

2121
return (
22-
<div
22+
<button
2323
className={cn("group relative border-2", {
2424
"border-blue-500": selectedFiles?.at(0)?.id === image.id,
2525
})}
26+
onClick={handleSelect}
2627
>
2728
<Image
2829
src={`${process.env.NEXT_PUBLIC_FRONTEND_URL}${image.url}`}
@@ -42,20 +43,14 @@ export default function ImageItem({ image }: ImageItemProps) {
4243
<Button
4344
variant="outline"
4445
className="absolute right-1 top-1 h-7 w-7 rounded-full p-0"
45-
onClick={() => {
46-
if (selectedFiles?.at(0)?.id === image.id) {
47-
setSelectedFiles([])
48-
} else {
49-
handleSelect(image)
50-
}
51-
}}
46+
// onClick={() => handleSelect(image)}
5247
>
5348
{selectedFiles?.at(0)?.id === image.id ? (
5449
<CheckCircle className="h-6 w-6 text-blue-500" />
5550
) : (
5651
<Circle className="h-6 w-6" />
5752
)}
5853
</Button>
59-
</div>
54+
</button>
6055
)
6156
}

apps/web/@/molecules/upload/ImageList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const ImageList: React.FC<ImageListProps> = ({ images, isLoading }) => {
2121
}
2222

2323
return (
24-
<div className="mt-2 flex flex-wrap gap-3 p-1">
24+
<div className="flex flex-wrap gap-3 p-1">
2525
{images?.map((image) => (
2626
<ImageItem
2727
key={image.id}

apps/web/@/molecules/upload/ImageSearchBar.tsx

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,29 @@
11
"use client"
22

3+
import { ArrowDownWideNarrow } from "lucide-react"
34
import { useTranslations } from "next-intl"
4-
import { Button, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "ui"
5+
import {
6+
Button,
7+
DropdownMenu,
8+
DropdownMenuContent,
9+
DropdownMenuLabel,
10+
DropdownMenuRadioGroup,
11+
DropdownMenuRadioItem,
12+
DropdownMenuSeparator,
13+
DropdownMenuTrigger,
14+
Input,
15+
Select,
16+
SelectContent,
17+
SelectItem,
18+
SelectTrigger,
19+
SelectValue,
20+
} from "ui"
521

6-
import { useFileManager } from "./FileManagerContainer"
22+
import { OrderByField, useFileManager } from "./FileManagerContainer"
723

824
export default function ImageSearchBar() {
925
const t = useTranslations()
10-
const { search, setSearch } = useFileManager()
26+
const { search, order, setSearch, setOrder } = useFileManager()
1127

1228
const onClearSearch = () => {
1329
setSearch("")
@@ -21,17 +37,31 @@ export default function ImageSearchBar() {
2137
className="max-w-[300px]"
2238
/>
2339

24-
<Select>
25-
<SelectTrigger className="w-[180px]">
26-
<SelectValue placeholder={t("common.sort_by")} />
27-
</SelectTrigger>
28-
<SelectContent>
29-
<SelectItem value="apple">Name A → Z</SelectItem>
30-
<SelectItem value="banana">Name Z → A</SelectItem>
31-
<SelectItem value="blueberry">Recent created</SelectItem>
32-
<SelectItem value="grapes">Last created</SelectItem>
33-
</SelectContent>
34-
</Select>
40+
<DropdownMenu>
41+
<DropdownMenuTrigger asChild>
42+
<Button
43+
variant="outline"
44+
className="gap-1"
45+
>
46+
<ArrowDownWideNarrow size={16} />
47+
{t(`uploads.order_by.${order}`)}
48+
</Button>
49+
</DropdownMenuTrigger>
50+
<DropdownMenuContent className="w-56">
51+
<DropdownMenuLabel>Order by</DropdownMenuLabel>
52+
<DropdownMenuSeparator />
53+
<DropdownMenuRadioGroup
54+
value={order}
55+
onValueChange={setOrder}
56+
>
57+
{Object.values(OrderByField).map((order) => (
58+
<DropdownMenuRadioItem value={order}>
59+
{t(`uploads.order_by.${order}`)}
60+
</DropdownMenuRadioItem>
61+
))}
62+
</DropdownMenuRadioGroup>
63+
</DropdownMenuContent>
64+
</DropdownMenu>
3565

3666
{search && (
3767
<Button

apps/web/@/molecules/upload/Loading.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ export default function Loading() {
44
return (
55
<div className="mt-2 flex flex-wrap gap-3 p-1">
66
{Array.from({ length: 10 }).map((_, i) => (
7-
<Skeleton className="w-[160px h-[160px] " />
7+
<Skeleton
8+
key={i}
9+
className="h-[160px] w-[160px]"
10+
/>
811
))}
912
</div>
1013
)

0 commit comments

Comments
 (0)