feat: Add configurable draw cycles, improve UX

Backend:
- Add configurable draw cycle settings (minutes/hourly/daily/weekly/custom)
- Add CYCLE_TYPE, CYCLE_INTERVAL_*, CYCLE_DAILY_TIME, CYCLE_WEEKLY_* env vars
- Add SALES_CLOSE_BEFORE_DRAW_MINUTES and CYCLES_TO_GENERATE_AHEAD
- Fix SQLite parameter issue in scheduler

Frontend:
- Add 'Save This Link' section with copy button on ticket status page
- Improve draw animation to show immediately when draw starts
- Show 'Waiting for next round...' instead of 'Drawing Now!' after draw
- Hide Buy Tickets button when waiting for next round
- Skip draw animation if no tickets were sold
- Keep winner screen open longer (15s) for next cycle to load
- Auto-refresh to next lottery cycle after draw

Telegram Bot:
- Various improvements and fixes
This commit is contained in:
Michilis
2025-11-28 03:24:17 +00:00
parent f743a6749c
commit 918d3bc31e
21 changed files with 584 additions and 140 deletions

View File

@@ -26,3 +26,4 @@ Thumbs.db
*.swp
*.swo

View File

@@ -42,3 +42,4 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD ["node", "dist/index.js"]

View File

@@ -144,3 +144,4 @@ User states:
MIT

View File

@@ -35,3 +35,4 @@
}
}

View File

@@ -48,3 +48,4 @@ export const config = {
export default config;

View File

@@ -339,3 +339,4 @@ export default {
broadcastDrawReminder,
};

View File

@@ -143,3 +143,4 @@ class ApiClient {
export const apiClient = new ApiClient();
export default apiClient;

View File

@@ -222,3 +222,4 @@ class GroupStateManager {
export const groupStateManager = new GroupStateManager();
export default groupStateManager;

View File

@@ -80,3 +80,4 @@ export const logPaymentEvent = (
export default logger;

View File

@@ -25,3 +25,4 @@ export async function generateQRCode(data: string): Promise<Buffer> {
export default { generateQRCode };

View File

@@ -260,3 +260,4 @@ class StateManager {
export const stateManager = new StateManager();
export default stateManager;

View File

@@ -23,3 +23,4 @@ export const DEFAULT_GROUP_SETTINGS: Omit<GroupSettings, 'groupId' | 'groupTitle
ticketPurchaseAllowed: false, // Disabled by default for privacy - users should buy in DM
};

View File

@@ -69,3 +69,4 @@ export function truncate(str: string, maxLength: number): string {
return str.substring(0, maxLength - 3) + '...';
}

View File

@@ -19,3 +19,4 @@
"exclude": ["node_modules", "dist"]
}