22
33import { formatAmount , parseAmount } from "@stellar-split/sdk" ;
44import type { Invoice } from "@stellar-split/sdk" ;
5+ import { useState } from "react" ;
56
67interface QueueEntry {
78 invoice : Invoice ;
@@ -12,14 +13,40 @@ interface Props {
1213 queue : QueueEntry [ ] ;
1314 onAmountChange : ( invoiceId : string , value : string ) => void ;
1415 onRemove : ( invoiceId : string ) => void ;
16+ onReorder ?: ( newQueue : QueueEntry [ ] ) => void ;
1517}
1618
17- export default function BatchPayQueue ( { queue, onAmountChange, onRemove } : Props ) {
19+ export default function BatchPayQueue ( { queue, onAmountChange, onRemove, onReorder } : Props ) {
20+ const [ draggedIndex , setDraggedIndex ] = useState < number | null > ( null ) ;
21+
1822 const runningTotal = queue . reduce ( ( sum , entry ) => {
1923 const parsed = parseFloat ( entry . amount ) ;
2024 return sum + ( isNaN ( parsed ) ? 0 : parsed ) ;
2125 } , 0 ) ;
2226
27+ const handleDrop = ( targetIndex : number ) => {
28+ if ( draggedIndex === null || draggedIndex === targetIndex ) return ;
29+ const newQueue = [ ...queue ] ;
30+ const [ dragged ] = newQueue . splice ( draggedIndex , 1 ) ;
31+ newQueue . splice ( targetIndex , 0 , dragged ) ;
32+ onReorder ?.( newQueue ) ;
33+ setDraggedIndex ( null ) ;
34+ } ;
35+
36+ const handleKeyDown = ( index : number , e : React . KeyboardEvent ) => {
37+ if ( e . altKey && e . key === "ArrowUp" && index > 0 ) {
38+ e . preventDefault ( ) ;
39+ const newQueue = [ ...queue ] ;
40+ [ newQueue [ index - 1 ] , newQueue [ index ] ] = [ newQueue [ index ] , newQueue [ index - 1 ] ] ;
41+ onReorder ?.( newQueue ) ;
42+ } else if ( e . altKey && e . key === "ArrowDown" && index < queue . length - 1 ) {
43+ e . preventDefault ( ) ;
44+ const newQueue = [ ...queue ] ;
45+ [ newQueue [ index ] , newQueue [ index + 1 ] ] = [ newQueue [ index + 1 ] , newQueue [ index ] ] ;
46+ onReorder ?.( newQueue ) ;
47+ }
48+ } ;
49+
2350 if ( queue . length === 0 ) {
2451 return (
2552 < p className = "text-gray-500 text-sm py-4 text-center" >
@@ -30,13 +57,23 @@ export default function BatchPayQueue({ queue, onAmountChange, onRemove }: Props
3057
3158 return (
3259 < div className = "flex flex-col gap-3" >
33- { queue . map ( ( { invoice, amount } ) => {
60+ { queue . map ( ( { invoice, amount } , index ) => {
3461 const total = invoice . recipients . reduce ( ( s , r ) => s + r . amount , 0n ) ;
3562 return (
3663 < div
3764 key = { invoice . id }
38- className = "bg-gray-900 rounded-xl px-4 py-3 flex flex-col sm:flex-row sm:items-center gap-3"
65+ draggable
66+ onDragStart = { ( ) => setDraggedIndex ( index ) }
67+ onDragOver = { ( e ) => e . preventDefault ( ) }
68+ onDrop = { ( ) => handleDrop ( index ) }
69+ onDragEnd = { ( ) => setDraggedIndex ( null ) }
70+ onKeyDown = { ( e ) => handleKeyDown ( index , e ) }
71+ tabIndex = { 0 }
72+ className = { `bg-gray-900 rounded-xl px-4 py-3 flex flex-col sm:flex-row sm:items-center gap-3 cursor-grab ${
73+ draggedIndex === index ? "opacity-50" : ""
74+ } `}
3975 >
76+ < span className = "text-gray-500 text-xs select-none" > ⋮⋮</ span >
4077 < div className = "flex-1 min-w-0" >
4178 < p className = "text-sm font-semibold truncate" > Invoice #{ invoice . id } </ p >
4279 < p className = "text-xs text-gray-500 mt-0.5" >
0 commit comments