@@ -23,7 +23,6 @@ import {
2323 useEffect ,
2424 useMemo ,
2525 useRef ,
26- useState ,
2726} from 'react'
2827import { PROTECTED_URLS } from '@/configs/urls'
2928import type { TemplateTagAssignment } from '@/core/modules/templates/models'
@@ -47,7 +46,6 @@ import { TAGS_PAGE_LIMIT } from './constants'
4746import TagsEmpty from './empty'
4847import TagsHeader from './header'
4948import { TagHistoryRow } from './history/tag-history-row'
50- import RollbackTagDialog from './rollback-dialog'
5149import { useTagTableStore } from './stores/table-store'
5250import {
5351 fallbackData ,
@@ -58,6 +56,7 @@ import {
5856 trackTagTableInteraction ,
5957 useTagColumns ,
6058} from './table-config'
59+ import { TagDialogProvider , useTagDialog } from './tag-dialog-provider'
6160import type { TagGroup } from './types'
6261
6362const ESTIMATED_ROW_HEIGHT_PX = 44
@@ -219,152 +218,145 @@ export default function TagsTable({ teamSlug, templateId }: TagsTableProps) {
219218 } , [ fetchNextPage , activeSearch , serverSort ] )
220219
221220 return (
222- < div className = "flex flex-col gap-6 h-full min-h-0" >
223- < TagsHeader
224- teamSlug = { teamSlug }
225- templateId = { templateId }
226- templateName = { templateName }
227- total = { countData ?. total }
228- hasSearch = { activeSearch !== undefined }
229- searchInvalid = { searchInvalid }
230- />
231- < div
232- ref = { scrollRef }
233- className = "flex-1 min-h-0 overflow-y-auto overflow-x-hidden -mx-8 px-8 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
234- >
235- < DataTable className = "w-full" >
236- < DataTableHeader className = "sticky top-0 z-30 bg-bg border-b-0 mb-px" >
237- { table . getHeaderGroups ( ) . map ( ( headerGroup ) => (
238- < DataTableRow
239- key = { headerGroup . id }
240- className = "border-b-0 flex items-center gap-6 -mx-8 px-8 w-[calc(100%+64px)]"
241- >
242- { headerGroup . headers . map ( ( header ) => (
243- < DataTableHead
244- key = { header . id }
245- header = { header }
246- sorting = { sorting . find ( ( s ) => s . id === header . id ) ?. desc }
247- align = { header . id === 'actions' ? 'right' : 'left' }
248- className = { cn (
249- 'h-auto px-0' ,
250- header . id === 'tag' && 'flex-1 min-w-0' ,
251- header . id === 'assignedAt' && 'w-[178px] shrink-0' ,
252- header . id === 'actions' &&
253- 'w-[203px] max-sm:w-4 shrink-0 justify-end'
254- ) }
255- style = {
256- header . id === 'tag' ? undefined : { width : undefined }
257- }
258- >
259- < span >
260- { header . isPlaceholder
261- ? null
262- : flexRender (
263- header . column . columnDef . header ,
264- header . getContext ( )
265- ) }
266- </ span >
267- </ DataTableHead >
268- ) ) }
269- </ DataTableRow >
270- ) ) }
271- </ DataTableHeader >
272-
273- { showLoader && (
274- < div className = "h-[35svh] w-full flex items-center justify-center" >
275- < Loader variant = "slash" size = "lg" />
276- </ div >
277- ) }
221+ < TagDialogProvider
222+ teamSlug = { teamSlug }
223+ templateId = { templateId }
224+ templateName = { templateName }
225+ >
226+ < div className = "flex flex-col gap-6 h-full min-h-0" >
227+ < TagsHeader
228+ teamSlug = { teamSlug }
229+ templateId = { templateId }
230+ templateName = { templateName }
231+ total = { countData ?. total }
232+ hasSearch = { activeSearch !== undefined }
233+ searchInvalid = { searchInvalid }
234+ />
235+ < div
236+ ref = { scrollRef }
237+ className = "flex-1 min-h-0 overflow-y-auto overflow-x-hidden -mx-8 px-8 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
238+ >
239+ < DataTable className = "w-full" >
240+ < DataTableHeader className = "sticky top-0 z-30 bg-bg border-b-0 mb-px" >
241+ { table . getHeaderGroups ( ) . map ( ( headerGroup ) => (
242+ < DataTableRow
243+ key = { headerGroup . id }
244+ className = "border-b-0 flex items-center gap-6 -mx-8 px-8 w-[calc(100%+64px)]"
245+ >
246+ { headerGroup . headers . map ( ( header ) => (
247+ < DataTableHead
248+ key = { header . id }
249+ header = { header }
250+ sorting = { sorting . find ( ( s ) => s . id === header . id ) ?. desc }
251+ align = { header . id === 'actions' ? 'right' : 'left' }
252+ className = { cn (
253+ 'h-auto px-0' ,
254+ header . id === 'tag' && 'flex-1 min-w-0' ,
255+ header . id === 'assignedAt' && 'w-[178px] shrink-0' ,
256+ header . id === 'actions' &&
257+ 'w-[203px] max-sm:w-4 shrink-0 justify-end'
258+ ) }
259+ style = {
260+ header . id === 'tag' ? undefined : { width : undefined }
261+ }
262+ >
263+ < span >
264+ { header . isPlaceholder
265+ ? null
266+ : flexRender (
267+ header . column . columnDef . header ,
268+ header . getContext ( )
269+ ) }
270+ </ span >
271+ </ DataTableHead >
272+ ) ) }
273+ </ DataTableRow >
274+ ) ) }
275+ </ DataTableHeader >
276+
277+ { showLoader && (
278+ < div className = "h-[35svh] w-full flex items-center justify-center" >
279+ < Loader variant = "slash" size = "lg" />
280+ </ div >
281+ ) }
278282
279- { showEmpty && < TagsEmpty hasSearch = { activeSearch !== undefined } /> }
283+ { showEmpty && < TagsEmpty hasSearch = { activeSearch !== undefined } /> }
280284
281- { hasData && (
282- < div
283- className = { cn (
284- 'relative' ,
285- showFilterRefetchingOverlay && 'opacity-70 transition-opacity'
286- ) }
287- style = { { height : `${ virtualizer . getTotalSize ( ) } px` } }
288- >
289- { virtualItems . map ( ( virtualRow ) => {
290- const row = rows [ virtualRow . index ]
291- if ( ! row ) return null
292- return (
293- < div
294- key = { virtualRow . key }
295- data-index = { virtualRow . index }
296- ref = { ( node ) => virtualizer . measureElement ( node ) }
297- className = { cn (
298- 'absolute left-0 right-0' ,
299- 'hover:z-20 focus-within:z-10' ,
300- 'border-b border-stroke/80' ,
301- 'has-[button[aria-haspopup=menu][data-state=open]]:z-10'
302- ) }
303- style = { {
304- transform : `translateY(${
305- virtualRow . start - HEADER_SCROLL_MARGIN_PX
306- } px)`,
307- } }
308- >
309- < GroupSection
310- row = { row }
311- teamSlug = { teamSlug }
312- templateId = { templateId }
313- templateName = { templateName }
314- />
315- </ div >
316- )
317- } ) }
318- </ div >
319- ) }
285+ { hasData && (
286+ < div
287+ className = { cn (
288+ 'relative' ,
289+ showFilterRefetchingOverlay && 'opacity-70 transition-opacity'
290+ ) }
291+ style = { { height : `${ virtualizer . getTotalSize ( ) } px` } }
292+ >
293+ { virtualItems . map ( ( virtualRow ) => {
294+ const row = rows [ virtualRow . index ]
295+ if ( ! row ) return null
296+ return (
297+ < div
298+ key = { virtualRow . key }
299+ data-index = { virtualRow . index }
300+ ref = { ( node ) => virtualizer . measureElement ( node ) }
301+ className = { cn (
302+ 'absolute left-0 right-0' ,
303+ 'hover:z-20 focus-within:z-10' ,
304+ 'border-b border-stroke/80' ,
305+ 'has-[button[aria-haspopup=menu][data-state=open]]:z-10'
306+ ) }
307+ style = { {
308+ transform : `translateY(${
309+ virtualRow . start - HEADER_SCROLL_MARGIN_PX
310+ } px)`,
311+ } }
312+ >
313+ < GroupSection
314+ row = { row }
315+ teamSlug = { teamSlug }
316+ templateId = { templateId }
317+ />
318+ </ div >
319+ )
320+ } ) }
321+ </ div >
322+ ) }
320323
321- { hasNextPage && (
322- < div className = "flex w-full items-center justify-center py-3" >
323- < LoadMoreButton
324- isLoading = { isFetchingNextPage }
325- onLoadMore = { handleLoadMore }
326- />
327- </ div >
328- ) }
329- </ DataTable >
324+ { hasNextPage && (
325+ < div className = "flex w-full items-center justify-center py-3" >
326+ < LoadMoreButton
327+ isLoading = { isFetchingNextPage }
328+ onLoadMore = { handleLoadMore }
329+ />
330+ </ div >
331+ ) }
332+ </ DataTable >
333+ </ div >
330334 </ div >
331- </ div >
335+ </ TagDialogProvider >
332336 )
333337}
334338
335339interface GroupSectionProps {
336340 row : Row < TagGroup >
337341 teamSlug : string
338342 templateId : string
339- templateName : string
340343}
341344
342- function GroupSection ( {
343- row,
344- teamSlug,
345- templateId,
346- templateName,
347- } : GroupSectionProps ) {
345+ function GroupSection ( { row, teamSlug, templateId } : GroupSectionProps ) {
348346 'use no memo'
349347
350348 const canExpand = row . getCanExpand ( )
351349 const isExpanded = row . getIsExpanded ( )
352350 const dataState = isExpanded ? 'open' : 'closed'
353351 const shouldReduceMotion = useReducedMotion ( )
354352
355- const [ rollbackRequest , setRollbackRequest ] = useState < {
356- target : TemplateTagAssignment
357- currentBuildId : string
358- } | null > ( null )
353+ const { actions } = useTagDialog ( )
359354
360355 const handleRequestRowRollback = useCallback (
361356 ( target : TemplateTagAssignment ) => {
362- setRollbackRequest ( {
363- target,
364- currentBuildId : row . original . primaryAssignment . buildId ,
365- } )
357+ actions . openRollback ( row . original , target , 'history-row' )
366358 } ,
367- [ row . original . primaryAssignment . buildId ]
359+ [ actions , row . original ]
368360 )
369361
370362 const toggle = ( ) => {
@@ -496,20 +488,6 @@ function GroupSection({
496488 </ motion . div >
497489 ) }
498490 </ AnimatePresence >
499-
500- < RollbackTagDialog
501- open = { rollbackRequest !== null }
502- onOpenChange = { ( next ) => {
503- if ( ! next ) setRollbackRequest ( null )
504- } }
505- tag = { row . original . tag }
506- currentBuildId = { rollbackRequest ?. currentBuildId ?? '' }
507- targetBuildId = { rollbackRequest ?. target . buildId ?? '' }
508- teamSlug = { teamSlug }
509- templateId = { templateId }
510- templateName = { templateName }
511- surface = "history-row"
512- />
513491 </ div >
514492 )
515493}
0 commit comments