11"use client" ;
22
3+ import { Alert , Button , Card , CardContent , CardHeader , CardTitle , Input , Typography } from "@/components/ui" ;
34import { ClickhouseMigrationRequest , ClickhouseMigrationResponse } from "@stackframe/stack-shared/dist/interface/admin-interface" ;
4- import { Button , Card , CardContent , CardHeader , CardTitle , Input , Typography , Alert } from "@/components/ui " ;
5+ import { notFound } from "next/navigation " ;
56import React from "react" ;
67import { PageLayout } from "../page-layout" ;
78import { useAdminApp } from "../use-admin-app" ;
8- import { notFound } from "next/navigation" ;
99
1010type MigrationCursor = {
1111 createdAtMillis : number ,
1212 id : string ,
1313} ;
1414
1515type MigrationSnapshot = {
16- totalEvents : number ,
17- processedEvents : number ,
18- remainingEvents : number ,
1916 migratedEvents : number ,
20- skippedExistingEvents : number ,
2117 insertedRows : number ,
22- progress : number ,
2318 nextCursor : MigrationCursor | null ,
2419} ;
2520
2621const normalizeResponse = ( response : ClickhouseMigrationResponse ) : MigrationSnapshot => ( {
27- totalEvents : response . total_events ,
28- processedEvents : response . processed_events ,
29- remainingEvents : response . remaining_events ,
3022 migratedEvents : response . migrated_events ,
31- skippedExistingEvents : response . skipped_existing_events ,
3223 insertedRows : response . inserted_rows ,
33- progress : response . progress ,
3424 nextCursor : response . next_cursor ? {
3525 createdAtMillis : response . next_cursor . created_at_millis ,
3626 id : response . next_cursor . id ,
@@ -46,13 +36,16 @@ export default function PageClient() {
4636 const [ minCreatedAt , setMinCreatedAt ] = React . useState ( "" ) ;
4737 const [ maxCreatedAt , setMaxCreatedAt ] = React . useState ( "" ) ;
4838 const [ limit , setLimit ] = React . useState ( 1000 ) ;
49- const [ stats , setStats ] = React . useState < MigrationSnapshot | null > ( null ) ;
5039 const [ cursor , setCursor ] = React . useState < MigrationCursor | null > ( null ) ;
5140 const [ running , setRunning ] = React . useState ( false ) ;
5241 const runningRef = React . useRef ( false ) ;
5342 const cursorRef = React . useRef < MigrationCursor | null > ( null ) ;
5443 const timeWindowRef = React . useRef < { minCreatedAtMillis : number , maxCreatedAtMillis : number } | null > ( null ) ;
5544 const [ error , setError ] = React . useState < string | null > ( null ) ;
45+ const [ totalMigratedEvents , setTotalMigratedEvents ] = React . useState ( 0 ) ;
46+ const [ totalInsertedRows , setTotalInsertedRows ] = React . useState ( 0 ) ;
47+ const [ batchCount , setBatchCount ] = React . useState ( 0 ) ;
48+ const [ done , setDone ] = React . useState ( false ) ;
5649
5750 const parseCreatedAtMillis = React . useCallback ( ( value : string | undefined ) => {
5851 if ( ! value ) return null ;
@@ -87,7 +80,9 @@ export default function PageClient() {
8780 const runBatch = React . useCallback ( async ( ) => {
8881 const response = await adminInterface . migrateEventsToClickhouse ( buildRequestBody ( ) ) ;
8982 const snapshot = normalizeResponse ( response ) ;
90- setStats ( snapshot ) ;
83+ setTotalMigratedEvents ( prev => prev + snapshot . migratedEvents ) ;
84+ setTotalInsertedRows ( prev => prev + snapshot . insertedRows ) ;
85+ setBatchCount ( prev => prev + 1 ) ;
9186 cursorRef . current = snapshot . nextCursor ;
9287 setCursor ( snapshot . nextCursor ) ;
9388 return snapshot ;
@@ -103,8 +98,11 @@ export default function PageClient() {
10398 cursorRef . current = null ;
10499 timeWindowRef . current = null ;
105100 setCursor ( null ) ;
106- setStats ( null ) ;
107101 setError ( null ) ;
102+ setTotalMigratedEvents ( 0 ) ;
103+ setTotalInsertedRows ( 0 ) ;
104+ setBatchCount ( 0 ) ;
105+ setDone ( false ) ;
108106 } , [ stopMigration ] ) ;
109107
110108 const startMigration = React . useCallback ( async ( ) => {
@@ -128,6 +126,7 @@ export default function PageClient() {
128126 while ( runningRef . current ) {
129127 const snapshot = await runBatch ( ) ;
130128 if ( ! snapshot . nextCursor ) {
129+ setDone ( true ) ;
131130 stopMigration ( ) ;
132131 break ;
133132 }
@@ -138,8 +137,6 @@ export default function PageClient() {
138137 }
139138 } , [ maxCreatedAt , minCreatedAt , parseCreatedAtMillis , runBatch , stopMigration ] ) ;
140139
141- const progressPercent = Math . min ( 100 , Math . max ( 0 , Math . round ( ( stats ?. progress ?? 0 ) * 100 ) ) ) ;
142-
143140 if ( stackAdminApp . projectId !== "internal" ) {
144141 return notFound ( ) ;
145142 }
@@ -186,7 +183,7 @@ export default function PageClient() {
186183 < Input
187184 type = "number"
188185 min = { 1 }
189- max = { 1000 }
186+ max = { 100_000 }
190187 value = { limit }
191188 onChange = { ( e ) => {
192189 setLimit ( Number ( e . target . value ) || 0 ) ;
@@ -225,40 +222,22 @@ export default function PageClient() {
225222 < CardTitle > Status</ CardTitle >
226223 </ CardHeader >
227224 < CardContent className = "space-y-3" >
228- < div className = "h-3 w-full rounded-full bg-muted" >
229- < div
230- className = "h-3 rounded-full bg-gradient-to-r from-blue-500 to-emerald-500"
231- style = { { width : `${ progressPercent } %` } }
232- />
233- </ div >
234- < div className = "flex items-center justify-between" >
235- < Typography variant = "secondary" > Progress</ Typography >
236- < Typography type = "label" > { progressPercent } %</ Typography >
237- </ div >
238225 < div className = "grid grid-cols-2 gap-3 text-sm" >
239226 < div >
240- < Typography variant = "secondary" > Processed</ Typography >
241- < Typography type = "label" > { stats ?. processedEvents ?? 0 } </ Typography >
242- </ div >
243- < div >
244- < Typography variant = "secondary" > Remaining</ Typography >
245- < Typography type = "label" > { stats ?. remainingEvents ?? 0 } </ Typography >
227+ < Typography variant = "secondary" > Events migrated</ Typography >
228+ < Typography type = "label" > { totalMigratedEvents . toLocaleString ( ) } </ Typography >
246229 </ div >
247230 < div >
248- < Typography variant = "secondary" > Migrated this run </ Typography >
249- < Typography type = "label" > { stats ?. migratedEvents ?? 0 } </ Typography >
231+ < Typography variant = "secondary" > Rows inserted </ Typography >
232+ < Typography type = "label" > { totalInsertedRows . toLocaleString ( ) } </ Typography >
250233 </ div >
251234 < div >
252- < Typography variant = "secondary" > Inserted rows </ Typography >
253- < Typography type = "label" > { stats ?. insertedRows ?? 0 } </ Typography >
235+ < Typography variant = "secondary" > Batches completed </ Typography >
236+ < Typography type = "label" > { batchCount . toLocaleString ( ) } </ Typography >
254237 </ div >
255238 < div >
256- < Typography variant = "secondary" > Total in scope</ Typography >
257- < Typography type = "label" > { stats ?. totalEvents ?? 0 } </ Typography >
258- </ div >
259- < div className = "" >
260239 < Typography variant = "secondary" > State</ Typography >
261- < Typography type = "label" > { running ? "Running" : "Idle" } </ Typography >
240+ < Typography type = "label" > { done ? "Done" : running ? "Running" : "Idle" } </ Typography >
262241 </ div >
263242 </ div >
264243 </ CardContent >
0 commit comments