1
1
"use client" ;
2
2
3
3
import { useState } from "react" ;
4
- import { useQuery , useMutation , useQueryClient } from "@tanstack/react-query" ;
4
+ import { useMutation , useQuery , useQueryClient } from "@tanstack/react-query" ;
5
5
import { toast } from "sonner" ;
6
6
import { cn } from "@/lib/utils" ;
7
7
@@ -32,7 +32,7 @@ export default function ObjectList({
32
32
const [ currentPage , setCurrentPage ] = useState ( 1 ) ;
33
33
const [ selectedObjects , setSelectedObjects ] = useState < string [ ] > ( [ ] ) ;
34
34
const itemsPerPage = 100 ;
35
- const s3Region = process . env . AWS_S3_REGION ;
35
+ const s3Region = process . env . NEXT_PUBLIC_AWS_S3_REGION ;
36
36
37
37
const queryClient = useQueryClient ( ) ;
38
38
@@ -67,11 +67,18 @@ export default function ObjectList({
67
67
return keys ;
68
68
} ,
69
69
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
+ } ) ;
75
82
toast . success ( "Selected objects deleted successfully!" ) ;
76
83
setSelectedObjects ( [ ] ) ;
77
84
} ,
@@ -164,29 +171,20 @@ export default function ObjectList({
164
171
165
172
const totalPages = Math . ceil ( filteredObjects . length / itemsPerPage ) ;
166
173
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 ) ;
182
175
183
176
const extractPath = ( url : string ) => {
184
177
const match = / h t t p s ? : \/ \/ [ ^ \/ ] + ( \/ .* ) $ / . exec ( url ) ;
185
178
return match ? match [ 1 ] : url ;
186
179
} ;
187
180
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 } ` ;
190
188
} ;
191
189
192
190
return (
@@ -204,8 +202,7 @@ export default function ObjectList({
204
202
onChange = { handleFolderChange }
205
203
className = "ml-2 rounded border border-border bg-inputBg p-2"
206
204
>
207
- < option value = "" > All Folders</ option >
208
- { uniqueFolders . map ( ( folder , index ) => (
205
+ { folders . map ( ( folder , index ) => (
209
206
< option key = { index } value = { folder } >
210
207
{ folder }
211
208
</ option >
@@ -234,6 +231,17 @@ export default function ObjectList({
234
231
}
235
232
/>
236
233
</ 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 >
237
245
{ type === "server" && (
238
246
< th
239
247
className = "cursor-pointer pb-2 text-textPrimary"
@@ -247,17 +255,6 @@ export default function ObjectList({
247
255
: "" }
248
256
</ th >
249
257
) }
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 >
261
258
< th
262
259
className = "w-[80px] cursor-pointer pb-2 text-textPrimary"
263
260
onClick = { ( ) => handleSort ( "size" ) }
@@ -292,6 +289,18 @@ export default function ObjectList({
292
289
onChange = { ( ) => handleCheckboxChange ( object . key ) }
293
290
/>
294
291
</ 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 >
295
304
{ type === "server" && (
296
305
< td className = "border-t border-border py-2" >
297
306
< a
@@ -304,16 +313,6 @@ export default function ObjectList({
304
313
</ a >
305
314
</ td >
306
315
) }
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 >
317
316
< td className = "border-t border-border py-2 text-textPrimary" >
318
317
{ object . sizeInKB } KB
319
318
</ td >
@@ -362,3 +361,26 @@ export default function ObjectList({
362
361
</ div >
363
362
) ;
364
363
}
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