11import {
22 ArrowPathIcon ,
3+ BookOpenIcon ,
34 CheckIcon ,
5+ ClockIcon ,
46 CloudArrowDownIcon ,
57 EnvelopeIcon ,
68 QueueListIcon ,
@@ -159,7 +161,8 @@ export function SpanView({
159161 return null ;
160162 }
161163
162- if ( fetcher . state !== "idle" || fetcher . data === undefined ) {
164+ // Only show loading spinner when there's no data yet, not during revalidation
165+ if ( fetcher . data === undefined ) {
163166 return (
164167 < div
165168 className = { cn (
@@ -308,32 +311,6 @@ function RunBody({
308311 const tab = value ( "tab" ) ;
309312 const resetFetcher = useTypedFetcher < typeof resetIdempotencyKeyAction > ( ) ;
310313
311- // Handle toast messages from the reset action
312- useEffect ( ( ) => {
313- if ( resetFetcher . data && resetFetcher . state === "idle" ) {
314- // Check if the response indicates success
315- if (
316- resetFetcher . data &&
317- typeof resetFetcher . data === "object" &&
318- "success" in resetFetcher . data &&
319- resetFetcher . data . success === true
320- ) {
321- toast . custom (
322- ( t ) => (
323- < ToastUI
324- variant = "success"
325- message = "Idempotency key reset successfully"
326- t = { t as string }
327- />
328- ) ,
329- {
330- duration : 5000 ,
331- }
332- ) ;
333- }
334- }
335- } , [ resetFetcher . data , resetFetcher . state ] ) ;
336-
337314 return (
338315 < div className = "grid h-full max-h-full grid-rows-[2.5rem_2rem_1fr_3.25rem] overflow-hidden bg-background-bright" >
339316 < div className = "flex items-center justify-between gap-2 overflow-x-hidden px-3 pr-2" >
@@ -443,6 +420,12 @@ function RunBody({
443420 />
444421 </ Property . Value >
445422 </ Property . Item >
423+ < Property . Item >
424+ < Property . Label > Run ID</ Property . Label >
425+ < Property . Value >
426+ < CopyableText value = { run . friendlyId } copyValue = { run . friendlyId } asChild />
427+ </ Property . Value >
428+ </ Property . Item >
446429 { run . relationships . root ? (
447430 run . relationships . root . isParent ? (
448431 < Property . Item >
@@ -581,38 +564,45 @@ function RunBody({
581564 </ Property . Item >
582565 ) }
583566 < Property . Item >
584- < Property . Label > Idempotency</ Property . Label >
585- < Property . Value >
586- < div className = "flex items-start justify-between gap-2" >
587- < div className = "flex-1" >
588- { run . idempotencyKey ? (
589- < CopyableText
590- value = { run . idempotencyKey }
591- copyValue = { run . idempotencyKey }
592- asChild
593- />
594- ) : (
595- < div className = "break-all" > –</ div >
596- ) }
597- { run . idempotencyKey && (
598- < div >
599- Expires:{ " " }
600- { run . idempotencyKeyExpiresAt ? (
601- < DateTime date = { run . idempotencyKeyExpiresAt } />
602- ) : (
603- "–"
604- ) }
605- </ div >
606- ) }
607- </ div >
608- { run . idempotencyKey && (
567+ < Property . Label >
568+ < div className = "flex items-center justify-between" >
569+ < span className = "flex items-center gap-1" >
570+ Idempotency
571+ < InfoIconTooltip
572+ content = {
573+ < div className = "flex max-w-xs flex-col gap-2 p-1" >
574+ < Paragraph variant = "small" >
575+ Idempotency keys prevent duplicate task runs. If you trigger a task
576+ with the same key twice, the second request returns the original run.
577+ </ Paragraph >
578+ < Paragraph variant = "small" >
579+ < strong > Scope:</ strong > < strong > global</ strong > applies across all
580+ runs, < strong > run</ strong > is unique to a parent run, and{ " " }
581+ < strong > attempt</ strong > is unique to a specific attempt.
582+ </ Paragraph >
583+ < Paragraph variant = "small" >
584+ < strong > Status:</ strong > < strong > Active</ strong > means duplicates are
585+ blocked, < strong > Expired</ strong > means the TTL has passed, and{ " " }
586+ < strong > Inactive</ strong > means the key was reset or cleared.
587+ </ Paragraph >
588+ < LinkButton
589+ to = { docsPath ( "idempotency" ) }
590+ variant = "docs/small"
591+ LeadingIcon = { BookOpenIcon }
592+ >
593+ Read docs
594+ </ LinkButton >
595+ </ div >
596+ }
597+ />
598+ </ span >
599+ { run . idempotencyKeyStatus === "active" ? (
609600 < resetFetcher . Form
610601 method = "post"
611602 action = { v3RunIdempotencyKeyResetPath ( organization , project , environment , {
612- friendlyId : runParam ,
603+ friendlyId : run . friendlyId ,
613604 } ) }
614605 >
615- < input type = "hidden" name = "taskIdentifier" value = { run . taskIdentifier } />
616606 < Button
617607 type = "submit"
618608 variant = "minimal/small"
@@ -622,8 +612,49 @@ function RunBody({
622612 { resetFetcher . state === "submitting" ? "Resetting..." : "Reset" }
623613 </ Button >
624614 </ resetFetcher . Form >
625- ) }
615+ ) : run . idempotencyKeyStatus === "expired" ? (
616+ < span className = "flex items-center gap-1 text-xs text-amber-500" >
617+ < ClockIcon className = "size-4" />
618+ Expired
619+ </ span >
620+ ) : run . idempotencyKeyStatus === "inactive" ? (
621+ < span className = "text-xs text-text-dimmed" > Inactive</ span >
622+ ) : null }
626623 </ div >
624+ </ Property . Label >
625+ < Property . Value >
626+ { run . idempotencyKeyStatus ? (
627+ < >
628+ < div >
629+ < span className = "text-text-dimmed" > Key: </ span >
630+ { run . idempotencyKey ? (
631+ < CopyableText
632+ value = { run . idempotencyKey }
633+ copyValue = { run . idempotencyKey }
634+ asChild
635+ />
636+ ) : (
637+ "–"
638+ ) }
639+ </ div >
640+ < div >
641+ < span className = "text-text-dimmed" > Scope: </ span >
642+ { run . idempotencyKeyScope ?? "–" }
643+ </ div >
644+ < div >
645+ < span className = "text-text-dimmed" >
646+ { run . idempotencyKeyStatus === "expired" ? "Expired: " : "Expires: " }
647+ </ span >
648+ { run . idempotencyKeyExpiresAt ? (
649+ < DateTime date = { run . idempotencyKeyExpiresAt } />
650+ ) : (
651+ "–"
652+ ) }
653+ </ div >
654+ </ >
655+ ) : (
656+ "–"
657+ ) }
627658 </ Property . Value >
628659 </ Property . Item >
629660 < Property . Item >
@@ -859,18 +890,6 @@ function RunBody({
859890 : "–" }
860891 </ Property . Value >
861892 </ Property . Item >
862- < Property . Item >
863- < Property . Label > Run ID</ Property . Label >
864- < Property . Value >
865- < CopyableText value = { run . friendlyId } copyValue = { run . friendlyId } asChild />
866- </ Property . Value >
867- </ Property . Item >
868- < Property . Item >
869- < Property . Label > Internal ID</ Property . Label >
870- < Property . Value >
871- < CopyableText value = { run . id } copyValue = { run . id } asChild />
872- </ Property . Value >
873- </ Property . Item >
874893 < Property . Item >
875894 < Property . Label > Run Engine</ Property . Label >
876895 < Property . Value > { run . engine } </ Property . Value >
0 commit comments