@@ -13,13 +13,14 @@ import { decrypt, generateAndStoreKey, retrieveKey } from "@/utils/encryption";
13
13
import { useUser } from "@clerk/nextjs" ;
14
14
import type { Prisma } from "@prisma/client" ;
15
15
import {
16
+ ArrowDownAZ ,
17
+ ArrowDownWideNarrow ,
18
+ Clock ,
16
19
Plus ,
17
20
SquareArrowOutUpRight ,
18
21
Trash ,
19
22
User ,
20
- ArrowDownAZ ,
21
- ArrowDownWideNarrow ,
22
- Clock ,
23
+ X ,
23
24
} from "lucide-react" ;
24
25
import Image from "next/image" ;
25
26
import { useRouter } from "next/navigation" ;
@@ -32,10 +33,11 @@ import {
32
33
ContextMenuLabel ,
33
34
ContextMenuTrigger ,
34
35
} from "../ui/context-menu" ;
36
+ import { Tooltip , TooltipContent , TooltipTrigger } from "../ui/tooltip" ;
37
+ import { ConfirmationDialog } from "./dialogs/confirm-dialog" ;
35
38
import { EmptyState } from "./empty-state" ;
36
39
import { PasswordDetails } from "./password-details" ;
37
40
import { Sidebar } from "./sidebar" ;
38
- import { Tooltip , TooltipContent , TooltipTrigger } from "../ui/tooltip" ;
39
41
40
42
interface PasswordEntry {
41
43
id : string ;
@@ -79,6 +81,11 @@ export const VaultPage: React.FC<VaultPageProps> = ({ user }) => {
79
81
const [ sortBy , setSortBy ] = useState < "name" | "created" | "updated" > (
80
82
"created"
81
83
) ;
84
+ const [ isConfirmationDialogOpen , setIsConfirmationDialogOpen ] =
85
+ useState ( false ) ;
86
+ const [ passwordToDelete , setPasswordToDelete ] =
87
+ useState < PasswordEntry | null > ( null ) ;
88
+ const [ isDeleting , setIsDeleting ] = useState ( false ) ;
82
89
83
90
useEffect ( ( ) => {
84
91
const ensureEncryptionKey = async ( ) => {
@@ -319,10 +326,9 @@ export const VaultPage: React.FC<VaultPageProps> = ({ user }) => {
319
326
filteredAndSortedPasswords . map ( ( password ) => (
320
327
< ContextMenu key = { password . id } >
321
328
< ContextMenuTrigger >
322
- < Button
323
- variant = "ghost"
329
+ < div
324
330
className = { cn (
325
- "w-full justify-start rounded-xl p-4 text-left transition-all hover:bg-rose-50/50 dark:hover:bg-rose-900/50" ,
331
+ "flex w-full justify-between rounded-xl p-2 text-left transition-all hover:bg-rose-50/50 dark:hover:bg-rose-900/50 hover:cursor-pointer " ,
326
332
selectedEntry ?. id === password . id &&
327
333
"bg-rose-50 dark:bg-rose-900"
328
334
) }
@@ -347,7 +353,20 @@ export const VaultPage: React.FC<VaultPageProps> = ({ user }) => {
347
353
</ div >
348
354
</ div >
349
355
</ div >
350
- </ Button >
356
+
357
+ < Button
358
+ variant = "ghost"
359
+ size = "icon"
360
+ className = "text-muted-foreground hover:text-foreground"
361
+ onClick = { ( e ) => {
362
+ e . stopPropagation ( ) ;
363
+ setPasswordToDelete ( password ) ;
364
+ setIsConfirmationDialogOpen ( true ) ;
365
+ } }
366
+ >
367
+ < X className = "h-4 w-4" />
368
+ </ Button >
369
+ </ div >
351
370
</ ContextMenuTrigger >
352
371
< ContextMenuContent className = "rounded-xl" >
353
372
< ContextMenuLabel >
@@ -413,7 +432,16 @@ export const VaultPage: React.FC<VaultPageProps> = ({ user }) => {
413
432
</ ContextMenuContent >
414
433
</ ContextMenu >
415
434
) ) }
416
-
435
+ { activeTab === "notes" && (
436
+ < div className = "flex h-full items-center justify-center text-gray-500 dark:text-gray-400" >
437
+ No { activeTab } available
438
+ </ div >
439
+ ) }
440
+ { activeTab === "pins" && (
441
+ < div className = "flex h-full items-center justify-center text-gray-500 dark:text-gray-400" >
442
+ No { activeTab } available
443
+ </ div >
444
+ ) }
417
445
{ activeTab === "cards" && (
418
446
< div className = "flex h-full items-center justify-center text-gray-500 dark:text-gray-400" >
419
447
No { activeTab } available
@@ -452,6 +480,7 @@ export const VaultPage: React.FC<VaultPageProps> = ({ user }) => {
452
480
) }
453
481
</ div >
454
482
</ div >
483
+
455
484
{ isEditDialogOpen && (
456
485
< EditPasswordDialog
457
486
isOpen = { isEditDialogOpen }
@@ -466,6 +495,7 @@ export const VaultPage: React.FC<VaultPageProps> = ({ user }) => {
466
495
entry = { selectedEntry }
467
496
/>
468
497
) }
498
+
469
499
< CreatePasswordDialog
470
500
open = { isCreateDialogOpen }
471
501
onClose = { async ( ) => {
@@ -476,6 +506,34 @@ export const VaultPage: React.FC<VaultPageProps> = ({ user }) => {
476
506
} }
477
507
setSelectedEntry = { setSelectedEntry }
478
508
/>
509
+
510
+ < ConfirmationDialog
511
+ open = { isConfirmationDialogOpen }
512
+ onClose = { ( ) => setIsConfirmationDialogOpen ( false ) }
513
+ title = "Delete Password"
514
+ message = "Are you sure you want to delete this password? This action cannot be undone."
515
+ onConfirm = { async ( ) => {
516
+ if ( passwordToDelete ) {
517
+ setIsDeleting ( true ) ;
518
+ try {
519
+ await deletePasswordItem ( passwordToDelete . id ) ;
520
+ const updatedItems = await getPasswords ( user ?. id as string ) ;
521
+ setPasswordItems ( updatedItems ?. passwordItems ) ;
522
+ if ( selectedEntry ?. id === passwordToDelete . id ) {
523
+ setSelectedEntry ( null ) ;
524
+ }
525
+ toast . success ( "Password deleted successfully" ) ;
526
+ } catch {
527
+ toast . error ( "Failed to delete password" ) ;
528
+ } finally {
529
+ setIsDeleting ( false ) ;
530
+ setIsConfirmationDialogOpen ( false ) ;
531
+ setPasswordToDelete ( null ) ;
532
+ }
533
+ }
534
+ } }
535
+ loading = { isDeleting }
536
+ />
479
537
</ div >
480
538
) ;
481
539
} ;
0 commit comments