diff --git a/.gitignore b/.gitignore index b6b8dc50..56e92252 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,16 @@ +# Secrets / local env .env -serviceKey.json \ No newline at end of file +serviceKey.json + +# macOS +.DS_Store +**/.DS_Store + +# Dependencies & build output (kept out of iCloud via *.nosync too) +node_modules/ +**/node_modules/ +.next/ +**/.next/ +out/ +**/out/ +*.nosync diff --git a/bsg-frontend/.next 2 b/bsg-frontend/.next 2 new file mode 120000 index 00000000..b8785493 --- /dev/null +++ b/bsg-frontend/.next 2 @@ -0,0 +1 @@ +.next.nosync \ No newline at end of file diff --git a/bsg-frontend/apps/extension/customComponents/NotificationToggle.tsx b/bsg-frontend/apps/extension/customComponents/NotificationToggle.tsx new file mode 100644 index 00000000..6d00f8d6 --- /dev/null +++ b/bsg-frontend/apps/extension/customComponents/NotificationToggle.tsx @@ -0,0 +1,46 @@ +type NotificationToggleProps = { + enabled: boolean; + onChange: (enabled: boolean) => void; + disabled?: boolean; +}; + +export const NotificationToggle = ({ + enabled, + onChange, + disabled = false, +}: NotificationToggleProps) => ( + +); \ No newline at end of file diff --git a/bsg-frontend/apps/extension/customComponents/RoomDisplays/ChatDisplay.tsx b/bsg-frontend/apps/extension/customComponents/RoomDisplays/ChatDisplay.tsx index 9e57fbe5..6fc84f60 100644 --- a/bsg-frontend/apps/extension/customComponents/RoomDisplays/ChatDisplay.tsx +++ b/bsg-frontend/apps/extension/customComponents/RoomDisplays/ChatDisplay.tsx @@ -28,6 +28,7 @@ export const ChatDisplay = ({ isActive }: { isActive: boolean }) => { } = useChatSocket(); const username = useUserStore(s => s.username); + const email = useUserStore(s => s.email); // ← add this line const emojiMap = require('@bsg/ui-styles/assets/emojis.json') as Record; const emojiList = Object.entries(emojiMap).flatMap(([category, emojis]) => emojis.map(emoji => ({ ...emoji, category })) @@ -76,7 +77,7 @@ export const ChatDisplay = ({ isActive }: { isActive: boolean }) => { : <> - {(group[0].userName === username) ? + {(group[0].userHandle === email) ? // just changed // User's own message
{ + + const roomId = useRoomStore(s => s.roomId); + const isAdmin = useRoomStore(s => s.isAdmin); + const isRoundStarted = useRoomStore(s => s.isRoundStarted); + const roundDuration = useRoomStore(s => s.roundDuration); + const setRoundDuration = useRoomStore(s => s.setRoundDuration); + + const [ value, setValue ] = useState(roundDuration ?? 30); + const [ saving, setSaving ] = useState(false); + const [ error, setError ] = useState(null); + const [ saved, setSaved ] = useState(false); + + + const chatNotificationsEnabled = useSettingsStore(s => s.chatNotificationsEnabled); + const setChatNotificationsEnabled = useSettingsStore(s => s.setChatNotificationsEnabled); + const loadSettings = useSettingsStore(s => s.loadSettings); + + useEffect(() => { + if (isActive) loadSettings(); + }, [isActive, loadSettings]); + + + // Sync the working value when the stored duration changes or the tab reopens. + useEffect(() => { + if (roundDuration != null) setValue(roundDuration); + }, [roundDuration, isActive]); + + const clamp = (n: number) => Math.max(MIN_DURATION, Math.min(MAX_DURATION, n)); + const change = (next: number) => { + setValue(clamp(next)); + setSaved(false); + setError(null); + }; + + const dirty = value !== roundDuration; + + const onSave = async () => { + if (!roomId) { + setError('Not in a room.'); + return; + } + setSaving(true); + setError(null); + try { + const res = await fetch(`${SERVER_URL}/rooms/${roomId}/rounds/time`, { + method: 'POST', + body: JSON.stringify({ duration: value }), + headers: { 'Content-Type': 'application/json' }, + credentials: 'include' + }); + const data = await res.json().catch(() => null); + if (!res.ok) { + setError((data && (data.error || data.message)) || `Failed to update timer: ${res.status}`); + } else { + setRoundDuration(data?.data?.duration ?? value); + setSaved(true); + } + } catch (e: any) { + setError(e?.message || 'Failed to update timer.'); + } finally { + setSaving(false); + } + }; + + //timer update success message timeout + useEffect(() => { + if (!saved) return; + + const timeout = setTimeout(() => { + setSaved(false); + }, 3000); + + return () => clearTimeout(timeout); + }, [saved]); + + return ( +
+
Settings
+ +
+
Edit Timer
+ +
+ {!isAdmin ? ( +

+ Only the room admin can change the timer. +

+ ) : isRoundStarted ? ( +

+ The timer can only be edited before the round starts. +

+ ) : ( +
+ + change(val[0])} + className="pt-2 max-w-[377px]" + /> +
+ )} +
+
+ + +
+
+ Chat sounds + + Play a sound when messages are sent or received + +
+ +
+ + {isAdmin && !isRoundStarted && ( +
+ {error &&

{error}

} + {saved && !dirty && ( +

Timer updated.

+ )} + +
+ )} +
+ ); +}; diff --git a/bsg-frontend/apps/extension/customComponents/TabBar/Sidebar.tsx b/bsg-frontend/apps/extension/customComponents/TabBar/Sidebar.tsx index b1f8d0e9..9f6fd559 100644 --- a/bsg-frontend/apps/extension/customComponents/TabBar/Sidebar.tsx +++ b/bsg-frontend/apps/extension/customComponents/TabBar/Sidebar.tsx @@ -15,7 +15,14 @@ export const Sidebar = ({ isInRoom }: { isInRoom: boolean }) => { const activeTab = useRoomStore(s => s.activeTab); const setActiveTab = useRoomStore(s => s.setActiveTab); const isPanelHovered = usePanelStore(s => s.isPanelHovered); + + const unreadCount = useRoomStore(s => s.unreadCount); + //to select tab and unfold the panel - sets active tab and unfolds the extension! + const selectTabAndUnfold = (tab: TabName) => { + setActiveTab(tab); + messageScript('UNFOLD'); + }; return (
messageScript('UNFOLD')} @@ -32,7 +39,7 @@ export const Sidebar = ({ isInRoom }: { isInRoom: boolean }) => { {/* Logo */}
-
+
{ stroke-linejoin="round" /> + + {unreadCount > 0 && ( + + {unreadCount > 9 ? ( + <> + 9 + + + + ) : ( + unreadCount + )} + + )}
@@ -65,7 +105,7 @@ export const Sidebar = ({ isInRoom }: { isInRoom: boolean }) => {
\ No newline at end of file diff --git a/bsg-frontend/apps/extension/pages/_app.tsx b/bsg-frontend/apps/extension/pages/_app.tsx index b35850f9..2131d4c7 100644 --- a/bsg-frontend/apps/extension/pages/_app.tsx +++ b/bsg-frontend/apps/extension/pages/_app.tsx @@ -11,6 +11,7 @@ import { Footer } from '@/customComponents/Footer/Footer'; import { useRoomStore } from '@/stores/useRoomStore'; import { messageScript } from '@/utils/messageScript'; import { useIsActive } from '@/hooks/useIsActive'; +import { useEffect } from 'react'; const poppins = Poppins({ weight: '400', subsets: ['latin'] }); @@ -21,6 +22,21 @@ export default function App({ Component, pageProps }: AppProps) { const setIsPanelHovered = usePanelStore(s => s.setIsPanelHovered); const isInRoom = useRoomStore(s => s.isInRoom); const { isActive, setIsActive } = useIsActive(); + const activeTab = useRoomStore(s => s.activeTab); + const clearUnread = useRoomStore(s => s.clearUnread); + + const setIsFolded = usePanelStore(s => s.setIsFolded); + + useEffect(() => { + setIsFolded(isFolded); + }, [isFolded, setIsFolded]); + + useEffect(() => { + if (activeTab === 'chat' && !isFolded) { + console.log('[BSG unread] clearing — chat is active'); + clearUnread(); + } + }, [activeTab, isFolded, clearUnread]); // Redirect popup render if (isDefaultPopup) { diff --git a/bsg-frontend/apps/extension/pages/room-page.tsx b/bsg-frontend/apps/extension/pages/room-page.tsx index a7b9a4c3..185ea8a3 100644 --- a/bsg-frontend/apps/extension/pages/room-page.tsx +++ b/bsg-frontend/apps/extension/pages/room-page.tsx @@ -2,7 +2,10 @@ import { RoomInfoDisplay } from '@/customComponents/RoomDisplays/RoomInfoDisplay import { ChatDisplay } from '@/customComponents/RoomDisplays/ChatDisplay'; import { LeaderboardDisplay } from '@/customComponents/RoomDisplays/LeaderboardDisplay'; import { StatisticsDisplay } from '@/customComponents/RoomDisplays/StatisticsDisplay'; +import { SettingsDisplay } from '@/customComponents/RoomDisplays/SettingsDisplay'; import { useRoomStore } from '@/stores/useRoomStore'; +import { useSettingsStore } from '@/stores/useSettingsStore'; +import { useEffect } from 'react'; export default function RoomPage() { @@ -10,6 +13,10 @@ export default function RoomPage() { const roomNotice = useRoomStore(s => s.roomNotice) const setRoomNotice = useRoomStore(s => s.setRoomNotice) + useEffect(() => { + useSettingsStore.getState().loadSettings(); + }, []); // Load settings when the component mounts + return ( <> {roomNotice && ( @@ -30,6 +37,7 @@ export default function RoomPage() { + ); } \ No newline at end of file diff --git a/bsg-frontend/apps/extension/stores/usePanelStore.ts b/bsg-frontend/apps/extension/stores/usePanelStore.ts index 62b3a2ef..f291cb9c 100644 --- a/bsg-frontend/apps/extension/stores/usePanelStore.ts +++ b/bsg-frontend/apps/extension/stores/usePanelStore.ts @@ -3,9 +3,13 @@ import { create } from 'zustand'; interface panelStoreState { isPanelHovered: boolean; setIsPanelHovered: (isPanelHovered: boolean) => void; + isFolded: boolean; + setIsFolded: (isFolded: boolean) => void; } export const usePanelStore = create((set) => ({ - isPanelHovered: false, - setIsPanelHovered: (isPanelHovered) => set({ isPanelHovered: isPanelHovered }), -})); \ No newline at end of file + isPanelHovered: false, + setIsPanelHovered: (isPanelHovered) => set({ isPanelHovered }), + isFolded: false, + setIsFolded: (isFolded) => set({ isFolded }), +})); diff --git a/bsg-frontend/apps/extension/stores/useRoomStore.ts b/bsg-frontend/apps/extension/stores/useRoomStore.ts index 63ef69f8..7805be87 100644 --- a/bsg-frontend/apps/extension/stores/useRoomStore.ts +++ b/bsg-frontend/apps/extension/stores/useRoomStore.ts @@ -14,9 +14,13 @@ interface roomStoreState { activeTab: TabName; isRoundStarted: boolean; roundEndTime: number | null; + roundDuration: number | null; lastGameEvent: GameEvent | null; roomNotice: string | null; + unreadCount: number; // for chat notification count + incrementUnread: () => void; // for chat notification count + clearUnread: () => void; // for chat notification count setIsInRoom: (isInRoom: boolean) => void; setRoomId: (roomId: string | null) => void; setIsConnected: (isConnected: boolean) => void; @@ -27,6 +31,7 @@ interface roomStoreState { setActiveTab: (activeTab: TabName) => void; setIsRoundStarted: (isRoundStarted: boolean) => void; setRoundEndTime: (roundEndTime: number | null) => void; + setRoundDuration: (roundDuration: number | null) => void; setLastGameEvent: (lastGameEvent: GameEvent | null) => void; setRoomNotice: (roomNotice: string | null) => void; @@ -50,8 +55,10 @@ const roomStoreInit = { activeTab: 'chat' as TabName, isRoundStarted: false, roundEndTime: null, + roundDuration: null, lastGameEvent: null, roomNotice: null, + unreadCount: 0, // set to 0 initially }; export const useRoomStore = create((set) => ({ @@ -67,8 +74,20 @@ export const useRoomStore = create((set) => ({ setActiveTab: (activeTab) => set({ activeTab: activeTab }), setIsRoundStarted: (isRoundStarted) => set({ isRoundStarted: isRoundStarted }), setRoundEndTime: (roundEndTime) => set({ roundEndTime: roundEndTime }), + setRoundDuration: (roundDuration) => set({ roundDuration: roundDuration }), setLastGameEvent: (lastGameEvent) => set({ lastGameEvent: lastGameEvent }), + // for chat notification count setRoomNotice: (roomNotice) => set({ roomNotice: roomNotice }), + incrementUnread: () => set((state) => { + const unreadCount = state.unreadCount + 1; + console.log('[BSG unread] increment →', unreadCount); + return { unreadCount }; + }), + clearUnread: () => { + console.log('[BSG unread] clear → 0'); + set({ unreadCount: 0 }); + }, + initRoom: ( roomId, @@ -80,7 +99,8 @@ export const useRoomStore = create((set) => ({ roomCode: roomCode, adminId: adminId, isAdmin: isAdmin, - isInRoom: true + isInRoom: true, + unreadCount: 0 }), resetRoom: () => set(roomStoreInit) })); \ No newline at end of file diff --git a/bsg-frontend/apps/extension/stores/useSettingsStore.ts b/bsg-frontend/apps/extension/stores/useSettingsStore.ts new file mode 100644 index 00000000..821332e0 --- /dev/null +++ b/bsg-frontend/apps/extension/stores/useSettingsStore.ts @@ -0,0 +1,29 @@ +import { create } from 'zustand'; + +interface SettingsStoreState { + chatNotificationsEnabled: boolean; + setChatNotificationsEnabled: (enabled: boolean) => void; + loadSettings: () => void; +} + +export const useSettingsStore = create((set) => ({ + chatNotificationsEnabled: true, + + setChatNotificationsEnabled: (enabled) => { + set({ chatNotificationsEnabled: enabled }); + + if (typeof chrome !== 'undefined' && chrome.storage?.local) { + chrome.storage.local.set({ chatNotificationsEnabled: enabled }); + } + }, + + loadSettings: () => { + if (typeof chrome !== 'undefined' && chrome.storage?.local) { + chrome.storage.local.get(['chatNotificationsEnabled'], (result) => { + if (typeof result.chatNotificationsEnabled === 'boolean') { + set({ chatNotificationsEnabled: result.chatNotificationsEnabled }); + } + }); + } + }, +})); \ No newline at end of file diff --git a/bsg-frontend/apps/extension/tsconfig.json b/bsg-frontend/apps/extension/tsconfig.json index 449bf4da..a870345f 100644 --- a/bsg-frontend/apps/extension/tsconfig.json +++ b/bsg-frontend/apps/extension/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "ignoreDeprecations": "5.0", + "ignoreDeprecations": "6.0", "types": [ "chrome", "node" diff --git a/bsg-frontend/extension/sounds/message-recieved.mp3 b/bsg-frontend/extension/sounds/message-recieved.mp3 new file mode 100644 index 00000000..0430b396 Binary files /dev/null and b/bsg-frontend/extension/sounds/message-recieved.mp3 differ diff --git a/bsg-frontend/extension/sounds/message-sent.mp3 b/bsg-frontend/extension/sounds/message-sent.mp3 new file mode 100644 index 00000000..4f61b6ec Binary files /dev/null and b/bsg-frontend/extension/sounds/message-sent.mp3 differ diff --git a/bsg-frontend/package-lock.json b/bsg-frontend/package-lock.json index 2c5063bd..c50192f7 100644 --- a/bsg-frontend/package-lock.json +++ b/bsg-frontend/package-lock.json @@ -199,6 +199,10 @@ "node": ">=6.9.0" } }, + "node_modules/@bsg/assets": { + "resolved": "packages/assets", + "link": true + }, "node_modules/@bsg/components": { "resolved": "packages/components/customComponents", "link": true @@ -311,7 +315,6 @@ "version": "6.36.5", "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.5.tgz", "integrity": "sha512-cd+FZEUlu3GQCYnguYm3EkhJ8KJVisqqUsCOKedBoAt/d9c76JUUap6U0UrpElln5k6VyrEOYliMuDAKIeDQLg==", - "peer": true, "dependencies": { "@codemirror/state": "^6.5.0", "style-mod": "^4.1.0", @@ -465,7 +468,6 @@ "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.13.2.tgz", "integrity": "sha512-jwtMmJa1BXXDCiDx1vC6SFN/+HfYG53UkfJa6qeN5ogvOunzbFDO3wISZy5n9xgYFUrEP6M7e8EG++riHNTv9w==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@firebase/component": "0.6.18", "@firebase/logger": "0.4.4", @@ -532,7 +534,6 @@ "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.4.2.tgz", "integrity": "sha512-LssbyKHlwLeiV8GBATyOyjmHcMpX/tFjzRUCS1jnwGAew1VsBB4fJowyS5Ud5LdFbYpJeS+IQoC+RQxpK7eH3Q==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@firebase/app": "0.13.2", "@firebase/component": "0.6.18", @@ -548,8 +549,7 @@ "version": "0.9.3", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/@firebase/auth-compat": { "version": "0.5.28", @@ -1000,7 +1000,6 @@ "integrity": "sha512-zGlBn/9Dnya5ta9bX/fgEoNC3Cp8s6h+uYPYaDieZsFOAdHP/ExzQ/eaDgxD3GOROdPkLKpvKY0iIzr9adle0w==", "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.1.0" }, @@ -1066,7 +1065,6 @@ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-7.1.0.tgz", "integrity": "sha512-fNxRUk1KhjSbnbuBxlWSnBLKLBNun52ZBTcs22H/xEEzM6Ap81ZFTQ4bZBxVQGQgVY0xugKGoRcCbaKjLQ3XZA==", "license": "MIT", - "peer": true, "dependencies": { "@fortawesome/fontawesome-common-types": "7.1.0" }, @@ -1781,6 +1779,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, + "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" @@ -3957,6 +3956,7 @@ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, + "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -3967,6 +3967,7 @@ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, + "peer": true, "dependencies": { "@types/eslint": "*", "@types/estree": "*" @@ -3976,7 +3977,8 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@types/filesystem": { "version": "0.0.36", @@ -4003,7 +4005,8 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@types/json5": { "version": "0.0.29", @@ -4025,7 +4028,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -4035,7 +4037,6 @@ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -4447,6 +4448,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, + "peer": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" @@ -4456,25 +4458,29 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, + "peer": true, "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", "@webassemblyjs/helper-api-error": "1.13.2", @@ -4485,13 +4491,15 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -4504,6 +4512,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, + "peer": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -4513,6 +4522,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, + "peer": true, "dependencies": { "@xtuc/long": "4.2.2" } @@ -4521,13 +4531,15 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -4544,6 +4556,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", @@ -4557,6 +4570,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -4569,6 +4583,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-api-error": "1.13.2", @@ -4583,6 +4598,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" @@ -4592,19 +4608,20 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/acorn": { "version": "8.14.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4640,6 +4657,7 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, + "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -4657,6 +4675,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -4672,7 +4691,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "peer": true }, "node_modules/ansi-regex": { "version": "5.0.1", @@ -5052,7 +5072,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", @@ -5214,6 +5233,7 @@ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, + "peer": true, "engines": { "node": ">=6.0" } @@ -5841,7 +5861,8 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/es-object-atoms": { "version": "1.1.1", @@ -5929,7 +5950,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -6083,7 +6103,6 @@ "version": "2.31.0", "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.8", @@ -6335,6 +6354,7 @@ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, + "peer": true, "engines": { "node": ">=0.8.x" } @@ -6479,7 +6499,8 @@ "type": "opencollective", "url": "https://opencollective.com/fastify" } - ] + ], + "peer": true }, "node_modules/fastq": { "version": "1.19.1", @@ -7533,6 +7554,7 @@ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, + "peer": true, "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -7547,6 +7569,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -7681,6 +7704,7 @@ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, + "peer": true, "engines": { "node": ">=6.11.5" } @@ -7752,7 +7776,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "dev": true, + "peer": true }, "node_modules/merge2": { "version": "1.4.1", @@ -7779,6 +7804,7 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, + "peer": true, "engines": { "node": ">= 0.6" } @@ -7788,6 +7814,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, + "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -7877,7 +7904,8 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true + "dev": true, + "peer": true }, "node_modules/next": { "version": "16.2.2", @@ -8799,6 +8827,7 @@ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, + "peer": true, "dependencies": { "safe-buffer": "^5.1.0" } @@ -8808,7 +8837,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -8818,7 +8846,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -8836,7 +8863,6 @@ "version": "7.55.0", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.55.0.tgz", "integrity": "sha512-XRnjsH3GVMQz1moZTW53MxfoWN7aDpUg/GpVNc4A3eXRVNdGXfbzJ4vM4aLQ8g6XCUh1nIbx70aaNCl7kxnjog==", - "peer": true, "engines": { "node": ">=18.0.0" }, @@ -8851,15 +8877,13 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "peer": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-redux": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", - "peer": true, "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -9018,8 +9042,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/redux-thunk": { "version": "3.1.0", @@ -9164,6 +9187,7 @@ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -9329,6 +9353,7 @@ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", "dev": true, + "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -9365,6 +9390,7 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -9376,7 +9402,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "peer": true }, "node_modules/semver": { "version": "7.7.3", @@ -9395,6 +9422,7 @@ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, + "peer": true, "dependencies": { "randombytes": "^2.1.0" } @@ -9627,6 +9655,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -9644,6 +9673,7 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, + "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -10111,6 +10141,7 @@ "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", "dev": true, + "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -10129,6 +10160,7 @@ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", "dev": true, + "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", @@ -10162,7 +10194,8 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/text-table": { "version": "0.2.0", @@ -10227,7 +10260,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -10374,7 +10406,6 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10607,6 +10638,7 @@ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true, + "peer": true, "engines": { "node": ">=10.13.0" } @@ -10616,6 +10648,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -10629,6 +10662,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, + "peer": true, "engines": { "node": ">=4.0" } @@ -10639,6 +10673,7 @@ "integrity": "sha512-e6vZvY6xboSwLz2GD36c16+O/2Z6fKvIf4pOXptw2rY9MVwE/TXc6RGqxD3I3x0a28lwBY7DE+76uTPSsBrrCA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -10900,6 +10935,10 @@ } } }, + "packages/assets": { + "name": "@bsg/assets", + "version": "1.0.0" + }, "packages/components/customComponents": { "name": "@bsg/components", "version": "1.0.0", diff --git a/bsg-frontend/packages/models/TabName.ts b/bsg-frontend/packages/models/TabName.ts index bb056ded..f655dbe8 100644 --- a/bsg-frontend/packages/models/TabName.ts +++ b/bsg-frontend/packages/models/TabName.ts @@ -1 +1 @@ -export type TabName = 'roomInfo' | 'chat' | 'leaderboard' | 'statistics'; \ No newline at end of file +export type TabName = 'roomInfo' | 'chat' | 'leaderboard' | 'statistics' | 'settings'; \ No newline at end of file diff --git a/bsg-frontend/pnpm-lock.yaml b/bsg-frontend/pnpm-lock.yaml deleted file mode 100644 index f16f4a9c..00000000 --- a/bsg-frontend/pnpm-lock.yaml +++ /dev/null @@ -1,1294 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - firebase: - specifier: ^11.9.1 - version: 11.10.0 - react: - specifier: ^19.2.4 - version: 19.2.5 - react-dom: - specifier: ^19.2.4 - version: 19.2.5(react@19.2.5) - recharts: - specifier: ^3.8.1 - version: 3.8.1(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react-is@19.2.5)(react@19.2.5)(redux@5.0.1) - zustand: - specifier: ^5.0.11 - version: 5.0.12(@types/react@19.2.14)(immer@11.1.4)(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5)) - devDependencies: - '@types/chrome': - specifier: ^0.0.317 - version: 0.0.317 - '@types/react': - specifier: ^19.2.14 - version: 19.2.14 - '@types/react-dom': - specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.14) - -packages: - - '@firebase/ai@1.4.1': - resolution: {integrity: sha512-bcusQfA/tHjUjBTnMx6jdoPMpDl3r8K15Z+snHz9wq0Foox0F/V+kNLXucEOHoTL2hTc9l+onZCyBJs2QoIC3g==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app': 0.x - '@firebase/app-types': 0.x - - '@firebase/analytics-compat@0.2.23': - resolution: {integrity: sha512-3AdO10RN18G5AzREPoFgYhW6vWXr3u+OYQv6pl3CX6Fky8QRk0AHurZlY3Q1xkXO0TDxIsdhO3y65HF7PBOJDw==} - peerDependencies: - '@firebase/app-compat': 0.x - - '@firebase/analytics-types@0.8.3': - resolution: {integrity: sha512-VrIp/d8iq2g501qO46uGz3hjbDb8xzYMrbu8Tp0ovzIzrvJZ2fvmj649gTjge/b7cCCcjT0H37g1gVtlNhnkbg==} - - '@firebase/analytics@0.10.17': - resolution: {integrity: sha512-n5vfBbvzduMou/2cqsnKrIes4auaBjdhg8QNA2ZQZ59QgtO2QiwBaXQZQE4O4sgB0Ds1tvLgUUkY+pwzu6/xEg==} - peerDependencies: - '@firebase/app': 0.x - - '@firebase/app-check-compat@0.3.26': - resolution: {integrity: sha512-PkX+XJMLDea6nmnopzFKlr+s2LMQGqdyT2DHdbx1v1dPSqOol2YzgpgymmhC67vitXVpNvS3m/AiWQWWhhRRPQ==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app-compat': 0.x - - '@firebase/app-check-interop-types@0.3.3': - resolution: {integrity: sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==} - - '@firebase/app-check-types@0.5.3': - resolution: {integrity: sha512-hyl5rKSj0QmwPdsAxrI5x1otDlByQ7bvNvVt8G/XPO2CSwE++rmSVf3VEhaeOR4J8ZFaF0Z0NDSmLejPweZ3ng==} - - '@firebase/app-check@0.10.1': - resolution: {integrity: sha512-MgNdlms9Qb0oSny87pwpjKush9qUwCJhfmTJHDfrcKo4neLGiSeVE4qJkzP7EQTIUFKp84pbTxobSAXkiuQVYQ==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app': 0.x - - '@firebase/app-compat@0.4.2': - resolution: {integrity: sha512-LssbyKHlwLeiV8GBATyOyjmHcMpX/tFjzRUCS1jnwGAew1VsBB4fJowyS5Ud5LdFbYpJeS+IQoC+RQxpK7eH3Q==} - engines: {node: '>=18.0.0'} - - '@firebase/app-types@0.9.3': - resolution: {integrity: sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==} - - '@firebase/app@0.13.2': - resolution: {integrity: sha512-jwtMmJa1BXXDCiDx1vC6SFN/+HfYG53UkfJa6qeN5ogvOunzbFDO3wISZy5n9xgYFUrEP6M7e8EG++riHNTv9w==} - engines: {node: '>=18.0.0'} - - '@firebase/auth-compat@0.5.28': - resolution: {integrity: sha512-HpMSo/cc6Y8IX7bkRIaPPqT//Jt83iWy5rmDWeThXQCAImstkdNo3giFLORJwrZw2ptiGkOij64EH1ztNJzc7Q==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app-compat': 0.x - - '@firebase/auth-interop-types@0.2.4': - resolution: {integrity: sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==} - - '@firebase/auth-types@0.13.0': - resolution: {integrity: sha512-S/PuIjni0AQRLF+l9ck0YpsMOdE8GO2KU6ubmBB7P+7TJUCQDa3R1dlgYm9UzGbbePMZsp0xzB93f2b/CgxMOg==} - peerDependencies: - '@firebase/app-types': 0.x - '@firebase/util': 1.x - - '@firebase/auth@1.10.8': - resolution: {integrity: sha512-GpuTz5ap8zumr/ocnPY57ZanX02COsXloY6Y/2LYPAuXYiaJRf6BAGDEdRq1BMjP93kqQnKNuKZUTMZbQ8MNYA==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app': 0.x - '@react-native-async-storage/async-storage': ^1.18.1 - peerDependenciesMeta: - '@react-native-async-storage/async-storage': - optional: true - - '@firebase/component@0.6.18': - resolution: {integrity: sha512-n28kPCkE2dL2U28fSxZJjzPPVpKsQminJ6NrzcKXAI0E/lYC8YhfwpyllScqVEvAI3J2QgJZWYgrX+1qGI+SQQ==} - engines: {node: '>=18.0.0'} - - '@firebase/data-connect@0.3.10': - resolution: {integrity: sha512-VMVk7zxIkgwlVQIWHOKFahmleIjiVFwFOjmakXPd/LDgaB/5vzwsB5DWIYo+3KhGxWpidQlR8geCIn39YflJIQ==} - peerDependencies: - '@firebase/app': 0.x - - '@firebase/database-compat@2.0.11': - resolution: {integrity: sha512-itEsHARSsYS95+udF/TtIzNeQ0Uhx4uIna0sk4E0wQJBUnLc/G1X6D7oRljoOuwwCezRLGvWBRyNrugv/esOEw==} - engines: {node: '>=18.0.0'} - - '@firebase/database-types@1.0.15': - resolution: {integrity: sha512-XWHJ0VUJ0k2E9HDMlKxlgy/ZuTa9EvHCGLjaKSUvrQnwhgZuRU5N3yX6SZ+ftf2hTzZmfRkv+b3QRvGg40bKNw==} - - '@firebase/database@1.0.20': - resolution: {integrity: sha512-H9Rpj1pQ1yc9+4HQOotFGLxqAXwOzCHsRSRjcQFNOr8lhUt6LeYjf0NSRL04sc4X0dWe8DsCvYKxMYvFG/iOJw==} - engines: {node: '>=18.0.0'} - - '@firebase/firestore-compat@0.3.53': - resolution: {integrity: sha512-qI3yZL8ljwAYWrTousWYbemay2YZa+udLWugjdjju2KODWtLG94DfO4NALJgPLv8CVGcDHNFXoyQexdRA0Cz8Q==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app-compat': 0.x - - '@firebase/firestore-types@3.0.3': - resolution: {integrity: sha512-hD2jGdiWRxB/eZWF89xcK9gF8wvENDJkzpVFb4aGkzfEaKxVRD1kjz1t1Wj8VZEp2LCB53Yx1zD8mrhQu87R6Q==} - peerDependencies: - '@firebase/app-types': 0.x - '@firebase/util': 1.x - - '@firebase/firestore@4.8.0': - resolution: {integrity: sha512-QSRk+Q1/CaabKyqn3C32KSFiOdZpSqI9rpLK5BHPcooElumOBooPFa6YkDdiT+/KhJtel36LdAacha9BptMj2A==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app': 0.x - - '@firebase/functions-compat@0.3.26': - resolution: {integrity: sha512-A798/6ff5LcG2LTWqaGazbFYnjBW8zc65YfID/en83ALmkhu2b0G8ykvQnLtakbV9ajrMYPn7Yc/XcYsZIUsjA==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app-compat': 0.x - - '@firebase/functions-types@0.6.3': - resolution: {integrity: sha512-EZoDKQLUHFKNx6VLipQwrSMh01A1SaL3Wg6Hpi//x6/fJ6Ee4hrAeswK99I5Ht8roiniKHw4iO0B1Oxj5I4plg==} - - '@firebase/functions@0.12.9': - resolution: {integrity: sha512-FG95w6vjbUXN84Ehezc2SDjGmGq225UYbHrb/ptkRT7OTuCiQRErOQuyt1jI1tvcDekdNog+anIObihNFz79Lg==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app': 0.x - - '@firebase/installations-compat@0.2.18': - resolution: {integrity: sha512-aLFohRpJO5kKBL/XYL4tN+GdwEB/Q6Vo9eZOM/6Kic7asSUgmSfGPpGUZO1OAaSRGwF4Lqnvi1f/f9VZnKzChw==} - peerDependencies: - '@firebase/app-compat': 0.x - - '@firebase/installations-types@0.5.3': - resolution: {integrity: sha512-2FJI7gkLqIE0iYsNQ1P751lO3hER+Umykel+TkLwHj6plzWVxqvfclPUZhcKFVQObqloEBTmpi2Ozn7EkCABAA==} - peerDependencies: - '@firebase/app-types': 0.x - - '@firebase/installations@0.6.18': - resolution: {integrity: sha512-NQ86uGAcvO8nBRwVltRL9QQ4Reidc/3whdAasgeWCPIcrhOKDuNpAALa6eCVryLnK14ua2DqekCOX5uC9XbU/A==} - peerDependencies: - '@firebase/app': 0.x - - '@firebase/logger@0.4.4': - resolution: {integrity: sha512-mH0PEh1zoXGnaR8gD1DeGeNZtWFKbnz9hDO91dIml3iou1gpOnLqXQ2dJfB71dj6dpmUjcQ6phY3ZZJbjErr9g==} - engines: {node: '>=18.0.0'} - - '@firebase/messaging-compat@0.2.22': - resolution: {integrity: sha512-5ZHtRnj6YO6f/QPa/KU6gryjmX4Kg33Kn4gRpNU6M1K47Gm8kcQwPkX7erRUYEH1mIWptfvjvXMHWoZaWjkU7A==} - peerDependencies: - '@firebase/app-compat': 0.x - - '@firebase/messaging-interop-types@0.2.3': - resolution: {integrity: sha512-xfzFaJpzcmtDjycpDeCUj0Ge10ATFi/VHVIvEEjDNc3hodVBQADZ7BWQU7CuFpjSHE+eLuBI13z5F/9xOoGX8Q==} - - '@firebase/messaging@0.12.22': - resolution: {integrity: sha512-GJcrPLc+Hu7nk+XQ70Okt3M1u1eRr2ZvpMbzbc54oTPJZySHcX9ccZGVFcsZbSZ6o1uqumm8Oc7OFkD3Rn1/og==} - peerDependencies: - '@firebase/app': 0.x - - '@firebase/performance-compat@0.2.20': - resolution: {integrity: sha512-XkFK5NmOKCBuqOKWeRgBUFZZGz9SzdTZp4OqeUg+5nyjapTiZ4XoiiUL8z7mB2q+63rPmBl7msv682J3rcDXIQ==} - peerDependencies: - '@firebase/app-compat': 0.x - - '@firebase/performance-types@0.2.3': - resolution: {integrity: sha512-IgkyTz6QZVPAq8GSkLYJvwSLr3LS9+V6vNPQr0x4YozZJiLF5jYixj0amDtATf1X0EtYHqoPO48a9ija8GocxQ==} - - '@firebase/performance@0.7.7': - resolution: {integrity: sha512-JTlTQNZKAd4+Q5sodpw6CN+6NmwbY72av3Lb6wUKTsL7rb3cuBIhQSrslWbVz0SwK3x0ZNcqX24qtRbwKiv+6w==} - peerDependencies: - '@firebase/app': 0.x - - '@firebase/remote-config-compat@0.2.18': - resolution: {integrity: sha512-YiETpldhDy7zUrnS8e+3l7cNs0sL7+tVAxvVYU0lu7O+qLHbmdtAxmgY+wJqWdW2c9nDvBFec7QiF58pEUu0qQ==} - peerDependencies: - '@firebase/app-compat': 0.x - - '@firebase/remote-config-types@0.4.0': - resolution: {integrity: sha512-7p3mRE/ldCNYt8fmWMQ/MSGRmXYlJ15Rvs9Rk17t8p0WwZDbeK7eRmoI1tvCPaDzn9Oqh+yD6Lw+sGLsLg4kKg==} - - '@firebase/remote-config@0.6.5': - resolution: {integrity: sha512-fU0c8HY0vrVHwC+zQ/fpXSqHyDMuuuglV94VF6Yonhz8Fg2J+KOowPGANM0SZkLvVOYpTeWp3ZmM+F6NjwWLnw==} - peerDependencies: - '@firebase/app': 0.x - - '@firebase/storage-compat@0.3.24': - resolution: {integrity: sha512-XHn2tLniiP7BFKJaPZ0P8YQXKiVJX+bMyE2j2YWjYfaddqiJnROJYqSomwW6L3Y+gZAga35ONXUJQju6MB6SOQ==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app-compat': 0.x - - '@firebase/storage-types@0.8.3': - resolution: {integrity: sha512-+Muk7g9uwngTpd8xn9OdF/D48uiQ7I1Fae7ULsWPuKoCH3HU7bfFPhxtJYzyhjdniowhuDpQcfPmuNRAqZEfvg==} - peerDependencies: - '@firebase/app-types': 0.x - '@firebase/util': 1.x - - '@firebase/storage@0.13.14': - resolution: {integrity: sha512-xTq5ixxORzx+bfqCpsh+o3fxOsGoDjC1nO0Mq2+KsOcny3l7beyBhP/y1u5T6mgsFQwI1j6oAkbT5cWdDBx87g==} - engines: {node: '>=18.0.0'} - peerDependencies: - '@firebase/app': 0.x - - '@firebase/util@1.12.1': - resolution: {integrity: sha512-zGlBn/9Dnya5ta9bX/fgEoNC3Cp8s6h+uYPYaDieZsFOAdHP/ExzQ/eaDgxD3GOROdPkLKpvKY0iIzr9adle0w==} - engines: {node: '>=18.0.0'} - - '@firebase/webchannel-wrapper@1.0.3': - resolution: {integrity: sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==} - - '@grpc/grpc-js@1.9.15': - resolution: {integrity: sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==} - engines: {node: ^8.13.0 || >=10.10.0} - - '@grpc/proto-loader@0.7.15': - resolution: {integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==} - engines: {node: '>=6'} - hasBin: true - - '@protobufjs/aspromise@1.1.2': - resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} - - '@protobufjs/base64@1.1.2': - resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} - - '@protobufjs/codegen@2.0.4': - resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} - - '@protobufjs/eventemitter@1.1.0': - resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} - - '@protobufjs/fetch@1.1.0': - resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} - - '@protobufjs/float@1.0.2': - resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} - - '@protobufjs/inquire@1.1.0': - resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} - - '@protobufjs/path@1.1.2': - resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} - - '@protobufjs/pool@1.1.0': - resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} - - '@protobufjs/utf8@1.1.0': - resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} - - '@reduxjs/toolkit@2.11.2': - resolution: {integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==} - peerDependencies: - react: ^16.9.0 || ^17.0.0 || ^18 || ^19 - react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 - peerDependenciesMeta: - react: - optional: true - react-redux: - optional: true - - '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - - '@standard-schema/utils@0.3.0': - resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} - - '@types/chrome@0.0.317': - resolution: {integrity: sha512-ibKycbXX8ZZToFshjgWg98BTvFUSvQht8m53Xc+87ye3Z6ZoHJubLjoiDsil8rtW+noWE+Z0+7y0nwLxArU+CQ==} - - '@types/d3-array@3.2.2': - resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} - - '@types/d3-color@3.1.3': - resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} - - '@types/d3-ease@3.0.2': - resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} - - '@types/d3-interpolate@3.0.4': - resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} - - '@types/d3-path@3.1.1': - resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} - - '@types/d3-scale@4.0.9': - resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} - - '@types/d3-shape@3.1.8': - resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} - - '@types/d3-time@3.0.4': - resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} - - '@types/d3-timer@3.0.2': - resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} - - '@types/filesystem@0.0.36': - resolution: {integrity: sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==} - - '@types/filewriter@0.0.33': - resolution: {integrity: sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==} - - '@types/har-format@1.2.16': - resolution: {integrity: sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==} - - '@types/node@25.3.3': - resolution: {integrity: sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==} - - '@types/react-dom@19.2.3': - resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} - peerDependencies: - '@types/react': ^19.2.0 - - '@types/react@19.2.14': - resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} - - '@types/use-sync-external-store@0.0.6': - resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - - clsx@2.1.1: - resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} - engines: {node: '>=6'} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - csstype@3.2.3: - resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - - d3-array@3.2.4: - resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} - engines: {node: '>=12'} - - d3-color@3.1.0: - resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} - engines: {node: '>=12'} - - d3-ease@3.0.1: - resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} - engines: {node: '>=12'} - - d3-format@3.1.2: - resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} - engines: {node: '>=12'} - - d3-interpolate@3.0.1: - resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} - engines: {node: '>=12'} - - d3-path@3.1.0: - resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} - engines: {node: '>=12'} - - d3-scale@4.0.2: - resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} - engines: {node: '>=12'} - - d3-shape@3.2.0: - resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} - engines: {node: '>=12'} - - d3-time-format@4.1.0: - resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} - engines: {node: '>=12'} - - d3-time@3.1.0: - resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} - engines: {node: '>=12'} - - d3-timer@3.0.1: - resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} - engines: {node: '>=12'} - - decimal.js-light@2.5.1: - resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - es-toolkit@1.45.1: - resolution: {integrity: sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==} - - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - - eventemitter3@5.0.4: - resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} - - faye-websocket@0.11.4: - resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} - engines: {node: '>=0.8.0'} - - firebase@11.10.0: - resolution: {integrity: sha512-nKBXoDzF0DrXTBQJlZa+sbC5By99ysYU1D6PkMRYknm0nCW7rJly47q492Ht7Ndz5MeYSBuboKuhS1e6mFC03w==} - - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - http-parser-js@0.5.10: - resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==} - - idb@7.1.1: - resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} - - immer@10.2.0: - resolution: {integrity: sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==} - - immer@11.1.4: - resolution: {integrity: sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==} - - internmap@2.0.3: - resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} - engines: {node: '>=12'} - - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - lodash.camelcase@4.3.0: - resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} - - long@5.3.2: - resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} - - protobufjs@7.5.4: - resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} - engines: {node: '>=12.0.0'} - - react-dom@19.2.5: - resolution: {integrity: sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==} - peerDependencies: - react: ^19.2.5 - - react-is@19.2.5: - resolution: {integrity: sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ==} - - react-redux@9.2.0: - resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==} - peerDependencies: - '@types/react': ^18.2.25 || ^19 - react: ^18.0 || ^19 - redux: ^5.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - redux: - optional: true - - react@19.2.5: - resolution: {integrity: sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==} - engines: {node: '>=0.10.0'} - - recharts@3.8.1: - resolution: {integrity: sha512-mwzmO1s9sFL0TduUpwndxCUNoXsBw3u3E/0+A+cLcrSfQitSG62L32N69GhqUrrT5qKcAE3pCGVINC6pqkBBQg==} - engines: {node: '>=18'} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-is: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - redux-thunk@3.1.0: - resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} - peerDependencies: - redux: ^5.0.0 - - redux@5.0.1: - resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} - - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - reselect@5.1.1: - resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - scheduler@0.27.0: - resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - tiny-invariant@1.3.3: - resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - undici-types@7.18.2: - resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} - - use-sync-external-store@1.6.0: - resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - victory-vendor@37.3.6: - resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} - - web-vitals@4.2.4: - resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} - - websocket-driver@0.7.4: - resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} - engines: {node: '>=0.8.0'} - - websocket-extensions@0.1.4: - resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} - engines: {node: '>=0.8.0'} - - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - - zustand@5.0.12: - resolution: {integrity: sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==} - engines: {node: '>=12.20.0'} - peerDependencies: - '@types/react': '>=18.0.0' - immer: '>=9.0.6' - react: '>=18.0.0' - use-sync-external-store: '>=1.2.0' - peerDependenciesMeta: - '@types/react': - optional: true - immer: - optional: true - react: - optional: true - use-sync-external-store: - optional: true - -snapshots: - - '@firebase/ai@1.4.1(@firebase/app-types@0.9.3)(@firebase/app@0.13.2)': - dependencies: - '@firebase/app': 0.13.2 - '@firebase/app-check-interop-types': 0.3.3 - '@firebase/app-types': 0.9.3 - '@firebase/component': 0.6.18 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.12.1 - tslib: 2.8.1 - - '@firebase/analytics-compat@0.2.23(@firebase/app-compat@0.4.2)(@firebase/app@0.13.2)': - dependencies: - '@firebase/analytics': 0.10.17(@firebase/app@0.13.2) - '@firebase/analytics-types': 0.8.3 - '@firebase/app-compat': 0.4.2 - '@firebase/component': 0.6.18 - '@firebase/util': 1.12.1 - tslib: 2.8.1 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/analytics-types@0.8.3': {} - - '@firebase/analytics@0.10.17(@firebase/app@0.13.2)': - dependencies: - '@firebase/app': 0.13.2 - '@firebase/component': 0.6.18 - '@firebase/installations': 0.6.18(@firebase/app@0.13.2) - '@firebase/logger': 0.4.4 - '@firebase/util': 1.12.1 - tslib: 2.8.1 - - '@firebase/app-check-compat@0.3.26(@firebase/app-compat@0.4.2)(@firebase/app@0.13.2)': - dependencies: - '@firebase/app-check': 0.10.1(@firebase/app@0.13.2) - '@firebase/app-check-types': 0.5.3 - '@firebase/app-compat': 0.4.2 - '@firebase/component': 0.6.18 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.12.1 - tslib: 2.8.1 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/app-check-interop-types@0.3.3': {} - - '@firebase/app-check-types@0.5.3': {} - - '@firebase/app-check@0.10.1(@firebase/app@0.13.2)': - dependencies: - '@firebase/app': 0.13.2 - '@firebase/component': 0.6.18 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.12.1 - tslib: 2.8.1 - - '@firebase/app-compat@0.4.2': - dependencies: - '@firebase/app': 0.13.2 - '@firebase/component': 0.6.18 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.12.1 - tslib: 2.8.1 - - '@firebase/app-types@0.9.3': {} - - '@firebase/app@0.13.2': - dependencies: - '@firebase/component': 0.6.18 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.12.1 - idb: 7.1.1 - tslib: 2.8.1 - - '@firebase/auth-compat@0.5.28(@firebase/app-compat@0.4.2)(@firebase/app-types@0.9.3)(@firebase/app@0.13.2)': - dependencies: - '@firebase/app-compat': 0.4.2 - '@firebase/auth': 1.10.8(@firebase/app@0.13.2) - '@firebase/auth-types': 0.13.0(@firebase/app-types@0.9.3)(@firebase/util@1.12.1) - '@firebase/component': 0.6.18 - '@firebase/util': 1.12.1 - tslib: 2.8.1 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/app-types' - - '@react-native-async-storage/async-storage' - - '@firebase/auth-interop-types@0.2.4': {} - - '@firebase/auth-types@0.13.0(@firebase/app-types@0.9.3)(@firebase/util@1.12.1)': - dependencies: - '@firebase/app-types': 0.9.3 - '@firebase/util': 1.12.1 - - '@firebase/auth@1.10.8(@firebase/app@0.13.2)': - dependencies: - '@firebase/app': 0.13.2 - '@firebase/component': 0.6.18 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.12.1 - tslib: 2.8.1 - - '@firebase/component@0.6.18': - dependencies: - '@firebase/util': 1.12.1 - tslib: 2.8.1 - - '@firebase/data-connect@0.3.10(@firebase/app@0.13.2)': - dependencies: - '@firebase/app': 0.13.2 - '@firebase/auth-interop-types': 0.2.4 - '@firebase/component': 0.6.18 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.12.1 - tslib: 2.8.1 - - '@firebase/database-compat@2.0.11': - dependencies: - '@firebase/component': 0.6.18 - '@firebase/database': 1.0.20 - '@firebase/database-types': 1.0.15 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.12.1 - tslib: 2.8.1 - - '@firebase/database-types@1.0.15': - dependencies: - '@firebase/app-types': 0.9.3 - '@firebase/util': 1.12.1 - - '@firebase/database@1.0.20': - dependencies: - '@firebase/app-check-interop-types': 0.3.3 - '@firebase/auth-interop-types': 0.2.4 - '@firebase/component': 0.6.18 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.12.1 - faye-websocket: 0.11.4 - tslib: 2.8.1 - - '@firebase/firestore-compat@0.3.53(@firebase/app-compat@0.4.2)(@firebase/app-types@0.9.3)(@firebase/app@0.13.2)': - dependencies: - '@firebase/app-compat': 0.4.2 - '@firebase/component': 0.6.18 - '@firebase/firestore': 4.8.0(@firebase/app@0.13.2) - '@firebase/firestore-types': 3.0.3(@firebase/app-types@0.9.3)(@firebase/util@1.12.1) - '@firebase/util': 1.12.1 - tslib: 2.8.1 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/app-types' - - '@firebase/firestore-types@3.0.3(@firebase/app-types@0.9.3)(@firebase/util@1.12.1)': - dependencies: - '@firebase/app-types': 0.9.3 - '@firebase/util': 1.12.1 - - '@firebase/firestore@4.8.0(@firebase/app@0.13.2)': - dependencies: - '@firebase/app': 0.13.2 - '@firebase/component': 0.6.18 - '@firebase/logger': 0.4.4 - '@firebase/util': 1.12.1 - '@firebase/webchannel-wrapper': 1.0.3 - '@grpc/grpc-js': 1.9.15 - '@grpc/proto-loader': 0.7.15 - tslib: 2.8.1 - - '@firebase/functions-compat@0.3.26(@firebase/app-compat@0.4.2)(@firebase/app@0.13.2)': - dependencies: - '@firebase/app-compat': 0.4.2 - '@firebase/component': 0.6.18 - '@firebase/functions': 0.12.9(@firebase/app@0.13.2) - '@firebase/functions-types': 0.6.3 - '@firebase/util': 1.12.1 - tslib: 2.8.1 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/functions-types@0.6.3': {} - - '@firebase/functions@0.12.9(@firebase/app@0.13.2)': - dependencies: - '@firebase/app': 0.13.2 - '@firebase/app-check-interop-types': 0.3.3 - '@firebase/auth-interop-types': 0.2.4 - '@firebase/component': 0.6.18 - '@firebase/messaging-interop-types': 0.2.3 - '@firebase/util': 1.12.1 - tslib: 2.8.1 - - '@firebase/installations-compat@0.2.18(@firebase/app-compat@0.4.2)(@firebase/app-types@0.9.3)(@firebase/app@0.13.2)': - dependencies: - '@firebase/app-compat': 0.4.2 - '@firebase/component': 0.6.18 - '@firebase/installations': 0.6.18(@firebase/app@0.13.2) - '@firebase/installations-types': 0.5.3(@firebase/app-types@0.9.3) - '@firebase/util': 1.12.1 - tslib: 2.8.1 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/app-types' - - '@firebase/installations-types@0.5.3(@firebase/app-types@0.9.3)': - dependencies: - '@firebase/app-types': 0.9.3 - - '@firebase/installations@0.6.18(@firebase/app@0.13.2)': - dependencies: - '@firebase/app': 0.13.2 - '@firebase/component': 0.6.18 - '@firebase/util': 1.12.1 - idb: 7.1.1 - tslib: 2.8.1 - - '@firebase/logger@0.4.4': - dependencies: - tslib: 2.8.1 - - '@firebase/messaging-compat@0.2.22(@firebase/app-compat@0.4.2)(@firebase/app@0.13.2)': - dependencies: - '@firebase/app-compat': 0.4.2 - '@firebase/component': 0.6.18 - '@firebase/messaging': 0.12.22(@firebase/app@0.13.2) - '@firebase/util': 1.12.1 - tslib: 2.8.1 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/messaging-interop-types@0.2.3': {} - - '@firebase/messaging@0.12.22(@firebase/app@0.13.2)': - dependencies: - '@firebase/app': 0.13.2 - '@firebase/component': 0.6.18 - '@firebase/installations': 0.6.18(@firebase/app@0.13.2) - '@firebase/messaging-interop-types': 0.2.3 - '@firebase/util': 1.12.1 - idb: 7.1.1 - tslib: 2.8.1 - - '@firebase/performance-compat@0.2.20(@firebase/app-compat@0.4.2)(@firebase/app@0.13.2)': - dependencies: - '@firebase/app-compat': 0.4.2 - '@firebase/component': 0.6.18 - '@firebase/logger': 0.4.4 - '@firebase/performance': 0.7.7(@firebase/app@0.13.2) - '@firebase/performance-types': 0.2.3 - '@firebase/util': 1.12.1 - tslib: 2.8.1 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/performance-types@0.2.3': {} - - '@firebase/performance@0.7.7(@firebase/app@0.13.2)': - dependencies: - '@firebase/app': 0.13.2 - '@firebase/component': 0.6.18 - '@firebase/installations': 0.6.18(@firebase/app@0.13.2) - '@firebase/logger': 0.4.4 - '@firebase/util': 1.12.1 - tslib: 2.8.1 - web-vitals: 4.2.4 - - '@firebase/remote-config-compat@0.2.18(@firebase/app-compat@0.4.2)(@firebase/app@0.13.2)': - dependencies: - '@firebase/app-compat': 0.4.2 - '@firebase/component': 0.6.18 - '@firebase/logger': 0.4.4 - '@firebase/remote-config': 0.6.5(@firebase/app@0.13.2) - '@firebase/remote-config-types': 0.4.0 - '@firebase/util': 1.12.1 - tslib: 2.8.1 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/remote-config-types@0.4.0': {} - - '@firebase/remote-config@0.6.5(@firebase/app@0.13.2)': - dependencies: - '@firebase/app': 0.13.2 - '@firebase/component': 0.6.18 - '@firebase/installations': 0.6.18(@firebase/app@0.13.2) - '@firebase/logger': 0.4.4 - '@firebase/util': 1.12.1 - tslib: 2.8.1 - - '@firebase/storage-compat@0.3.24(@firebase/app-compat@0.4.2)(@firebase/app-types@0.9.3)(@firebase/app@0.13.2)': - dependencies: - '@firebase/app-compat': 0.4.2 - '@firebase/component': 0.6.18 - '@firebase/storage': 0.13.14(@firebase/app@0.13.2) - '@firebase/storage-types': 0.8.3(@firebase/app-types@0.9.3)(@firebase/util@1.12.1) - '@firebase/util': 1.12.1 - tslib: 2.8.1 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/app-types' - - '@firebase/storage-types@0.8.3(@firebase/app-types@0.9.3)(@firebase/util@1.12.1)': - dependencies: - '@firebase/app-types': 0.9.3 - '@firebase/util': 1.12.1 - - '@firebase/storage@0.13.14(@firebase/app@0.13.2)': - dependencies: - '@firebase/app': 0.13.2 - '@firebase/component': 0.6.18 - '@firebase/util': 1.12.1 - tslib: 2.8.1 - - '@firebase/util@1.12.1': - dependencies: - tslib: 2.8.1 - - '@firebase/webchannel-wrapper@1.0.3': {} - - '@grpc/grpc-js@1.9.15': - dependencies: - '@grpc/proto-loader': 0.7.15 - '@types/node': 25.3.3 - - '@grpc/proto-loader@0.7.15': - dependencies: - lodash.camelcase: 4.3.0 - long: 5.3.2 - protobufjs: 7.5.4 - yargs: 17.7.2 - - '@protobufjs/aspromise@1.1.2': {} - - '@protobufjs/base64@1.1.2': {} - - '@protobufjs/codegen@2.0.4': {} - - '@protobufjs/eventemitter@1.1.0': {} - - '@protobufjs/fetch@1.1.0': - dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/inquire': 1.1.0 - - '@protobufjs/float@1.0.2': {} - - '@protobufjs/inquire@1.1.0': {} - - '@protobufjs/path@1.1.2': {} - - '@protobufjs/pool@1.1.0': {} - - '@protobufjs/utf8@1.1.0': {} - - '@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.5)(redux@5.0.1))(react@19.2.5)': - dependencies: - '@standard-schema/spec': 1.1.0 - '@standard-schema/utils': 0.3.0 - immer: 11.1.4 - redux: 5.0.1 - redux-thunk: 3.1.0(redux@5.0.1) - reselect: 5.1.1 - optionalDependencies: - react: 19.2.5 - react-redux: 9.2.0(@types/react@19.2.14)(react@19.2.5)(redux@5.0.1) - - '@standard-schema/spec@1.1.0': {} - - '@standard-schema/utils@0.3.0': {} - - '@types/chrome@0.0.317': - dependencies: - '@types/filesystem': 0.0.36 - '@types/har-format': 1.2.16 - - '@types/d3-array@3.2.2': {} - - '@types/d3-color@3.1.3': {} - - '@types/d3-ease@3.0.2': {} - - '@types/d3-interpolate@3.0.4': - dependencies: - '@types/d3-color': 3.1.3 - - '@types/d3-path@3.1.1': {} - - '@types/d3-scale@4.0.9': - dependencies: - '@types/d3-time': 3.0.4 - - '@types/d3-shape@3.1.8': - dependencies: - '@types/d3-path': 3.1.1 - - '@types/d3-time@3.0.4': {} - - '@types/d3-timer@3.0.2': {} - - '@types/filesystem@0.0.36': - dependencies: - '@types/filewriter': 0.0.33 - - '@types/filewriter@0.0.33': {} - - '@types/har-format@1.2.16': {} - - '@types/node@25.3.3': - dependencies: - undici-types: 7.18.2 - - '@types/react-dom@19.2.3(@types/react@19.2.14)': - dependencies: - '@types/react': 19.2.14 - - '@types/react@19.2.14': - dependencies: - csstype: 3.2.3 - - '@types/use-sync-external-store@0.0.6': {} - - ansi-regex@5.0.1: {} - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - clsx@2.1.1: {} - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.4: {} - - csstype@3.2.3: {} - - d3-array@3.2.4: - dependencies: - internmap: 2.0.3 - - d3-color@3.1.0: {} - - d3-ease@3.0.1: {} - - d3-format@3.1.2: {} - - d3-interpolate@3.0.1: - dependencies: - d3-color: 3.1.0 - - d3-path@3.1.0: {} - - d3-scale@4.0.2: - dependencies: - d3-array: 3.2.4 - d3-format: 3.1.2 - d3-interpolate: 3.0.1 - d3-time: 3.1.0 - d3-time-format: 4.1.0 - - d3-shape@3.2.0: - dependencies: - d3-path: 3.1.0 - - d3-time-format@4.1.0: - dependencies: - d3-time: 3.1.0 - - d3-time@3.1.0: - dependencies: - d3-array: 3.2.4 - - d3-timer@3.0.1: {} - - decimal.js-light@2.5.1: {} - - emoji-regex@8.0.0: {} - - es-toolkit@1.45.1: {} - - escalade@3.2.0: {} - - eventemitter3@5.0.4: {} - - faye-websocket@0.11.4: - dependencies: - websocket-driver: 0.7.4 - - firebase@11.10.0: - dependencies: - '@firebase/ai': 1.4.1(@firebase/app-types@0.9.3)(@firebase/app@0.13.2) - '@firebase/analytics': 0.10.17(@firebase/app@0.13.2) - '@firebase/analytics-compat': 0.2.23(@firebase/app-compat@0.4.2)(@firebase/app@0.13.2) - '@firebase/app': 0.13.2 - '@firebase/app-check': 0.10.1(@firebase/app@0.13.2) - '@firebase/app-check-compat': 0.3.26(@firebase/app-compat@0.4.2)(@firebase/app@0.13.2) - '@firebase/app-compat': 0.4.2 - '@firebase/app-types': 0.9.3 - '@firebase/auth': 1.10.8(@firebase/app@0.13.2) - '@firebase/auth-compat': 0.5.28(@firebase/app-compat@0.4.2)(@firebase/app-types@0.9.3)(@firebase/app@0.13.2) - '@firebase/data-connect': 0.3.10(@firebase/app@0.13.2) - '@firebase/database': 1.0.20 - '@firebase/database-compat': 2.0.11 - '@firebase/firestore': 4.8.0(@firebase/app@0.13.2) - '@firebase/firestore-compat': 0.3.53(@firebase/app-compat@0.4.2)(@firebase/app-types@0.9.3)(@firebase/app@0.13.2) - '@firebase/functions': 0.12.9(@firebase/app@0.13.2) - '@firebase/functions-compat': 0.3.26(@firebase/app-compat@0.4.2)(@firebase/app@0.13.2) - '@firebase/installations': 0.6.18(@firebase/app@0.13.2) - '@firebase/installations-compat': 0.2.18(@firebase/app-compat@0.4.2)(@firebase/app-types@0.9.3)(@firebase/app@0.13.2) - '@firebase/messaging': 0.12.22(@firebase/app@0.13.2) - '@firebase/messaging-compat': 0.2.22(@firebase/app-compat@0.4.2)(@firebase/app@0.13.2) - '@firebase/performance': 0.7.7(@firebase/app@0.13.2) - '@firebase/performance-compat': 0.2.20(@firebase/app-compat@0.4.2)(@firebase/app@0.13.2) - '@firebase/remote-config': 0.6.5(@firebase/app@0.13.2) - '@firebase/remote-config-compat': 0.2.18(@firebase/app-compat@0.4.2)(@firebase/app@0.13.2) - '@firebase/storage': 0.13.14(@firebase/app@0.13.2) - '@firebase/storage-compat': 0.3.24(@firebase/app-compat@0.4.2)(@firebase/app-types@0.9.3)(@firebase/app@0.13.2) - '@firebase/util': 1.12.1 - transitivePeerDependencies: - - '@react-native-async-storage/async-storage' - - get-caller-file@2.0.5: {} - - http-parser-js@0.5.10: {} - - idb@7.1.1: {} - - immer@10.2.0: {} - - immer@11.1.4: {} - - internmap@2.0.3: {} - - is-fullwidth-code-point@3.0.0: {} - - lodash.camelcase@4.3.0: {} - - long@5.3.2: {} - - protobufjs@7.5.4: - dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/base64': 1.1.2 - '@protobufjs/codegen': 2.0.4 - '@protobufjs/eventemitter': 1.1.0 - '@protobufjs/fetch': 1.1.0 - '@protobufjs/float': 1.0.2 - '@protobufjs/inquire': 1.1.0 - '@protobufjs/path': 1.1.2 - '@protobufjs/pool': 1.1.0 - '@protobufjs/utf8': 1.1.0 - '@types/node': 25.3.3 - long: 5.3.2 - - react-dom@19.2.5(react@19.2.5): - dependencies: - react: 19.2.5 - scheduler: 0.27.0 - - react-is@19.2.5: {} - - react-redux@9.2.0(@types/react@19.2.14)(react@19.2.5)(redux@5.0.1): - dependencies: - '@types/use-sync-external-store': 0.0.6 - react: 19.2.5 - use-sync-external-store: 1.6.0(react@19.2.5) - optionalDependencies: - '@types/react': 19.2.14 - redux: 5.0.1 - - react@19.2.5: {} - - recharts@3.8.1(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react-is@19.2.5)(react@19.2.5)(redux@5.0.1): - dependencies: - '@reduxjs/toolkit': 2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.5)(redux@5.0.1))(react@19.2.5) - clsx: 2.1.1 - decimal.js-light: 2.5.1 - es-toolkit: 1.45.1 - eventemitter3: 5.0.4 - immer: 10.2.0 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) - react-is: 19.2.5 - react-redux: 9.2.0(@types/react@19.2.14)(react@19.2.5)(redux@5.0.1) - reselect: 5.1.1 - tiny-invariant: 1.3.3 - use-sync-external-store: 1.6.0(react@19.2.5) - victory-vendor: 37.3.6 - transitivePeerDependencies: - - '@types/react' - - redux - - redux-thunk@3.1.0(redux@5.0.1): - dependencies: - redux: 5.0.1 - - redux@5.0.1: {} - - require-directory@2.1.1: {} - - reselect@5.1.1: {} - - safe-buffer@5.2.1: {} - - scheduler@0.27.0: {} - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - tiny-invariant@1.3.3: {} - - tslib@2.8.1: {} - - undici-types@7.18.2: {} - - use-sync-external-store@1.6.0(react@19.2.5): - dependencies: - react: 19.2.5 - - victory-vendor@37.3.6: - dependencies: - '@types/d3-array': 3.2.2 - '@types/d3-ease': 3.0.2 - '@types/d3-interpolate': 3.0.4 - '@types/d3-scale': 4.0.9 - '@types/d3-shape': 3.1.8 - '@types/d3-time': 3.0.4 - '@types/d3-timer': 3.0.2 - d3-array: 3.2.4 - d3-ease: 3.0.1 - d3-interpolate: 3.0.1 - d3-scale: 4.0.2 - d3-shape: 3.2.0 - d3-time: 3.1.0 - d3-timer: 3.0.1 - - web-vitals@4.2.4: {} - - websocket-driver@0.7.4: - dependencies: - http-parser-js: 0.5.10 - safe-buffer: 5.2.1 - websocket-extensions: 0.1.4 - - websocket-extensions@0.1.4: {} - - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - y18n@5.0.8: {} - - yargs-parser@21.1.1: {} - - yargs@17.7.2: - dependencies: - cliui: 8.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - - zustand@5.0.12(@types/react@19.2.14)(immer@11.1.4)(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5)): - optionalDependencies: - '@types/react': 19.2.14 - immer: 11.1.4 - react: 19.2.5 - use-sync-external-store: 1.6.0(react@19.2.5) diff --git a/central-service/controllers/room.go b/central-service/controllers/room.go index d00f6716..d97d86ff 100644 --- a/central-service/controllers/room.go +++ b/central-service/controllers/room.go @@ -165,6 +165,36 @@ func (controller *RoomController) StartRoundEndpoint(c echo.Context) error { }) } +func (controller *RoomController) UpdateRoundDurationEndpoint(c echo.Context) error { + targetRoomID := c.Param("roomID") + userAuthID := c.Get("userAuthID").(string) + var body struct { + Duration int `json:"duration"` + } + if err := c.Bind(&body); err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Invalid data. Please try again") + } + newDuration, err := controller.roomService.SetRoundDurationByRoomID(targetRoomID, userAuthID, body.Duration) + if err != nil { + controller.logger.Error("Failed to update round duration", err, map[string]interface{}{ + "room_id": targetRoomID, + "user_id": userAuthID, + }) + if bsgErr, ok := err.(services.BSGError); ok { + return echo.NewHTTPError(bsgErr.StatusCode, "Failed to update timer. "+bsgErr.Message) + } + if bsgErr, ok := err.(*services.BSGError); ok { + return echo.NewHTTPError(bsgErr.StatusCode, "Failed to update timer. "+bsgErr.Message) + } + return echo.NewHTTPError(http.StatusInternalServerError, "Failed to update timer. Please try again later") + } + return c.JSON(http.StatusOK, map[string]interface{}{ + "data": map[string]interface{}{ + "duration": newDuration, + }, + }) +} + func (controller *RoomController) GetLeaderboardEndpoint(c echo.Context) error { roomID := c.Param("roomID") leaderboard, err := controller.roomService.GetLeaderboard(roomID) @@ -256,6 +286,7 @@ func (controller *RoomController) InitializeRoutes(g *echo.Group) { g.POST("/:roomID/join", controller.JoinRoomEndpoint) g.POST("/:roomID/leave", controller.LeaveRoomEndpoint) g.POST("/:roomID/rounds/create", controller.CreateNewRoundEndpoint) + g.POST("/:roomID/rounds/time", controller.UpdateRoundDurationEndpoint) g.POST("/:roomID/start", controller.StartRoundEndpoint) g.POST("/:roomID/end", controller.EndRoundEndpoint) g.POST("/:roomID/:problemID", controller.CreateSubmissionEndpoint) diff --git a/central-service/services/room.go b/central-service/services/room.go index fcc33f90..45112a65 100644 --- a/central-service/services/room.go +++ b/central-service/services/room.go @@ -502,6 +502,29 @@ func (service *RoomService) StartRoundByRoomID(roomID string, userID string) (*t return roundStartTime, problems, nil } +// SetRoundDurationByRoomID updates the duration of the room's not-yet-started round. +// Reserved for the room admin, and only valid while a round is in the CREATED state. +func (service *RoomService) SetRoundDurationByRoomID(roomID string, userID string, duration int) (int, error) { + room, err := service.FindRoomByID(roomID) + if err != nil { + return 0, err + } + if room.Admin != userID { // check if user is room admin + return 0, BSGError{http.StatusUnauthorized, "User is not room admin. This functionality is reserved for room admin..."} + } + var round *models.Round + for i := range room.Rounds { + if room.Rounds[i].Status == constants.ROUND_CREATED { + round = &room.Rounds[i] + break + } + } + if round == nil { + return 0, BSGError{http.StatusNotFound, "No round to edit. The timer can only be changed before the round starts."} + } + return service.roundService.UpdateRoundDuration(round, duration) +} + func (service *RoomService) GetLeaderboard(roomID string) ([]redis.Z, error) { return service.roundService.GetLeaderboardByRoomID(roomID) } diff --git a/central-service/services/round.go b/central-service/services/round.go index b004ffe3..4706cc81 100644 --- a/central-service/services/round.go +++ b/central-service/services/round.go @@ -251,6 +251,42 @@ func (service *RoundService) InitiateRoundStart(round *models.Round, activeRoomP return &roundStartTime, round.ProblemSet, nil } +// Allowed round duration range (minutes), matching the create-round Slider in the UI. +const ( + MIN_ROUND_DURATION = 5 + MAX_ROUND_DURATION = 120 +) + +// UpdateRoundDuration sets the duration (in minutes) of a round that has not started yet. +// The timer only begins on round start, so before then this is a plain column update that +// the eventual InitiateRoundStart will read — nothing needs to be rescheduled or broadcast. +func (service *RoundService) UpdateRoundDuration(round *models.Round, newDuration int) (int, error) { + if round == nil { + return 0, &BSGError{ + Message: "Round not found", + StatusCode: 404, + } + } + if round.Status != constants.ROUND_CREATED { + return 0, &BSGError{ + Message: "Timer can only be edited before the round starts", + StatusCode: 400, + } + } + if newDuration < MIN_ROUND_DURATION || newDuration > MAX_ROUND_DURATION { + return 0, &BSGError{ + Message: fmt.Sprintf("Duration must be between %d and %d minutes", MIN_ROUND_DURATION, MAX_ROUND_DURATION), + StatusCode: 400, + } + } + // Explicit column update: GORM's Updates(struct) skips zero values, Update does not. + if err := service.db.Model(round).Update("duration", newDuration).Error; err != nil { + return 0, err + } + round.Duration = newDuration + return newDuration, nil +} + // Adds a user to a round leaderboard // Member values are a compression of the user's score and last submission timestamp // User's initial score will be 0 with the current timestamp diff --git a/docker-compose.yml b/docker-compose.yml index c229ab44..dc564885 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -156,4 +156,4 @@ volumes: zookeeper-data-volume: zookeeper-log-volume: redisinsight-volume: - kafka-data-volume: + kafka-data-volume: \ No newline at end of file diff --git a/server/package-lock.json b/server/package-lock.json index 8f880ef4..9e3b2aed 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -53,7 +53,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1067,7 +1066,6 @@ "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.10.0.tgz", "integrity": "sha512-JXmM4XCoso6C75Mr3lhKA3eNxSzkYi3nCzxDIKY+YOszYsJjuKbFgVtguVPbLMOttN4iu2fXoc2BGhdnYhIOxA==", "license": "MIT", - "peer": true, "dependencies": { "cluster-key-slot": "1.1.2" }, @@ -1822,7 +1820,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -2610,7 +2607,6 @@ "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.2.tgz", "integrity": "sha512-SZjssGQC7TzTs9rpPDuUrR23GNZ9+2+IkA/+IJWmvQilTr5OSliEHGF+D9scbIpdC6yGtTI0/VhaHoVes2AN/A==", "license": "MIT", - "peer": true, "dependencies": { "cookie": "0.7.2", "cookie-signature": "1.0.7", @@ -4778,7 +4774,6 @@ "resolved": "https://registry.npmjs.org/redis/-/redis-5.10.0.tgz", "integrity": "sha512-0/Y+7IEiTgVGPrLFKy8oAEArSyEJkU0zvgV5xyi9NzNQ+SLZmyFbUsWIbgPcd4UdUh00opXGKlXJwMmsis5Byw==", "license": "MIT", - "peer": true, "dependencies": { "@redis/bloom": "5.10.0", "@redis/client": "5.10.0", diff --git a/server/src/routes/rooms.js b/server/src/routes/rooms.js index 4b01718d..083c66e0 100644 --- a/server/src/routes/rooms.js +++ b/server/src/routes/rooms.js @@ -71,6 +71,31 @@ router.post('/:id/rounds/create', ensureAuth, async (req, res) => { } }); +// Update Round Timer (pre-start duration edit) +router.post('/:id/rounds/time', ensureAuth, async (req, res) => { + const authID = req.user.id; + const { id } = req.params; + try { + const response = await fetch(`${centralServiceUrl}/api/rooms/${id}/rounds/time`, { + method: 'POST', + body: JSON.stringify(req.body), + headers: { + 'Content-Type': 'application/json', + 'X-Server-Secret': serverSecret, + 'X-User-Auth-ID': authID + } + }); + const data = await response.json(); + res.status(response.status).json(data); + } catch (error) { + logger.error('Error updating round timer', error, { + user_id: authID, + room_id: id, + }); + res.status(500).json({ error: 'Internal server error' }); + } +}); + // Start Round router.post('/:id/start', ensureAuth, async (req, res) => { const authID = req.user.id; diff --git a/worker-service/.DS_Store b/worker-service/.DS_Store deleted file mode 100644 index 893dff2e..00000000 Binary files a/worker-service/.DS_Store and /dev/null differ