ReviewRow.tsx97 lines · main
1import { useQueryClient } from '@tanstack/react-query'
2import dayjs from 'dayjs'
3import { MoreVertical, X } from 'lucide-react'
4import { useRouter } from 'next/router'
5import { toast } from 'sonner'
6import {
7 Button,
8 DropdownMenu,
9 DropdownMenuContent,
10 DropdownMenuItem,
11 DropdownMenuTrigger,
12} from 'ui'
13
14import { useBranchUpdateMutation } from '@/data/branches/branch-update-mutation'
15import type { Branch } from '@/data/branches/branches-query'
16import { branchKeys } from '@/data/branches/keys'
17
18interface ReviewRowProps {
19 branch: Branch
20}
21
22export const ReviewRow = ({ branch }: ReviewRowProps) => {
23 const router = useRouter()
24 const queryClient = useQueryClient()
25 const { project_ref: branchRef, parent_project_ref: projectRef } = branch
26
27 const { mutate: updateBranch, isPending: isUpdating } = useBranchUpdateMutation({
28 onSuccess: () => {
29 toast.success('Branch marked as not ready for review')
30 queryClient.invalidateQueries({
31 queryKey: branchKeys.list(projectRef),
32 })
33 },
34 onError: (error) => {
35 toast.error(`Failed to update branch: ${error.message}`)
36 },
37 })
38
39 const handleRowClick = () => {
40 router.push(`/project/${branchRef}/merge`)
41 }
42
43 const handleNotReadyForReview = (e?: Event) => {
44 e?.preventDefault()
45 e?.stopPropagation()
46
47 updateBranch({
48 branchRef,
49 projectRef,
50 requestReview: false,
51 })
52 }
53
54 const formattedReviewDate = branch.review_requested_at
55 ? dayjs(branch.review_requested_at).format('MMM DD, YYYY HH:mm')
56 : ''
57
58 return (
59 <div
60 className="w-full flex items-center justify-between px-6 py-3 hover:bg-surface-100 cursor-pointer transition-colors"
61 onClick={handleRowClick}
62 >
63 <div className="flex items-center gap-x-4">
64 <div className="flex gap-4">
65 <h4 className="text-sm text-foreground" title={branch.name}>
66 {branch.name}
67 </h4>
68 <p className="text-foreground-light">{formattedReviewDate}</p>
69 </div>
70 </div>
71
72 <div className="flex items-center gap-x-2">
73 <DropdownMenu>
74 <DropdownMenuTrigger asChild>
75 <Button
76 type="text"
77 icon={<MoreVertical />}
78 className="px-1"
79 onClick={(e) => e.stopPropagation()}
80 />
81 </DropdownMenuTrigger>
82 <DropdownMenuContent className="w-56" side="bottom" align="end">
83 <DropdownMenuItem
84 className="gap-x-2"
85 disabled={isUpdating}
86 onClick={(e) => e.stopPropagation()}
87 onSelect={handleNotReadyForReview}
88 >
89 <X size={14} />
90 Not ready for review
91 </DropdownMenuItem>
92 </DropdownMenuContent>
93 </DropdownMenu>
94 </div>
95 </div>
96 )
97}