Bug Fix: Lucky Spin Countdown Timer — Update Label & Format to dd:hh:mm:ss
Last Updated: 15/07/2026
Type: UI Fix — Countdown Timer Format
Screen: Lucky Spin Result Dialog (LuckySpinResultScreen)
Severity: 🟠 Medium — Visual bug, impacts clarity of voucher expiration
Feedback Screenshot:
1. Current Issues
In the prize modal/result screen, the voucher expiration countdown is displaying incorrectly:
Kết thúc sau 719 : 59 : 52
^^^ ^^ ^^
(min?) (sec?) (?)
Two distinct issues:
- Wrong Label:
"Kết thúc sau"(Ends in) → change to"Hết hạn sau"(Expires in). - Incorrect Formatting: Displaying raw minutes (
719) instead of calculatingdays : hours : minutes : seconds.- 719 minutes = 11 hours 59 minutes (less than 24 hours, so the day column should not show).
- Example: If a voucher expires in 1 day 11 hours 59 minutes, it should render:
01 : 11 : 59 : 52
2. Formatting Rules
Mathematical Conversion
remainingSeconds (totalSeconds) = expiryTime - currentTime
dd = Math.floor(totalSeconds / 86400) // 86400 seconds in a day
hh = Math.floor((totalSeconds % 86400) / 3600) // remaining hours
mm = Math.floor((totalSeconds % 3600) / 60) // remaining minutes
ss = totalSeconds % 60 // remaining seconds
Visual Specifications
| Condition | Visual Format | Example |
|---|---|---|
| Remaining time ≥ 24h | dd : hh : mm : ss | 01 : 11 : 59 : 52 |
| Remaining time < 24h | hh : mm : ss | 11 : 59 : 52 |
| Remaining time < 1h | mm : ss | 59 : 52 |
| Expired | Hide timer, show "Voucher đã hết hạn" | — |
Note: For the specific case in the screenshot (719 minutes = 11h 59m), the format should automatically resolve to
11 : 59 : 52(no day column).
3. Code Implementation
3.1 Label Change
// ❌ Before
<Text>Kết thúc sau</Text>
// ✅ After
<Text>Hết hạn sau</Text>
3.2 Time Breakdown Utility
/**
* Calculates remaining time chunks
* Returns { dd, hh, mm, ss, totalSeconds }
*/
const getCountdownParts = (expiredAt: string | Date) => {
const now = new Date().getTime();
const end = new Date(expiredAt).getTime();
const totalSeconds = Math.max(0, Math.floor((end - now) / 1000));
const dd = Math.floor(totalSeconds / 86400);
const hh = Math.floor((totalSeconds % 86400) / 3600);
const mm = Math.floor((totalSeconds % 3600) / 60);
const ss = totalSeconds % 60;
return { dd, hh, mm, ss, totalSeconds };
};
const pad = (n: number) => String(n).padStart(2, '0');
3.3 React Native Timer Component
const SpinPrizeCountdown = ({ expiredAt }: { expiredAt: string }) => {
const [parts, setParts] = useState(getCountdownParts(expiredAt));
useEffect(() => {
const timer = setInterval(() => {
setParts(getCountdownParts(expiredAt));
}, 1000);
return () => clearInterval(timer);
}, [expiredAt]);
const { dd, hh, mm, ss, totalSeconds } = parts;
if (totalSeconds <= 0) {
return <Text style={styles.expired}>Voucher đã hết hạn</Text>;
}
return (
<View style={styles.countdownRow}>
<Text style={styles.label}>Hết hạn sau</Text> {/* ← LABEL CHANGED */}
<View style={styles.timerBoxes}>
{/* Render Day block only if remaining time is 24h or more */}
{dd > 0 && (
<>
<TimerBox value={pad(dd)} />
<Text style={styles.separator}>:</Text>
</>
)}
{/* Render Hour block if days or hours exist */}
{(dd > 0 || hh > 0) && (
<>
<TimerBox value={pad(hh)} />
<Text style={styles.separator}>:</Text>
</>
)}
<TimerBox value={pad(mm)} />
<Text style={styles.separator}>:</Text>
<TimerBox value={pad(ss)} />
</View>
</View>
);
};
const TimerBox = ({ value }: { value: string }) => (
<View style={styles.timerBox}>
<Text style={styles.timerValue}>{value}</Text>
</View>
);
4. Visual Examples
Scenario A (719 minutes remaining):
719 minutes = 11h 59m (Under 24h)
Hết hạn sau 11 : 59 : 52
hh mm ss
Scenario B (2 days, 3 hours remaining):
Hết hạn sau 02 : 03 : 15 : 00
dd hh mm ss
Scenario C (45 minutes remaining):
Hết hạn sau 45 : 00
mm ss
Technical Checklist
- Locate the countdown view in the spin result modal/screen (
LuckySpinResultScreenorSpinPrizeModal). - Rename
"Kết thúc sau"to"Hết hạn sau". - Implement the
getCountdownPartslogic to calculate dynamic units instead of raw minutes. - Integrate a 1-second interval timer using
setIntervaland handle cleanup. - Hide day
ddblock ifdd === 0. - Display an expired state text when
totalSeconds <= 0. - Test case: Voucher expires in 719 minutes → UI displays
11 : 59 : ss(no day column). - Test case: Voucher expires in 25 hours → UI displays
01 : 01 : 00 : ss.