[DrawScreen] handleConfirm called, state: reviewing isConfirming: false
[useDrawState] confirm called
... (STUCK - tidak ada log lanjutan, tidak pernah selesai)
SEBELUM melakukan apapun, tampilkan FULL CODE dari function-function berikut:
# Jalankan command ini dan tampilkan hasilnya:
# 1. useDrawState hook - function confirm
cat src/hooks/useDrawState.ts
# 2. DrawScreen - handleConfirm
cat src/pages/DrawScreen.tsx
# 3. winnerRepository - confirmByPrizeId
cat src/repositories/winnerRepository.ts
# 4. couponRepository - void functions
cat src/repositories/couponRepository.ts
# 5. drawService - jika ada confirm related
cat src/services/drawService.tsTUNGGU USER APPROVAL sebelum melanjutkan.
Setelah melihat code, tambahkan console.log di SETIAP LINE dalam function confirm:
// useDrawState.ts - confirm function
const confirm = async () => {
console.log('[CONFIRM] Step 0: Function entered')
// Log semua variables
console.log('[CONFIRM] state:', state)
console.log('[CONFIRM] prizeId:', prizeId)
console.log('[CONFIRM] eventId:', eventId)
console.log('[CONFIRM] currentBatchWinners:', currentBatchWinners)
// SETIAP kondisi if harus ada log
if (someCondition) {
console.log('[CONFIRM] BLOCKED BY: someCondition')
return
}
console.log('[CONFIRM] Step 1: Passed all guards')
// SETIAP await harus ada log sebelum dan sesudah
console.log('[CONFIRM] Step 2: About to call winnerRepository.confirmByPrizeId')
try {
await winnerRepository.confirmByPrizeId(prizeId)
console.log('[CONFIRM] Step 2: DONE - confirmByPrizeId')
} catch (error) {
console.error('[CONFIRM] Step 2: ERROR -', error)
throw error
}
console.log('[CONFIRM] Step 3: About to call voidCoupons')
try {
await voidCoupons()
console.log('[CONFIRM] Step 3: DONE - voidCoupons')
} catch (error) {
console.error('[CONFIRM] Step 3: ERROR -', error)
throw error
}
// ... dst untuk setiap operation
console.log('[CONFIRM] Step FINAL: All done')
}// Cari pattern seperti ini:
while (condition) {
// Jika condition tidak pernah false = infinite loop
}
// Atau forEach dengan await (tidak akan wait):
items.forEach(async (item) => {
await something(item) // TIDAK AKAN WAIT!
})
// HARUS pakai for...of:
for (const item of items) {
await something(item) // Ini baru wait
}// Promise tanpa resolve
new Promise((resolve, reject) => {
// Lupa panggil resolve()!
})
// Atau await pada function yang bukan async
const syncFunction = () => { return 'value' }
await syncFunction() // Ini akan instant, tapi kalau function throw...// BUGGY - error di-catch tapi tidak di-handle
try {
await something()
} catch (e) {
// Silent! Tidak ada log, tidak re-throw
}
// CORRECT
try {
await something()
} catch (e) {
console.error('ERROR:', e)
throw e // Re-throw agar caller tahu
}// BUGGY - return tanpa log
if (state.status !== 'reviewing') return
// CORRECT
if (state.status !== 'reviewing') {
console.warn('[CONFIRM] EARLY RETURN: status is', state.status)
return
}Jika masih tidak ketemu, SIMPLIFY function untuk test:
const confirm = async () => {
console.log('[CONFIRM] START - simplified version')
// Hapus semua logic dulu, cuma test basic flow
await new Promise(resolve => setTimeout(resolve, 1000))
console.log('[CONFIRM] END - simplified version')
// Dispatch success
dispatch({ type: 'CONFIRM_SUCCESS' })
}Jika ini WORKS, berarti masalah ada di salah satu operation yang dihapus. Tambahkan kembali satu per satu sampai ketemu yang stuck.
async confirmByPrizeId(prizeId: string): Promise<void> {
console.log('[WinnerRepo] confirmByPrizeId START, prizeId:', prizeId)
// Check if there are winners to confirm
const winners = await db.winners
.where('prizeId')
.equals(prizeId)
.filter(w => w.status === 'valid' && !w.confirmedAt)
.toArray()
console.log('[WinnerRepo] Winners to confirm:', winners.length)
if (winners.length === 0) {
console.log('[WinnerRepo] No winners to confirm, returning')
return
}
// Modify
console.log('[WinnerRepo] About to modify...')
const modified = await db.winners
.where('prizeId')
.equals(prizeId)
.filter(w => w.status === 'valid' && !w.confirmedAt)
.modify({ confirmedAt: new Date().toISOString() })
console.log('[WinnerRepo] Modified count:', modified)
console.log('[WinnerRepo] confirmByPrizeId END')
}async voidByWinnerCouponIds(couponIds: string[]): Promise<void> {
console.log('[CouponRepo] voidByWinnerCouponIds START, count:', couponIds.length)
if (couponIds.length === 0) {
console.log('[CouponRepo] No coupons to void, returning')
return
}
console.log('[CouponRepo] About to modify...')
const modified = await db.coupons
.where('id')
.anyOf(couponIds)
.modify({ status: 'void' })
console.log('[CouponRepo] Modified count:', modified)
console.log('[CouponRepo] voidByWinnerCouponIds END')
}// BUGGY - filter + modify tidak work seperti expected di Dexie
await db.winners
.where('prizeId')
.equals(prizeId)
.filter(w => w.status === 'valid') // Filter di JS, bukan query
.modify({ confirmedAt: now }) // Modify semua hasil where, BUKAN hasil filter!
// CORRECT - pakai toArray dulu, lalu update satu-satu
const winners = await db.winners
.where('prizeId')
.equals(prizeId)
.filter(w => w.status === 'valid' && !w.confirmedAt)
.toArray()
for (const winner of winners) {
await db.winners.update(winner.id, { confirmedAt: now })
}
// ATAU gunakan bulkPut
const updated = winners.map(w => ({ ...w, confirmedAt: now }))
await db.winners.bulkPut(updated)// Jika participant punya 1000+ kupon, ini bisa timeout
async voidByParticipantId(eventId: string, participantId: string) {
// Single query untuk void ribuan coupons
await db.coupons
.where(['eventId', 'participantId'])
.equals([eventId, participantId])
.modify({ status: 'void' })
}
// SOLUTION: Batch jika terlalu banyak
async voidByParticipantId(eventId: string, participantId: string) {
const coupons = await db.coupons
.where('participantId')
.equals(participantId)
.filter(c => c.eventId === eventId && c.status === 'active')
.toArray()
console.log('[CouponRepo] Coupons to void:', coupons.length)
// Batch update
const BATCH_SIZE = 100
for (let i = 0; i < coupons.length; i += BATCH_SIZE) {
const batch = coupons.slice(i, i + BATCH_SIZE)
await db.coupons.bulkPut(
batch.map(c => ({ ...c, status: 'void' }))
)
console.log('[CouponRepo] Voided batch', i / BATCH_SIZE + 1)
}
}// BUGGY - nested transactions bisa deadlock
await db.transaction('rw', db.winners, async () => {
await db.transaction('rw', db.coupons, async () => { // NESTED = DEADLOCK!
await db.coupons.modify(...)
})
})
// CORRECT - single transaction with multiple tables
await db.transaction('rw', [db.winners, db.coupons], async () => {
await db.winners.modify(...)
await db.coupons.modify(...)
})Jika tidak punya waktu untuk debug penuh, coba quick fix ini:
// useDrawState.ts - confirm function
const confirm = async () => {
console.log('[CONFIRM] Starting...')
try {
// Get valid winners
const validWinners = currentBatchWinners.filter(w => w.status === 'valid')
console.log('[CONFIRM] Valid winners:', validWinners.length)
if (validWinners.length === 0) {
console.log('[CONFIRM] No valid winners, skipping')
dispatch({ type: 'CONFIRM_SUCCESS' })
return
}
// Confirm winners one by one (slower but more reliable)
const now = new Date().toISOString()
for (const winner of validWinners) {
console.log('[CONFIRM] Confirming winner:', winner.id)
await db.winners.update(winner.id, { confirmedAt: now })
}
console.log('[CONFIRM] All winners confirmed')
// Void coupons one by one
for (const winner of validWinners) {
console.log('[CONFIRM] Voiding coupon:', winner.couponId)
await db.coupons.update(winner.couponId, { status: 'void' })
}
console.log('[CONFIRM] All coupons voided')
// Dispatch success
console.log('[CONFIRM] Dispatching success')
dispatch({ type: 'CONFIRM_SUCCESS' })
console.log('[CONFIRM] DONE!')
} catch (error) {
console.error('[CONFIRM] ERROR:', error)
// Reset confirming state
setIsConfirming(false)
throw error
}
}| File | What to Look For |
|---|---|
src/hooks/useDrawState.ts |
confirm function - setiap line |
src/repositories/winnerRepository.ts |
confirmByPrizeId - database operations |
src/repositories/couponRepository.ts |
void, voidByParticipantId - database operations |
src/services/drawService.ts |
Any confirm-related logic |
src/db/index.ts |
Database schema, indexes |
Setelah fix, test dengan:
- Draw beberapa winners
- Klik Confirm
- Console harus show semua steps sampai "DONE!"
- Button harus kembali ke state normal
- Winners harus ter-confirm di database (check IndexedDB)