11"use client" ;
22
33import { useState } from "react" ;
4- import { useQuery , useMutation , useQueryClient } from "@tanstack/react-query" ;
4+ import { useMutation , useQuery , useQueryClient } from "@tanstack/react-query" ;
55import { toast } from "sonner" ;
66import { cn } from "@/lib/utils" ;
77
@@ -32,7 +32,7 @@ export default function ObjectList({
3232 const [ currentPage , setCurrentPage ] = useState ( 1 ) ;
3333 const [ selectedObjects , setSelectedObjects ] = useState < string [ ] > ( [ ] ) ;
3434 const itemsPerPage = 100 ;
35- const s3Region = process . env . AWS_S3_REGION ;
35+ const s3Region = process . env . NEXT_PUBLIC_AWS_S3_REGION ;
3636
3737 const queryClient = useQueryClient ( ) ;
3838
@@ -67,11 +67,18 @@ export default function ObjectList({
6767 return keys ;
6868 } ,
6969 onSuccess : ( deletedKeys ) => {
70- queryClient . setQueryData < S3ObjectData > ( [ "objects" , type ] , ( oldData ) => ( {
71- objects : oldData ?. objects . filter (
72- ( object ) => ! deletedKeys . includes ( object . key ) ,
73- ) ,
74- } ) ) ;
70+ queryClient . setQueryData < S3ObjectData > ( [ "objects" , type ] , ( oldData ) => {
71+ if ( ! oldData ) {
72+ return { objects : [ ] } ;
73+ }
74+
75+ return {
76+ ...oldData ,
77+ objects : oldData . objects . filter (
78+ ( object ) => ! deletedKeys . includes ( object . key ) ,
79+ ) ,
80+ } ;
81+ } ) ;
7582 toast . success ( "Selected objects deleted successfully!" ) ;
7683 setSelectedObjects ( [ ] ) ;
7784 } ,
@@ -164,29 +171,20 @@ export default function ObjectList({
164171
165172 const totalPages = Math . ceil ( filteredObjects . length / itemsPerPage ) ;
166173
167- const uniqueFolders = Array . from (
168- new Set (
169- objects . map ( ( object ) => {
170- const segments = object . key . split ( "/" ) ;
171- if ( segments [ 0 ] === "assets" ) {
172- if ( segments [ 1 ] === "images" && segments . length > 2 ) {
173- return `assets/${ segments [ 2 ] } ` ;
174- } else if ( segments [ 1 ] !== "images" ) {
175- return null ;
176- }
177- }
178- return segments [ 0 ] ;
179- } ) ,
180- ) ,
181- ) . filter ( ( folder ) => folder !== null ) ;
174+ const folders = filterObjectsByPrefix ( objects , type ) ;
182175
183176 const extractPath = ( url : string ) => {
184177 const match = / h t t p s ? : \/ \/ [ ^ \/ ] + ( \/ .* ) $ / . exec ( url ) ;
185178 return match ? match [ 1 ] : url ;
186179 } ;
187180
188- const generateS3Link = ( key : string ) => {
189- return `https://${ s3Region } .console.aws.amazon.com/s3/object/blazing-peon-images?region=${ s3Region } &bucketType=general&prefix=${ key } ` ;
181+ const generateS3Link = ( key : string , type : string ) => {
182+ const bucket =
183+ type === "server"
184+ ? process . env . NEXT_PUBLIC_AWS_S3_BUCKET_NAME
185+ : process . env . NEXT_PUBLIC_AWS_S3_STORAGE_BUCKET_NAME ;
186+
187+ return `https://${ s3Region } .console.aws.amazon.com/s3/object/${ bucket } ?region=${ s3Region } &bucketType=general&prefix=${ key } ` ;
190188 } ;
191189
192190 return (
@@ -204,8 +202,7 @@ export default function ObjectList({
204202 onChange = { handleFolderChange }
205203 className = "ml-2 rounded border border-border bg-inputBg p-2"
206204 >
207- < option value = "" > All Folders</ option >
208- { uniqueFolders . map ( ( folder , index ) => (
205+ { folders . map ( ( folder , index ) => (
209206 < option key = { index } value = { folder } >
210207 { folder }
211208 </ option >
@@ -234,6 +231,17 @@ export default function ObjectList({
234231 }
235232 />
236233 </ th >
234+ < th
235+ className = "cursor-pointer pb-2 text-textPrimary"
236+ onClick = { ( ) => handleSort ( "s3Object" ) }
237+ >
238+ S3 Object{ " " }
239+ { sortOrder . column === "s3Object"
240+ ? sortOrder . order === "asc"
241+ ? "▲"
242+ : "▼"
243+ : "" }
244+ </ th >
237245 { type === "server" && (
238246 < th
239247 className = "cursor-pointer pb-2 text-textPrimary"
@@ -247,17 +255,6 @@ export default function ObjectList({
247255 : "" }
248256 </ th >
249257 ) }
250- < th
251- className = "cursor-pointer pb-2 text-textPrimary"
252- onClick = { ( ) => handleSort ( "s3Object" ) }
253- >
254- S3 Object{ " " }
255- { sortOrder . column === "s3Object"
256- ? sortOrder . order === "asc"
257- ? "▲"
258- : "▼"
259- : "" }
260- </ th >
261258 < th
262259 className = "w-[80px] cursor-pointer pb-2 text-textPrimary"
263260 onClick = { ( ) => handleSort ( "size" ) }
@@ -292,6 +289,18 @@ export default function ObjectList({
292289 onChange = { ( ) => handleCheckboxChange ( object . key ) }
293290 />
294291 </ td >
292+ < td className = "border-t border-border py-2" >
293+ < a
294+ href = { generateS3Link ( object . key , type ) }
295+ target = "_blank"
296+ rel = "noopener noreferrer"
297+ className = "text-link hover:underline"
298+ >
299+ { type === "server"
300+ ? object . key . split ( "/" ) . pop ( )
301+ : object . key }
302+ </ a >
303+ </ td >
295304 { type === "server" && (
296305 < td className = "border-t border-border py-2" >
297306 < a
@@ -304,16 +313,6 @@ export default function ObjectList({
304313 </ a >
305314 </ td >
306315 ) }
307- < td className = "border-t border-border py-2" >
308- < a
309- href = { generateS3Link ( object . key ) }
310- target = "_blank"
311- rel = "noopener noreferrer"
312- className = "text-link text-blue-500 hover:underline"
313- >
314- { object . key . split ( "/" ) . pop ( ) }
315- </ a >
316- </ td >
317316 < td className = "border-t border-border py-2 text-textPrimary" >
318317 { object . sizeInKB } KB
319318 </ td >
@@ -362,3 +361,26 @@ export default function ObjectList({
362361 </ div >
363362 ) ;
364363}
364+
365+ function filterObjectsByPrefix (
366+ objects : S3Object [ ] ,
367+ type : "server" | "storage" ,
368+ ) : string [ ] {
369+ const prefix = type === "server" ? "assets/" : "images/assets/" ;
370+
371+ // Ensure uniqueness
372+ return objects
373+ . filter ( ( obj ) => obj . key . startsWith ( prefix ) )
374+ . map ( ( obj ) => {
375+ // Remove prefix
376+ const path = obj . key ;
377+ // Remove the file name (anything after the last '/')
378+ const lastSlashIndex = path . lastIndexOf ( "/" ) ;
379+ if ( lastSlashIndex === - 1 ) {
380+ return "" ; // Return empty string if no folder structure exists
381+ }
382+ return path . substring ( 0 , lastSlashIndex ) ;
383+ } )
384+ . filter ( ( folder ) => folder !== "" ) // Remove empty strings
385+ . filter ( ( folder , index , self ) => self . indexOf ( folder ) === index ) ;
386+ }
0 commit comments