diff --git a/backend/src/controllers/magicCode.controller.ts b/backend/src/controllers/magicCode.controller.ts index d12828d..cc64441 100644 --- a/backend/src/controllers/magicCode.controller.ts +++ b/backend/src/controllers/magicCode.controller.ts @@ -49,11 +49,8 @@ export async function getSettingsByCode(req: Request, res: Response) { const key = row.setting_key as string; const value = row.setting_value as string; - // Parse numeric values - if (key === 'daily_time_limit_minutes') { - settings[key] = parseInt(value, 10); - } else if (key === 'enabled_apps') { - // Parse JSON array + // Parse JSON array for enabled_apps + if (key === 'enabled_apps') { try { settings[key] = JSON.parse(value); } catch (e) { @@ -75,7 +72,6 @@ export async function getSettingsByCode(req: Request, res: Response) { data: { magicCode: profile.magic_code, settings, - dailyTimeLimit: settings.daily_time_limit_minutes || null, enabledApps } }); diff --git a/backend/src/controllers/settings.controller.ts b/backend/src/controllers/settings.controller.ts index 6e44485..82e552f 100644 --- a/backend/src/controllers/settings.controller.ts +++ b/backend/src/controllers/settings.controller.ts @@ -4,63 +4,6 @@ import { getSetting, setSetting } from '../config/database.js'; import { connectionTracker } from '../services/connection-tracker.service.js'; import crypto from 'crypto'; -export async function getTimeLimit(req: AuthRequest, res: Response) { - try { - const limit = await getSetting('daily_time_limit_minutes'); - const defaultLimit = 1; // Default 1 minute for testing - - res.json({ - success: true, - data: { - dailyLimit: limit ? parseInt(limit, 10) : defaultLimit - } - }); - } catch (error: any) { - console.error('Get time limit error:', error); - res.status(500).json({ - success: false, - error: { - code: 'GET_TIME_LIMIT_ERROR', - message: 'Error fetching time limit' - } - }); - } -} - -export async function setTimeLimit(req: AuthRequest, res: Response) { - try { - const { dailyLimit } = req.body; - - if (!dailyLimit || typeof dailyLimit !== 'number' || dailyLimit < 1) { - return res.status(400).json({ - success: false, - error: { - code: 'INVALID_LIMIT', - message: 'Daily limit must be a number greater than 0' - } - }); - } - - await setSetting('daily_time_limit_minutes', dailyLimit.toString()); - - res.json({ - success: true, - data: { - dailyLimit - } - }); - } catch (error: any) { - console.error('Set time limit error:', error); - res.status(500).json({ - success: false, - error: { - code: 'SET_TIME_LIMIT_ERROR', - message: 'Error setting time limit' - } - }); - } -} - /** * Heartbeat endpoint - clients ping this to indicate they're active * Public endpoint - no auth required @@ -85,23 +28,19 @@ export async function heartbeat(req: AuthRequest, res: Response) { }); } - // Get route, video info, and time limit usage from request body + // Get route and video info from request body const route = req.body.route || '/'; const videoTitle = req.body.videoTitle; const videoChannel = req.body.videoChannel; - const timeUsed = req.body.timeUsed; - const dailyLimit = req.body.dailyLimit; - // Register heartbeat (with user info if authenticated, current route, video info, and time limit usage) + // Register heartbeat (with user info if authenticated, current route, and video info) connectionTracker.heartbeat( sessionId, req.userId, req.username, route, videoTitle, - videoChannel, - timeUsed, - dailyLimit + videoChannel ); res.json({ diff --git a/backend/src/controllers/settingsProfiles.controller.ts b/backend/src/controllers/settingsProfiles.controller.ts index 2c93dc5..e8cba6b 100644 --- a/backend/src/controllers/settingsProfiles.controller.ts +++ b/backend/src/controllers/settingsProfiles.controller.ts @@ -73,9 +73,6 @@ export async function getAllProfiles(req: AuthRequest, res: Response) { return { ...profile, - dailyTimeLimit: profile.settings.daily_time_limit_minutes - ? parseInt(profile.settings.daily_time_limit_minutes, 10) - : null, enabledApps }; }); @@ -168,9 +165,6 @@ export async function getProfile(req: AuthRequest, res: Response) { updatedAt: profile.updated_at, isActive: profile.is_active === 1, settings, - dailyTimeLimit: settings.daily_time_limit_minutes - ? parseInt(settings.daily_time_limit_minutes, 10) - : null, enabledApps } }); @@ -198,7 +192,7 @@ export async function createProfile(req: AuthRequest, res: Response) { }); } - const { name, description, dailyTimeLimit, enabledApps } = req.body; + const { name, description, enabledApps } = req.body; if (!name || typeof name !== 'string' || name.trim().length === 0) { return res.status(400).json({ @@ -210,16 +204,6 @@ export async function createProfile(req: AuthRequest, res: Response) { }); } - if (!dailyTimeLimit || typeof dailyTimeLimit !== 'number' || dailyTimeLimit < 1) { - return res.status(400).json({ - success: false, - error: { - code: 'INVALID_TIME_LIMIT', - message: 'Daily time limit must be a number greater than 0' - } - }); - } - // Generate unique magic code const magicCode = await generateMagicCode(); @@ -234,15 +218,6 @@ export async function createProfile(req: AuthRequest, res: Response) { const profileId = Number(profileResult.lastInsertRowid); - // Add settings - await db.execute({ - sql: ` - INSERT INTO settings_profile_values (profile_id, setting_key, setting_value) - VALUES (?, ?, ?) - `, - args: [profileId, 'daily_time_limit_minutes', dailyTimeLimit.toString()] - }); - // Add enabled apps if provided if (enabledApps && Array.isArray(enabledApps)) { await db.execute({ @@ -269,8 +244,7 @@ export async function createProfile(req: AuthRequest, res: Response) { description: createdProfile.rows[0].description, createdAt: createdProfile.rows[0].created_at, updatedAt: createdProfile.rows[0].updated_at, - isActive: createdProfile.rows[0].is_active === 1, - dailyTimeLimit + isActive: createdProfile.rows[0].is_active === 1 } }); } catch (error: any) { @@ -487,7 +461,7 @@ export async function updateProfileSettings(req: AuthRequest, res: Response) { }); } - const { dailyTimeLimit, enabledApps } = req.body; + const { enabledApps } = req.body; // Verify ownership const existing = await db.execute({ @@ -505,30 +479,6 @@ export async function updateProfileSettings(req: AuthRequest, res: Response) { }); } - if (dailyTimeLimit !== undefined) { - if (typeof dailyTimeLimit !== 'number' || dailyTimeLimit < 1) { - return res.status(400).json({ - success: false, - error: { - code: 'INVALID_TIME_LIMIT', - message: 'Daily time limit must be a number greater than 0' - } - }); - } - - // Update or insert setting - await db.execute({ - sql: ` - INSERT INTO settings_profile_values (profile_id, setting_key, setting_value, updated_at) - VALUES (?, ?, ?, ?) - ON CONFLICT(profile_id, setting_key) DO UPDATE SET - setting_value = excluded.setting_value, - updated_at = excluded.updated_at - `, - args: [profileId, 'daily_time_limit_minutes', dailyTimeLimit.toString(), new Date().toISOString()] - }); - } - if (enabledApps !== undefined) { if (!Array.isArray(enabledApps)) { return res.status(400).json({ @@ -553,24 +503,9 @@ export async function updateProfileSettings(req: AuthRequest, res: Response) { }); } - // Get updated settings - const settingsResult = await db.execute({ - sql: 'SELECT setting_key, setting_value FROM settings_profile_values WHERE profile_id = ?', - args: [profileId] - }); - - const settings: Record = {}; - for (const row of settingsResult.rows) { - settings[row.setting_key as string] = row.setting_value as string; - } - res.json({ success: true, - data: { - dailyTimeLimit: settings.daily_time_limit_minutes - ? parseInt(settings.daily_time_limit_minutes, 10) - : null - } + data: {} }); } catch (error: any) { console.error('Update profile settings error:', error); diff --git a/backend/src/routes/settings.routes.ts b/backend/src/routes/settings.routes.ts index 1b73188..98deffe 100644 --- a/backend/src/routes/settings.routes.ts +++ b/backend/src/routes/settings.routes.ts @@ -1,17 +1,9 @@ import { Router } from 'express'; -import { getTimeLimit, setTimeLimit, heartbeat, getConnectionStats } from '../controllers/settings.controller.js'; -import { authMiddleware } from '../middleware/auth.js'; -import { adminMiddleware } from '../middleware/admin.js'; +import { heartbeat, getConnectionStats } from '../controllers/settings.controller.js'; import { optionalAuthMiddleware } from '../middleware/optionalAuth.js'; const router = Router(); -// Public route - anyone can read the time limit -router.get('/time-limit', getTimeLimit); - -// Admin-only route - only admins can set time limits -router.put('/time-limit', authMiddleware, adminMiddleware, setTimeLimit); - // Public route - heartbeat for connection tracking (optional auth to track authenticated users) router.post('/heartbeat', optionalAuthMiddleware, heartbeat); diff --git a/backend/src/services/connection-tracker.service.ts b/backend/src/services/connection-tracker.service.ts index 1ee28af..e8ff94d 100644 --- a/backend/src/services/connection-tracker.service.ts +++ b/backend/src/services/connection-tracker.service.ts @@ -10,8 +10,6 @@ interface Connection { route?: string; videoTitle?: string; videoChannel?: string; - timeUsed?: number; // minutes - dailyLimit?: number; // minutes lastHeartbeat: number; connectedAt: number; } @@ -31,7 +29,7 @@ class ConnectionTracker { /** * Register or update a connection heartbeat */ - heartbeat(sessionId: string, userId?: number, username?: string, route?: string, videoTitle?: string, videoChannel?: string, timeUsed?: number, dailyLimit?: number): void { + heartbeat(sessionId: string, userId?: number, username?: string, route?: string, videoTitle?: string, videoChannel?: string): void { const now = Date.now(); const existing = this.connections.get(sessionId); @@ -43,8 +41,6 @@ class ConnectionTracker { if (route !== undefined) existing.route = route; if (videoTitle !== undefined) existing.videoTitle = videoTitle; if (videoChannel !== undefined) existing.videoChannel = videoChannel; - if (timeUsed !== undefined) existing.timeUsed = timeUsed; - if (dailyLimit !== undefined) existing.dailyLimit = dailyLimit; } else { // New connection this.connections.set(sessionId, { @@ -54,8 +50,6 @@ class ConnectionTracker { route, videoTitle, videoChannel, - timeUsed, - dailyLimit, lastHeartbeat: now, connectedAt: now }); diff --git a/frontend/src/components/MagicCodeInput/MagicCodeInput.tsx b/frontend/src/components/MagicCodeInput/MagicCodeInput.tsx index f0842b9..70c5ea8 100644 --- a/frontend/src/components/MagicCodeInput/MagicCodeInput.tsx +++ b/frontend/src/components/MagicCodeInput/MagicCodeInput.tsx @@ -64,13 +64,6 @@ export function MagicCodeInput({ onApplied, onClose }: MagicCodeInputProps) { } }; - const formatTime = (minutes: number | null) => { - if (!minutes) return 'Not set'; - if (minutes < 60) return `${minutes} minutes`; - const hours = Math.floor(minutes / 60); - const mins = minutes % 60; - return mins > 0 ? `${hours} hour${hours !== 1 ? 's' : ''} ${mins} minute${mins !== 1 ? 's' : ''}` : `${hours} hour${hours !== 1 ? 's' : ''}`; - }; return (
@@ -85,11 +78,6 @@ export function MagicCodeInput({ onApplied, onClose }: MagicCodeInputProps) {

Code: {appliedCode}

- {settings.dailyTimeLimit && ( -

- Daily time limit: {formatTime(settings.dailyTimeLimit)} -

- )} -
- {dailyLimit !== null && ( -

- Current limit: {formatTime(dailyLimit)} per day -

- )} - - - {dailyLimit !== null && ( -
-

Today's Usage

-
-
-
-
-
- - Used: {formatTime(timeUsed)} - - - Remaining: {formatTime(remainingTime)} - -
-
- - {timeUsed > 0 && ( -
- -
- )} -
- )} -
- - {showResetConfirm && ( -
setShowResetConfirm(false)} - > -
e.stopPropagation()} - > -

Reset Today's Counter?

-

- This will reset the time used today back to 0. Users will be able to watch videos again. -

-
- - -
-
-
- )} - - ); -} diff --git a/frontend/src/hooks/useTimeLimit.ts b/frontend/src/hooks/useTimeLimit.ts deleted file mode 100644 index 0a2b6b0..0000000 --- a/frontend/src/hooks/useTimeLimit.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { useState, useEffect, useCallback, useRef } from 'react'; -import { - getDailyLimit, - getDailyLimitSync, - getTimeUsedToday, - getRemainingTimeToday, - isLimitReached, - addTimeSpent -} from '../services/timeLimitService'; - -interface UseTimeLimitReturn { - dailyLimit: number; - timeUsed: number; - remainingTime: number; - limitReached: boolean; - startTracking: () => void; - stopTracking: () => void; - isTracking: boolean; -} - -/** - * Hook to manage video time limits - * Tracks time spent watching videos and enforces daily limits - */ -export function useTimeLimit(): UseTimeLimitReturn { - const [dailyLimit, setDailyLimit] = useState(getDailyLimitSync()); - const [timeUsed, setTimeUsed] = useState(getTimeUsedToday()); - const [remainingTime, setRemainingTime] = useState(getRemainingTimeToday()); - const [limitReached, setLimitReached] = useState(isLimitReached()); - const [isTracking, setIsTracking] = useState(false); - - const trackingIntervalRef = useRef(null); - const startTimeRef = useRef(null); - const lastUpdateRef = useRef(Date.now()); - - // Update state from localStorage and cached server limit - const updateState = useCallback(() => { - setDailyLimit(getDailyLimitSync()); - setTimeUsed(getTimeUsedToday()); - setRemainingTime(getRemainingTimeToday()); - setLimitReached(isLimitReached()); - }, []); - - // Fetch limit from server on mount - useEffect(() => { - getDailyLimit().then(limit => { - setDailyLimit(limit); - // Immediately recalculate limitReached with the correct server limit - updateState(); - }); - }, [updateState]); - - // Start tracking time - const startTracking = useCallback(() => { - if (limitReached) { - return; // Don't start if limit already reached - } - - if (trackingIntervalRef.current) { - return; // Already tracking - } - - startTimeRef.current = Date.now(); - lastUpdateRef.current = Date.now(); - setIsTracking(true); - - // Update every 5 seconds - trackingIntervalRef.current = setInterval(() => { - if (startTimeRef.current) { - const now = Date.now(); - const secondsElapsed = (now - lastUpdateRef.current) / 1000; - lastUpdateRef.current = now; - - // Add time spent - addTimeSpent(secondsElapsed); - - // Update state - updateState(); - - // Check if limit reached during tracking - if (isLimitReached()) { - // Stop tracking if limit reached - if (trackingIntervalRef.current) { - clearInterval(trackingIntervalRef.current); - trackingIntervalRef.current = null; - } - if (startTimeRef.current && lastUpdateRef.current) { - const secondsElapsed = (Date.now() - lastUpdateRef.current) / 1000; - if (secondsElapsed > 0) { - addTimeSpent(secondsElapsed); - } - startTimeRef.current = null; - } - setIsTracking(false); - updateState(); - } - } - }, 5000); - }, [limitReached, updateState]); - - // Stop tracking time - const stopTracking = useCallback(() => { - if (trackingIntervalRef.current) { - clearInterval(trackingIntervalRef.current); - trackingIntervalRef.current = null; - } - - // Add any remaining time - if (startTimeRef.current && lastUpdateRef.current) { - const secondsElapsed = (Date.now() - lastUpdateRef.current) / 1000; - if (secondsElapsed > 0) { - addTimeSpent(secondsElapsed); - } - startTimeRef.current = null; - } - - setIsTracking(false); - updateState(); - }, [updateState]); - - // Cleanup on unmount - useEffect(() => { - return () => { - if (trackingIntervalRef.current) { - clearInterval(trackingIntervalRef.current); - } - // Save any remaining time before unmount - if (startTimeRef.current && lastUpdateRef.current) { - const secondsElapsed = (Date.now() - lastUpdateRef.current) / 1000; - if (secondsElapsed > 0) { - addTimeSpent(secondsElapsed); - } - } - }; - }, []); - - // Update state periodically to catch external changes - useEffect(() => { - const interval = setInterval(() => { - updateState(); - }, 10000); // Check every 10 seconds - - return () => clearInterval(interval); - }, [updateState]); - - return { - dailyLimit, - timeUsed, - remainingTime, - limitReached, - startTracking, - stopTracking, - isTracking - }; -} diff --git a/frontend/src/services/apiClient.ts b/frontend/src/services/apiClient.ts index 66627cd..282619c 100644 --- a/frontend/src/services/apiClient.ts +++ b/frontend/src/services/apiClient.ts @@ -124,12 +124,7 @@ export const videosApi = { // Settings API export const settingsApi = { - getTimeLimit: () => api.get('/settings/time-limit'), - - setTimeLimit: (dailyLimit: number) => - api.put('/settings/time-limit', { dailyLimit }), - - heartbeat: (sessionId: string, route: string, video?: { title: string; channelName: string }, timeLimit?: { timeUsed: number; dailyLimit: number }) => api.post('/settings/heartbeat', { sessionId, route, videoTitle: video?.title, videoChannel: video?.channelName, timeUsed: timeLimit?.timeUsed, dailyLimit: timeLimit?.dailyLimit }), + heartbeat: (sessionId: string, route: string, video?: { title: string; channelName: string }) => api.post('/settings/heartbeat', { sessionId, route, videoTitle: video?.title, videoChannel: video?.channelName }), getConnectionStats: () => api.get('/settings/connection-stats') }; @@ -177,7 +172,7 @@ export const settingsProfilesApi = { getById: (id: number) => api.get(`/settings-profiles/${id}`), - create: (data: { name: string; description?: string; dailyTimeLimit?: number; enabledApps?: string[] }) => + create: (data: { name: string; description?: string; enabledApps?: string[] }) => api.post('/settings-profiles', data), update: (id: number, data: { name?: string; description?: string; isActive?: boolean }) => @@ -185,7 +180,7 @@ export const settingsProfilesApi = { delete: (id: number) => api.delete(`/settings-profiles/${id}`), - updateSettings: (id: number, settings: { dailyTimeLimit?: number; enabledApps?: string[] }) => + updateSettings: (id: number, settings: { enabledApps?: string[] }) => api.put(`/settings-profiles/${id}/settings`, settings), regenerateCode: (id: number) => api.post(`/settings-profiles/${id}/regenerate-code`) diff --git a/frontend/src/services/connectionTracker.ts b/frontend/src/services/connectionTracker.ts index abf46dd..1eecea6 100644 --- a/frontend/src/services/connectionTracker.ts +++ b/frontend/src/services/connectionTracker.ts @@ -106,23 +106,19 @@ export function setCurrentVideo(video: { title: string; channelName: string } | } /** - * Send a heartbeat to the server with the session ID, current route, video info, and time limit usage + * Send a heartbeat to the server with the session ID, current route, and video info */ async function sendHeartbeat(): Promise { try { const { settingsApi } = await import('./apiClient'); - const { getTimeUsedToday, getDailyLimitSync } = await import('./timeLimitService'); const sessionId = getSessionId(); const route = getCurrentRoute(); - const timeUsed = getTimeUsedToday(); - const dailyLimit = getDailyLimitSync(); await settingsApi.heartbeat( sessionId, route, - currentVideo ? { title: currentVideo.title, channelName: currentVideo.channelName } : undefined, - { timeUsed, dailyLimit } + currentVideo ? { title: currentVideo.title, channelName: currentVideo.channelName } : undefined ); } catch (error) { // Silently fail - don't spam console with errors diff --git a/frontend/src/services/magicCodeService.ts b/frontend/src/services/magicCodeService.ts index b83a0ad..cd948e7 100644 --- a/frontend/src/services/magicCodeService.ts +++ b/frontend/src/services/magicCodeService.ts @@ -2,7 +2,6 @@ const MAGIC_CODE_KEY = 'magic_code'; const MAGIC_CODE_SETTINGS_KEY = 'magic_code_settings'; export interface MagicCodeSettings { - dailyTimeLimit: number | null; enabledApps: string[] | null; appliedAt: string; } @@ -58,7 +57,6 @@ export async function applyMagicCode(code: string): Promise { const response: any = await magicCodeApi.getSettingsByCode(normalizedCode); const settings: MagicCodeSettings = { - dailyTimeLimit: response.data.dailyTimeLimit, enabledApps: response.data.enabledApps || null, appliedAt: new Date().toISOString() }; diff --git a/frontend/src/services/timeLimitService.ts b/frontend/src/services/timeLimitService.ts deleted file mode 100644 index 0a72dc3..0000000 --- a/frontend/src/services/timeLimitService.ts +++ /dev/null @@ -1,210 +0,0 @@ -interface TimeLimitData { - dailyLimit: number; // minutes (stored on server) - dailyTimeUsed: number; // minutes (stored per-device in localStorage) - lastResetDate: string; // ISO date string (YYYY-MM-DD) -} - -const STORAGE_KEY = 'video_time_limit'; -const DEFAULT_DAILY_LIMIT = 1; // 1 minute for testing - -// Cache for daily limit from server -let cachedDailyLimit: number | null = null; -let limitCacheTime: number = 0; -const LIMIT_CACHE_DURATION = 5 * 60 * 1000; // 5 minutes - -/** - * Get local date string in YYYY-MM-DD format (not UTC) - * This ensures the daily reset happens at local midnight, not UTC midnight - */ -function getLocalDateString(): string { - const now = new Date(); - const year = now.getFullYear(); - const month = String(now.getMonth() + 1).padStart(2, '0'); - const day = String(now.getDate()).padStart(2, '0'); - return `${year}-${month}-${day}`; -} - -/** - * Get time limit data from localStorage (for usage tracking only) - */ -function getTimeLimitData(): Omit { - try { - const stored = localStorage.getItem(STORAGE_KEY); - if (stored) { - const data = JSON.parse(stored); - return { - dailyTimeUsed: data.dailyTimeUsed || 0, - lastResetDate: data.lastResetDate || getLocalDateString() - }; - } - } catch (e) { - console.warn('Failed to parse time limit data from localStorage', e); - } - - // Return default data - return { - dailyTimeUsed: 0, - lastResetDate: getLocalDateString() - }; -} - -/** - * Save time limit data to localStorage (for usage tracking only) - */ -function saveTimeLimitData(data: Omit): void { - try { - localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); - } catch (e) { - console.warn('Failed to save time limit data to localStorage', e); - } -} - -/** - * Check if we need to reset daily counter (new day) - */ -function shouldResetDaily(): boolean { - const data = getTimeLimitData(); - const today = getLocalDateString(); - return data.lastResetDate !== today; -} - -/** - * Reset daily counter if it's a new day - */ -function resetIfNeeded(): void { - if (shouldResetDaily()) { - const data = getTimeLimitData(); - data.dailyTimeUsed = 0; - data.lastResetDate = getLocalDateString(); - saveTimeLimitData(data); - } -} - -/** - * Get current time limit configuration - * Priority: Magic code settings > Server settings > Cached > Default - */ -export async function getDailyLimit(): Promise { - // Check magic code settings first (highest priority) - try { - const { getMagicCodeSettings } = await import('./magicCodeService'); - const magicCodeSettings = getMagicCodeSettings(); - if (magicCodeSettings && magicCodeSettings.dailyTimeLimit !== null && magicCodeSettings.dailyTimeLimit !== undefined) { - return magicCodeSettings.dailyTimeLimit; - } - } catch (error) { - console.warn('Failed to check magic code settings:', error); - } - - // Return cached value if still valid - const now = Date.now(); - if (cachedDailyLimit !== null && (now - limitCacheTime) < LIMIT_CACHE_DURATION) { - return cachedDailyLimit; - } - - try { - const { settingsApi } = await import('./apiClient'); - const response = await settingsApi.getTimeLimit(); - const limit = response.data.dailyLimit; - cachedDailyLimit = limit; - limitCacheTime = now; - return limit; - } catch (error) { - console.warn('Failed to fetch daily limit from server, using cached/default:', error); - // Return cached value or default - return cachedDailyLimit ?? DEFAULT_DAILY_LIMIT; - } -} - -/** - * Set daily time limit (in minutes) on server - */ -export async function setDailyLimit(minutes: number): Promise { - try { - const { settingsApi } = await import('./apiClient'); - await settingsApi.setTimeLimit(minutes); - cachedDailyLimit = minutes; - limitCacheTime = Date.now(); - } catch (error) { - console.error('Failed to set daily limit on server:', error); - throw error; - } -} - -/** - * Synchronous version for use in hooks (uses cached value or magic code settings) - * Note: Magic code settings are checked synchronously from localStorage - */ -export function getDailyLimitSync(): number { - // Check magic code settings first (highest priority) - // We can access localStorage synchronously - try { - const MAGIC_CODE_SETTINGS_KEY = 'magic_code_settings'; - const stored = localStorage.getItem(MAGIC_CODE_SETTINGS_KEY); - if (stored) { - const magicCodeSettings = JSON.parse(stored); - if (magicCodeSettings && magicCodeSettings.dailyTimeLimit !== null && magicCodeSettings.dailyTimeLimit !== undefined) { - return magicCodeSettings.dailyTimeLimit; - } - } - } catch (error) { - // Ignore errors in sync context - } - - return cachedDailyLimit ?? DEFAULT_DAILY_LIMIT; -} - -/** - * Get time used today (in minutes) - per device - */ -export function getTimeUsedToday(): number { - resetIfNeeded(); - const data = getTimeLimitData(); - return data.dailyTimeUsed; -} - -/** - * Get remaining time today (in minutes) - * Note: Uses cached limit value - */ -export function getRemainingTimeToday(): number { - resetIfNeeded(); - const limit = getDailyLimitSync(); - const used = getTimeUsedToday(); - return Math.max(0, limit - used); -} - -/** - * Check if daily limit has been reached - * Note: Uses cached limit value - */ -export function isLimitReached(): boolean { - resetIfNeeded(); - return getRemainingTimeToday() <= 0; -} - -/** - * Add time spent (in seconds) to the daily counter - * Note: Uses cached limit value to cap the usage - */ -export function addTimeSpent(seconds: number): void { - resetIfNeeded(); - const data = getTimeLimitData(); - const minutesToAdd = seconds / 60; - const limit = getDailyLimitSync(); - data.dailyTimeUsed = Math.min( - limit, - data.dailyTimeUsed + minutesToAdd - ); - saveTimeLimitData(data); -} - -/** - * Reset daily counter (for testing/admin purposes) - */ -export function resetDailyCounter(): void { - const data = getTimeLimitData(); - data.dailyTimeUsed = 0; - data.lastResetDate = getLocalDateString(); - saveTimeLimitData(data); -} diff --git a/frontend/src/types/api.ts b/frontend/src/types/api.ts index a36d739..d7ebab9 100644 --- a/frontend/src/types/api.ts +++ b/frontend/src/types/api.ts @@ -50,7 +50,6 @@ export interface SettingsProfile { createdAt: string; updatedAt: string; isActive: boolean; - dailyTimeLimit: number | null; enabledApps?: string[]; }