diff --git a/.gitignore b/.gitignore index 8e70e44..aff770a 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ Gemfile expo-env.d.ts # @end expo-cli +.dual-graph/ diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..b64498e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,81 @@ + +# Dual-Graph Context Policy + +This project uses a local dual-graph MCP server for efficient context retrieval. + +## MANDATORY: Always follow this order + +1. **Call `graph_continue` first** — before any file exploration, grep, or code reading. + +2. **If `graph_continue` returns `needs_project=true`**: call `graph_scan` with the + current project directory (`pwd`). Do NOT ask the user. + +3. **If `graph_continue` returns `skip=true`**: project has fewer than 5 files. + Do NOT do broad or recursive exploration. Read only specific files if their names + are mentioned, or ask the user what to work on. + +4. **Read `recommended_files`** using `graph_read` — **one call per file**. + - `graph_read` accepts a single `file` parameter (string). Call it separately for each + recommended file. Do NOT pass an array or batch multiple files into one call. + - `recommended_files` may contain `file::symbol` entries (e.g. `src/auth.ts::handleLogin`). + Pass them verbatim to `graph_read(file: "src/auth.ts::handleLogin")` — it reads only + that symbol's lines, not the full file. + - Example: if `recommended_files` is `["src/auth.ts::handleLogin", "src/db.ts"]`, + call `graph_read(file: "src/auth.ts::handleLogin")` and `graph_read(file: "src/db.ts")` + as two separate calls (they can be parallel). + +5. **Check `confidence` and obey the caps strictly:** + - `confidence=high` -> Stop. Do NOT grep or explore further. + - `confidence=medium` -> If recommended files are insufficient, call `fallback_rg` + at most `max_supplementary_greps` time(s) with specific terms, then `graph_read` + at most `max_supplementary_files` additional file(s). Then stop. + - `confidence=low` -> Call `fallback_rg` at most `max_supplementary_greps` time(s), + then `graph_read` at most `max_supplementary_files` file(s). Then stop. + +## Token Usage + +A `token-counter` MCP is available for tracking live token usage. + +- To check how many tokens a large file or text will cost **before** reading it: + `count_tokens({text: ""})` +- To log actual usage after a task completes (if the user asks): + `log_usage({input_tokens: , output_tokens: , description: ""})` +- To show the user their running session cost: + `get_session_stats()` + +Live dashboard URL is printed at startup next to "Token usage". + +## Rules + +- Do NOT use `rg`, `grep`, or bash file exploration before calling `graph_continue`. +- Do NOT do broad/recursive exploration at any confidence level. +- `max_supplementary_greps` and `max_supplementary_files` are hard caps - never exceed them. +- Do NOT dump full chat history. +- Do NOT call `graph_retrieve` more than once per turn. +- After edits, call `graph_register_edit` with the changed files. Use `file::symbol` notation (e.g. `src/auth.ts::handleLogin`) when the edit targets a specific function, class, or hook. + +## Context Store + +Whenever you make a decision, identify a task, note a next step, fact, or blocker during a conversation, call `graph_add_memory`. + +**To add an entry:** +``` +graph_add_memory(type="decision|task|next|fact|blocker", content="one sentence max 15 words", tags=["topic"], files=["relevant/file.ts"]) +``` + +**Do NOT write context-store.json directly** — always use `graph_add_memory`. It applies pruning and keeps the store healthy. + +**Rules:** +- Only log things worth remembering across sessions (not every minor detail) +- `content` must be under 15 words +- `files` lists the files this decision/task relates to (can be empty) +- Log immediately when the item arises — not at session end + +## Session End + +When the user signals they are done (e.g. "bye", "done", "wrap up", "end session"), proactively update `CONTEXT.md` in the project root with: +- **Current Task**: one sentence on what was being worked on +- **Key Decisions**: bullet list, max 3 items +- **Next Steps**: bullet list, max 3 items + +Keep `CONTEXT.md` under 20 lines total. Do NOT summarize the full conversation — only what's needed to resume next session. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..9e5c3e7 --- /dev/null +++ b/NOTICE @@ -0,0 +1,4 @@ +Resgrid Dispatch +Copyright 2026 Resgrid, LLC + +This product includes software developed for the Resgrid project. \ No newline at end of file diff --git a/__mocks__/@gorhom/bottom-sheet.web.js b/__mocks__/@gorhom/bottom-sheet.web.js new file mode 100644 index 0000000..cd48cde --- /dev/null +++ b/__mocks__/@gorhom/bottom-sheet.web.js @@ -0,0 +1,117 @@ +/** + * Web-safe mock for @gorhom/bottom-sheet. + * The real package depends on react-native-reanimated worklets which are unavailable on web. + * This mock respects index/-1 visibility, onChange callbacks, and ref methods + * so modals open/close correctly on web. + */ +const React = require('react'); +const { View, StyleSheet, Pressable, Modal } = require('react-native'); + +const BottomSheet = React.forwardRef(function BottomSheet(props, ref) { + const { index = -1, children, onChange, snapPoints, backdropComponent, enablePanDownToClose } = props; + const [currentIndex, setCurrentIndex] = React.useState(index); + + React.useEffect(() => { + setCurrentIndex(index); + }, [index]); + + React.useImperativeHandle(ref, () => ({ + expand: () => { + setCurrentIndex(0); + if (onChange) onChange(0); + }, + collapse: () => { + setCurrentIndex(0); + if (onChange) onChange(0); + }, + close: () => { + setCurrentIndex(-1); + if (onChange) onChange(-1); + }, + snapToIndex: (i) => { + setCurrentIndex(i); + if (onChange) onChange(i); + }, + snapToPosition: () => {}, + forceClose: () => { + setCurrentIndex(-1); + if (onChange) onChange(-1); + }, + })); + + const isVisible = currentIndex >= 0; + if (!isVisible) return null; + + const handleBackdropPress = () => { + if (enablePanDownToClose !== false) { + setCurrentIndex(-1); + if (onChange) onChange(-1); + } + }; + + return React.createElement( + Modal, + { visible: true, transparent: true, animationType: 'slide', onRequestClose: handleBackdropPress }, + React.createElement( + View, + { style: styles.overlay }, + React.createElement(Pressable, { style: styles.backdrop, onPress: handleBackdropPress }), + React.createElement( + View, + { style: styles.sheet }, + children + ) + ) + ); +}); + +const BottomSheetView = function BottomSheetView(props) { + return React.createElement(View, { style: props.style }, props.children); +}; + +const BottomSheetScrollView = function BottomSheetScrollView(props) { + const { ScrollView } = require('react-native'); + return React.createElement(ScrollView, props, props.children); +}; + +const BottomSheetBackdrop = function BottomSheetBackdrop() { + return null; +}; + +const BottomSheetModalProvider = function BottomSheetModalProvider(props) { + return props.children || null; +}; + +const BottomSheetModal = React.forwardRef(function BottomSheetModal(props, ref) { + return React.createElement(BottomSheet, Object.assign({}, props, { ref: ref })); +}); + +const styles = StyleSheet.create({ + overlay: { + flex: 1, + justifyContent: 'flex-end', + }, + backdrop: { + ...StyleSheet.absoluteFillObject, + backgroundColor: 'rgba(0,0,0,0.4)', + }, + sheet: { + backgroundColor: '#fff', + borderTopLeftRadius: 16, + borderTopRightRadius: 16, + maxHeight: '70%', + minHeight: 200, + overflow: 'hidden', + }, +}); + +module.exports = { + __esModule: true, + default: BottomSheet, + BottomSheet, + BottomSheetView, + BottomSheetScrollView, + BottomSheetBackdrop, + BottomSheetModalProvider, + BottomSheetModal, +}; diff --git a/metro.config.js b/metro.config.js index b30288b..f8c0c68 100644 --- a/metro.config.js +++ b/metro.config.js @@ -3,9 +3,6 @@ const _ = require('lodash'); const path = require('path'); const { getSentryExpoConfig } = require('@sentry/react-native/metro'); -const exclusionList = require('metro-config/src/defaults/exclusionList'); -//const { getDefaultConfig } = require('expo/metro-config'); -//const path = require('path'); const { withNativeWind } = require('nativewind/metro'); const config = getSentryExpoConfig(__dirname, { @@ -14,9 +11,11 @@ const config = getSentryExpoConfig(__dirname, { // Exclude electron directory from Metro bundler for Android/iOS // Electron files use Node.js APIs that don't exist in React Native -config.resolver.blockList = exclusionList([ - /electron\/.*/, -]); +const existingBlockList = config.resolver.blockList; +const extraBlocked = [/electron\/.*/]; +config.resolver.blockList = existingBlockList + ? [...(Array.isArray(existingBlockList) ? existingBlockList : [existingBlockList]), ...extraBlocked] + : extraBlocked; // 1. Watch all files within the monorepo // 2. Let Metro know where to resolve packages and in what order @@ -48,6 +47,21 @@ config.resolver.resolveRequest = (context, moduleName, platform) => { }; } + // @gorhom/bottom-sheet depends on reanimated worklets - not available on web + if (moduleName === '@gorhom/bottom-sheet') { + return { + type: 'sourceFile', + filePath: path.resolve(__dirname, '__mocks__/@gorhom/bottom-sheet.web.js'), + }; + } + + // NetInfo - not needed on web, use navigator.onLine instead + if (moduleName === '@react-native-community/netinfo') { + return { + type: 'empty', + }; + } + // expo-keep-awake mock for web if (moduleName === 'expo-keep-awake') { return { diff --git a/package.json b/package.json index c62ae30..460eb61 100644 --- a/package.json +++ b/package.json @@ -90,9 +90,9 @@ "@config-plugins/react-native-callkeep": "^11.0.0", "@config-plugins/react-native-webrtc": "~12.0.0", "@dev-plugins/react-query": "~0.2.0", - "@expo/config-plugins": "~10.1.1", + "@expo/config-plugins": "~54.0.4", "@expo/html-elements": "~0.10.1", - "@expo/metro-runtime": "~5.0.5", + "@expo/metro-runtime": "~6.1.2", "@gluestack-ui/accordion": "~1.0.6", "@gluestack-ui/actionsheet": "~0.2.44", "@gluestack-ui/alert": "~0.1.15", @@ -137,8 +137,8 @@ "@react-native-community/netinfo": "^11.4.1", "@rnmapbox/maps": "10.1.42-rc.0", "@semantic-release/git": "^10.0.1", - "@sentry/react-native": "~6.14.0", - "@shopify/flash-list": "1.7.6", + "@sentry/react-native": "~7.2.0", + "@shopify/flash-list": "2.0.2", "@tanstack/react-query": "~5.52.1", "app-icon-badge": "^0.1.2", "axios": "~1.12.0", @@ -147,37 +147,37 @@ "countly-sdk-react-native-bridge": "^25.4.0", "date-fns": "^4.1.0", "dompurify": "^3.3.1", - "expo": "~53.0.23", - "expo-application": "~6.1.5", - "expo-asset": "~11.1.7", - "expo-audio": "~0.4.9", - "expo-auth-session": "~6.2.1", - "expo-av": "~15.1.7", - "expo-build-properties": "~0.14.8", - "expo-constants": "~17.1.7", - "expo-crypto": "~14.1.5", - "expo-dev-client": "~5.2.4", - "expo-device": "~7.1.4", - "expo-document-picker": "~13.1.6", - "expo-file-system": "~18.1.11", - "expo-font": "~13.3.2", - "expo-image": "~2.4.0", - "expo-image-manipulator": "~13.1.7", - "expo-image-picker": "~16.1.4", - "expo-keep-awake": "~14.1.4", - "expo-linking": "~7.1.7", - "expo-localization": "~16.1.6", - "expo-location": "~18.1.6", - "expo-navigation-bar": "~4.2.8", - "expo-notifications": "~0.31.4", - "expo-router": "~5.1.7", - "expo-screen-orientation": "~8.1.7", - "expo-sharing": "~13.1.5", - "expo-splash-screen": "~0.30.10", - "expo-status-bar": "~2.2.3", - "expo-system-ui": "~5.0.11", - "expo-task-manager": "~13.1.6", - "expo-web-browser": "~14.2.0", + "expo": "^54.0.33", + "expo-application": "~7.0.8", + "expo-asset": "~12.0.12", + "expo-audio": "~1.1.1", + "expo-auth-session": "~7.0.10", + "expo-av": "~16.0.8", + "expo-build-properties": "~1.0.10", + "expo-constants": "~18.0.13", + "expo-crypto": "~15.0.8", + "expo-dev-client": "~6.0.20", + "expo-device": "~8.0.10", + "expo-document-picker": "~14.0.8", + "expo-file-system": "~19.0.21", + "expo-font": "~14.0.11", + "expo-image": "~3.0.11", + "expo-image-manipulator": "~14.0.8", + "expo-image-picker": "~17.0.10", + "expo-keep-awake": "~15.0.8", + "expo-linking": "~8.0.11", + "expo-localization": "~17.0.8", + "expo-location": "~19.0.8", + "expo-navigation-bar": "~5.0.10", + "expo-notifications": "~0.32.16", + "expo-router": "~6.0.23", + "expo-screen-orientation": "~9.0.8", + "expo-sharing": "~14.0.8", + "expo-splash-screen": "~31.0.13", + "expo-status-bar": "~3.0.9", + "expo-system-ui": "~6.0.9", + "expo-task-manager": "~14.0.9", + "expo-web-browser": "~15.0.10", "geojson": "~0.5.0", "he": "^1.2.0", "i18next": "~23.14.0", @@ -189,28 +189,29 @@ "mapbox-gl": "^3.15.0", "moti": "~0.29.0", "nativewind": "~4.1.21", - "react": "19.0.0", - "react-dom": "19.0.0", + "react": "19.1.0", + "react-dom": "19.1.0", "react-error-boundary": "~4.0.13", "react-hook-form": "~7.53.0", "react-i18next": "~15.0.1", - "react-native": "0.79.6", + "react-native": "0.81.5", "react-native-base64": "~0.2.1", "react-native-ble-manager": "^12.1.5", "react-native-callkeep": "github:Irfanwani/react-native-callkeep#957193d0716f1c2dfdc18e627cbff0f8a0800971", "react-native-edge-to-edge": "1.6.0", "react-native-flash-message": "~0.4.2", - "react-native-gesture-handler": "~2.24.0", - "react-native-keyboard-controller": "^1.18.6", + "react-native-gesture-handler": "~2.28.0", + "react-native-keyboard-controller": "1.18.5", "react-native-logs": "~5.3.0", "react-native-mmkv": "~3.1.0", - "react-native-reanimated": "~3.17.4", + "react-native-reanimated": "~4.1.1", "react-native-restart": "0.0.27", - "react-native-safe-area-context": "5.4.0", - "react-native-screens": "~4.11.1", - "react-native-svg": "15.11.2", - "react-native-web": "^0.20.0", - "react-native-webview": "~13.13.1", + "react-native-safe-area-context": "~5.6.0", + "react-native-screens": "~4.16.0", + "react-native-svg": "15.12.1", + "react-native-web": "^0.21.0", + "react-native-webview": "13.15.0", + "react-native-worklets": "0.5.1", "react-query-kit": "~3.3.0", "tailwind-variants": "~0.2.1", "zod": "~3.23.8", @@ -220,7 +221,7 @@ "@babel/core": "~7.26.0", "@commitlint/cli": "~19.2.2", "@commitlint/config-conventional": "~19.2.2", - "@expo/config": "^11.0.0", + "@expo/config": "~12.0.12", "@testing-library/jest-dom": "~6.5.0", "@testing-library/react-native": "~12.9.0", "@types/geojson": "~7946.0.16", @@ -228,7 +229,7 @@ "@types/i18n-js": "~3.8.9", "@types/jest": "~29.5.14", "@types/lodash.memoize": "~4.1.9", - "@types/react": "~19.0.10", + "@types/react": "~19.1.10", "@types/react-native-base64": "~0.2.2", "@typescript-eslint/eslint-plugin": "~5.62.0", "@typescript-eslint/parser": "~5.62.0", @@ -254,7 +255,7 @@ "eslint-plugin-unused-imports": "~2.0.0", "jest": "~29.7.0", "jest-environment-jsdom": "~29.7.0", - "jest-expo": "~53.0.10", + "jest-expo": "~54.0.17", "jest-junit": "~16.0.0", "lint-staged": "~15.2.9", "np": "~10.0.7", @@ -265,7 +266,7 @@ "tailwindcss": "3.4.4", "ts-jest": "~29.1.2", "ts-node": "~10.9.2", - "typescript": "~5.8.3", + "typescript": "~5.9.2", "wait-on": "^9.0.3" }, "repository": { diff --git a/src/__tests__/app/call/[id].security.test.tsx b/src/__tests__/app/call/[id].security.test.tsx index 0d7d1a8..fbe07e0 100644 --- a/src/__tests__/app/call/[id].security.test.tsx +++ b/src/__tests__/app/call/[id].security.test.tsx @@ -5,13 +5,26 @@ import React from 'react'; jest.mock('react-native', () => ({ Platform: { OS: 'ios', + select: (obj: any) => obj.ios ?? obj.default, + }, + PixelRatio: { + getFontScale: () => 1, + get: () => 2, + roundToNearestPixel: (size: number) => size, + }, + Dimensions: { + get: () => ({ width: 375, height: 812, scale: 2, fontScale: 1 }), + addEventListener: jest.fn(), + removeEventListener: jest.fn(), }, ScrollView: ({ children, ...props }: any) =>
{children}
, StyleSheet: { create: (styles: any) => styles, + flatten: (styles: any) => styles, }, useWindowDimensions: () => ({ width: 375, height: 812 }), View: ({ children, ...props }: any) =>
{children}
, + Text: ({ children, ...props }: any) => {children}, })); // Mock expo-router @@ -153,6 +166,7 @@ jest.mock('react-i18next', () => ({ })); jest.mock('nativewind', () => ({ + cssInterop: jest.fn((Component: any) => Component), useColorScheme: () => ({ colorScheme: 'light' }), })); @@ -304,6 +318,36 @@ jest.mock('react-native-svg', () => ({ Mixin: {}, })); +// Mock @legendapp/motion +jest.mock('@legendapp/motion', () => ({ + Motion: { + View: jest.fn().mockImplementation(({ children }: any) => children), + Text: jest.fn().mockImplementation(({ children }: any) => children), + }, + AnimatePresence: jest.fn().mockImplementation(({ children }: any) => children), + createMotionAnimatedComponent: jest.fn((Component: any) => Component), +})); + +// Mock bottom-sheet to avoid deep gluestack/actionsheet import chain +jest.mock('@/components/ui/bottom-sheet', () => ({ + BottomSheet: ({ children }: any) =>
{children}
, + BottomSheetContent: ({ children }: any) =>
{children}
, + BottomSheetDragIndicator: () => null, + BottomSheetDragIndicatorWrapper: ({ children }: any) =>
{children}
, + BottomSheetBackdrop: () => null, +})); + +// Mock video feeds tab to avoid actionsheet import chain +jest.mock('@/components/callVideoFeeds/video-feeds-tab', () => ({ + __esModule: true, + default: () =>
Video Feeds
, +})); + +// Mock check-in tab +jest.mock('@/components/checkIn/check-in-tab', () => ({ + CheckInTab: () =>
Check In
, +})); + import CallDetail from '../../../app/call/[id]'; describe('CallDetail', () => { diff --git a/src/__tests__/app/call/[id].test.tsx b/src/__tests__/app/call/[id].test.tsx index fdfdc49..a1cdf4c 100644 --- a/src/__tests__/app/call/[id].test.tsx +++ b/src/__tests__/app/call/[id].test.tsx @@ -11,6 +11,16 @@ import { useLocationStore } from '@/stores/app/location-store'; import { useStatusBottomSheetStore } from '@/stores/status/store'; import { useToastStore } from '@/stores/toast/store'; +// Mock @legendapp/motion before importing CallDetail +jest.mock('@legendapp/motion', () => ({ + Motion: { + View: jest.fn().mockImplementation(({ children }) => children), + Text: jest.fn().mockImplementation(({ children }) => children), + }, + AnimatePresence: jest.fn().mockImplementation(({ children }) => children), + createMotionAnimatedComponent: jest.fn((Component) => Component), +})); + import CallDetail from '../../../app/call/[id]'; diff --git a/src/api/callVideoFeeds/callVideoFeeds.ts b/src/api/callVideoFeeds/callVideoFeeds.ts new file mode 100644 index 0000000..e547073 --- /dev/null +++ b/src/api/callVideoFeeds/callVideoFeeds.ts @@ -0,0 +1,47 @@ +import { type CallVideoFeedsResult } from '@/models/v4/callVideoFeeds/callVideoFeedsResult'; +import { type DeleteCallVideoFeedResult } from '@/models/v4/callVideoFeeds/deleteCallVideoFeedResult'; +import { type SaveCallVideoFeedResult } from '@/models/v4/callVideoFeeds/saveCallVideoFeedResult'; + +import { createApiEndpoint } from '../common/client'; + +const getCallVideoFeedsApi = createApiEndpoint('/CallVideoFeeds/GetCallVideoFeeds'); +const saveCallVideoFeedApi = createApiEndpoint('/CallVideoFeeds/SaveCallVideoFeed'); +const editCallVideoFeedApi = createApiEndpoint('/CallVideoFeeds/EditCallVideoFeed'); +const deleteCallVideoFeedApi = createApiEndpoint('/CallVideoFeeds/DeleteCallVideoFeed'); + +export interface SaveCallVideoFeedInput { + CallId: string; + Name: string; + Url: string; + FeedType?: number; + FeedFormat?: number; + Description?: string; + Latitude?: string; + Longitude?: string; + SortOrder: number; +} + +export interface EditCallVideoFeedInput extends SaveCallVideoFeedInput { + CallVideoFeedId: string; + Status: number; +} + +export const getCallVideoFeeds = async (callId: string, signal?: AbortSignal) => { + const response = await getCallVideoFeedsApi.get({ callId }, signal); + return response.data; +}; + +export const saveCallVideoFeed = async (input: SaveCallVideoFeedInput) => { + const response = await saveCallVideoFeedApi.post(input); + return response.data; +}; + +export const editCallVideoFeed = async (input: EditCallVideoFeedInput) => { + const response = await editCallVideoFeedApi.put(input); + return response.data; +}; + +export const deleteCallVideoFeed = async (callVideoFeedId: string) => { + const response = await deleteCallVideoFeedApi.delete({ callVideoFeedId }); + return response.data; +}; diff --git a/src/api/calls/calls.ts b/src/api/calls/calls.ts index fe66c7c..519246a 100644 --- a/src/api/calls/calls.ts +++ b/src/api/calls/calls.ts @@ -9,7 +9,7 @@ const callsApi = createApiEndpoint('/Calls/GetActiveCalls'); const getCallApi = createApiEndpoint('/Calls/GetCall'); const getCallExtraDataApi = createApiEndpoint('/Calls/GetCallExtraData'); const createCallApi = createApiEndpoint('/Calls/SaveCall'); -const updateCallApi = createApiEndpoint('/Calls/UpdateCall'); +const updateCallApi = createApiEndpoint('/Calls/EditCall'); const closeCallApi = createApiEndpoint('/Calls/CloseCall'); export const getCalls = async () => { @@ -97,20 +97,16 @@ export const createCall = async (callData: CreateCallRequest) => { const dispatchEntries: string[] = []; if (callData.dispatchUsers) { - //dispatchEntries.push(...callData.dispatchUsers.map((user) => `U:${user}`)); - dispatchEntries.push(...callData.dispatchUsers); + dispatchEntries.push(...callData.dispatchUsers.map((user) => `P:${user}`)); } if (callData.dispatchGroups) { - //dispatchEntries.push(...callData.dispatchGroups.map((group) => `G:${group}`)); - dispatchEntries.push(...callData.dispatchGroups); + dispatchEntries.push(...callData.dispatchGroups.map((group) => `G:${group}`)); } if (callData.dispatchRoles) { - //dispatchEntries.push(...callData.dispatchRoles.map((role) => `R:${role}`)); - dispatchEntries.push(...callData.dispatchRoles); + dispatchEntries.push(...callData.dispatchRoles.map((role) => `R:${role}`)); } if (callData.dispatchUnits) { - //dispatchEntries.push(...callData.dispatchUnits.map((unit) => `U:${unit}`)); - dispatchEntries.push(...callData.dispatchUnits); + dispatchEntries.push(...callData.dispatchUnits.map((unit) => `U:${unit}`)); } dispatchList = dispatchEntries.join('|'); @@ -149,27 +145,23 @@ export const updateCall = async (callData: UpdateCallRequest) => { const dispatchEntries: string[] = []; if (callData.dispatchUsers) { - //dispatchEntries.push(...callData.dispatchUsers.map((user) => `U:${user}`)); - dispatchEntries.push(...callData.dispatchUsers); + dispatchEntries.push(...callData.dispatchUsers.map((user) => `P:${user}`)); } if (callData.dispatchGroups) { - //dispatchEntries.push(...callData.dispatchGroups.map((group) => `G:${group}`)); - dispatchEntries.push(...callData.dispatchGroups); + dispatchEntries.push(...callData.dispatchGroups.map((group) => `G:${group}`)); } if (callData.dispatchRoles) { - //dispatchEntries.push(...callData.dispatchRoles.map((role) => `R:${role}`)); - dispatchEntries.push(...callData.dispatchRoles); + dispatchEntries.push(...callData.dispatchRoles.map((role) => `R:${role}`)); } if (callData.dispatchUnits) { - //dispatchEntries.push(...callData.dispatchUnits.map((unit) => `U:${unit}`)); - dispatchEntries.push(...callData.dispatchUnits); + dispatchEntries.push(...callData.dispatchUnits.map((unit) => `U:${unit}`)); } dispatchList = dispatchEntries.join('|'); } const data = { - CallId: callData.callId, + Id: callData.callId, Name: callData.name, Nature: callData.nature, Note: callData.note || '', @@ -188,7 +180,7 @@ export const updateCall = async (callData: UpdateCallRequest) => { ReferenceId: callData.referenceId || '', }; - const response = await updateCallApi.post(data); + const response = await updateCallApi.put(data); return response.data; }; diff --git a/src/api/checkIn/checkInTimers.ts b/src/api/checkIn/checkInTimers.ts new file mode 100644 index 0000000..98eb80f --- /dev/null +++ b/src/api/checkIn/checkInTimers.ts @@ -0,0 +1,79 @@ +import { type CheckInRecordResult } from '@/models/v4/checkIn/checkInRecordResult'; +import { type CheckInTimerStatusResult } from '@/models/v4/checkIn/checkInTimerStatusResult'; +import { type PerformCheckInResult } from '@/models/v4/checkIn/performCheckInResult'; +import { type ResolvedCheckInTimerResult } from '@/models/v4/checkIn/resolvedCheckInTimerResult'; + +import { createApiEndpoint } from '../common/client'; + +const getTimerStatusesApi = createApiEndpoint('/CheckInTimers/GetTimerStatuses'); +const getTimersForCallApi = createApiEndpoint('/CheckInTimers/GetTimersForCall'); +const performCheckInApi = createApiEndpoint('/CheckInTimers/PerformCheckIn'); +const getCheckInHistoryApi = createApiEndpoint('/CheckInTimers/GetCheckInHistory'); +const toggleCallTimersApi = createApiEndpoint('/CheckInTimers/ToggleCallTimers'); + +export const getTimerStatuses = async (callId: number) => { + const response = await getTimerStatusesApi.get({ callId }); + return response.data; +}; + +export const getTimerStatusesForCalls = async (callIds: number[]): Promise => { + const results = await Promise.allSettled( + callIds.map((callId) => + getTimerStatuses(callId).then((result) => ({ + callId, + data: result.Data || [], + })) + ) + ); + + const allData = results + .filter((r): r is PromiseFulfilledResult<{ callId: number; data: CheckInTimerStatusResult['Data'] }> => r.status === 'fulfilled') + .flatMap((r) => r.value.data.map((timer) => ({ ...timer, CallId: r.value.callId }))); + + return { + Data: allData, + PageSize: 0, + Timestamp: '', + Version: '', + Node: '', + RequestId: '', + Status: '', + Environment: '', + } as CheckInTimerStatusResult; +}; + +export const getTimersForCall = async (callId: number) => { + const response = await getTimersForCallApi.get({ callId }); + return response.data; +}; + +export interface PerformCheckInInput { + CallId: number; + CheckInType: number; + UnitId?: number; + Latitude?: string; + Longitude?: string; + Note?: string; +} + +export const performCheckIn = async (input: PerformCheckInInput) => { + const response = await performCheckInApi.post({ + CallId: input.CallId, + CheckInType: input.CheckInType, + UnitId: input.UnitId ?? 0, + Latitude: input.Latitude ?? '', + Longitude: input.Longitude ?? '', + Note: input.Note ?? '', + }); + return response.data; +}; + +export const getCheckInHistory = async (callId: number) => { + const response = await getCheckInHistoryApi.get({ callId }); + return response.data; +}; + +export const toggleCallTimers = async (callId: number, enabled: boolean) => { + const response = await toggleCallTimersApi.put({ callId, enabled }); + return response.data; +}; diff --git a/src/api/weatherAlerts/weatherAlerts.ts b/src/api/weatherAlerts/weatherAlerts.ts new file mode 100644 index 0000000..5fb8306 --- /dev/null +++ b/src/api/weatherAlerts/weatherAlerts.ts @@ -0,0 +1,44 @@ +import { type ActiveWeatherAlertsResult } from '@/models/v4/weatherAlerts/activeWeatherAlertsResult'; +import { type WeatherAlertResult } from '@/models/v4/weatherAlerts/weatherAlertResult'; +import { type WeatherAlertSettingsResult } from '@/models/v4/weatherAlerts/weatherAlertSettingsResult'; +import { type WeatherAlertZonesResult } from '@/models/v4/weatherAlerts/weatherAlertZonesResult'; + +import { createApiEndpoint } from '../common/client'; + +const activeAlertsApi = createApiEndpoint('/WeatherAlerts/GetActiveAlerts'); +// alertDetail uses a path parameter, so we create the endpoint dynamically per call +const getAlertDetailEndpoint = (alertId: string) => createApiEndpoint(`/WeatherAlerts/GetWeatherAlert/${encodeURIComponent(alertId)}`); +const alertsNearLocationApi = createApiEndpoint('/WeatherAlerts/GetAlertsNearLocation'); +const alertHistoryApi = createApiEndpoint('/WeatherAlerts/GetAlertHistory'); +const settingsApi = createApiEndpoint('/WeatherAlerts/GetSettings'); +const zonesApi = createApiEndpoint('/WeatherAlerts/GetZones'); + +export const getActiveAlerts = async (signal?: AbortSignal) => { + const response = await activeAlertsApi.get(undefined, signal); + return response.data; +}; + +export const getWeatherAlert = async (alertId: string, signal?: AbortSignal) => { + const response = await getAlertDetailEndpoint(alertId).get(undefined, signal); + return response.data; +}; + +export const getAlertsNearLocation = async (lat: number, lng: number, radiusMiles: number = 25, signal?: AbortSignal) => { + const response = await alertsNearLocationApi.get({ lat, lng, radiusMiles }, signal); + return response.data; +}; + +export const getAlertHistory = async (startDate: string, endDate: string, signal?: AbortSignal) => { + const response = await alertHistoryApi.get({ startDate, endDate }, signal); + return response.data; +}; + +export const getWeatherAlertSettings = async (signal?: AbortSignal) => { + const response = await settingsApi.get(undefined, signal); + return response.data; +}; + +export const getWeatherAlertZones = async (signal?: AbortSignal) => { + const response = await zonesApi.get(undefined, signal); + return response.data; +}; diff --git a/src/app/(app)/_layout.tsx b/src/app/(app)/_layout.tsx index 063f30c..498e5bd 100644 --- a/src/app/(app)/_layout.tsx +++ b/src/app/(app)/_layout.tsx @@ -33,6 +33,7 @@ import useLockscreenStore from '@/stores/lockscreen/store'; import { useRolesStore } from '@/stores/roles/store'; import { securityStore } from '@/stores/security/store'; import { useSignalRStore } from '@/stores/signalr/signalr-store'; +import { useWeatherAlertsStore } from '@/stores/weatherAlerts/store'; export default function TabLayout() { const { t } = useTranslation(); @@ -158,6 +159,24 @@ export default function TabLayout() { // Don't fail initialization if SignalR connection fails } + // Initialize weather alerts + try { + await useWeatherAlertsStore.getState().fetchSettings(); + const weatherSettings = useWeatherAlertsStore.getState().settings; + if (weatherSettings?.WeatherAlertsEnabled) { + await useWeatherAlertsStore.getState().fetchActiveAlerts(); + } + logger.info({ + message: 'Weather alerts initialized', + context: { enabled: weatherSettings?.WeatherAlertsEnabled ?? false }, + }); + } catch (error) { + logger.error({ + message: 'Failed to initialize weather alerts', + context: { error, platform: Platform.OS }, + }); + } + hasInitialized.current = true; logger.info({ diff --git a/src/app/(app)/calls.tsx b/src/app/(app)/calls.tsx index ae90cad..3766928 100644 --- a/src/app/(app)/calls.tsx +++ b/src/app/(app)/calls.tsx @@ -74,7 +74,6 @@ export default function Calls() { renderItem={({ item }: { item: CallResultData }) => ( router.push(`/call/${item.CallId}` as Href)}> p.Id === item.Priority)} /> - p.Id === item.Priority)} /> )} keyExtractor={(item: CallResultData) => item.CallId} diff --git a/src/app/(app)/home.tsx b/src/app/(app)/home.tsx index 03a9075..21a8f0d 100644 --- a/src/app/(app)/home.tsx +++ b/src/app/(app)/home.tsx @@ -9,11 +9,13 @@ import { getCallNotes, saveCallNote } from '@/api/calls/callNotes'; import { getCallExtraData } from '@/api/calls/calls'; import { getMapDataAndMarkers } from '@/api/mapping/mapping'; import { AudioStreamBottomSheet } from '@/components/audio-stream/audio-stream-bottom-sheet'; +import { CloseCallBottomSheet } from '@/components/calls/close-call-bottom-sheet'; import { ActiveCallFilterBanner, ActiveCallsPanel, ActivityLogPanel, AddNoteBottomSheet, MapWidget, NotesPanel, PersonnelPanel, PTTInterface, StatsHeader, UnitsPanel } from '@/components/dispatch-console'; import { Box } from '@/components/ui/box'; import { FocusAwareStatusBar } from '@/components/ui/focus-aware-status-bar'; import { HStack } from '@/components/ui/hstack'; import { VStack } from '@/components/ui/vstack'; +import { WeatherAlertBanner } from '@/components/weatherAlerts/weather-alert-banner'; import { useAnalytics } from '@/hooks/use-analytics'; import { logger } from '@/lib/logging'; import { isCallActive, isCallPending, isCallScheduled } from '@/lib/utils'; @@ -27,6 +29,7 @@ import { useNotesStore } from '@/stores/notes/store'; import { usePersonnelStore } from '@/stores/personnel/store'; import { useSignalRStore } from '@/stores/signalr/signalr-store'; import { useUnitsStore } from '@/stores/units/store'; +import { useWeatherAlertsStore } from '@/stores/weatherAlerts/store'; export default function DispatchConsole() { const { t } = useTranslation(); @@ -42,9 +45,12 @@ export default function DispatchConsole() { const { units, isLoading: unitsLoading, fetchUnits } = useUnitsStore(); const { personnel, isLoading: personnelLoading, fetchPersonnel } = usePersonnelStore(); const { notes, isLoading: notesLoading, fetchNotes } = useNotesStore(); - const { lastUpdateTimestamp } = useSignalRStore(); + const { lastPersonnelUpdateTimestamp, lastUnitsUpdateTimestamp, lastCallsUpdateTimestamp, lastEventType } = useSignalRStore(); const { userId } = useAuthStore(); + // Weather alerts + const { alerts: weatherAlerts, settings: weatherSettings, fetchActiveAlerts: fetchWeatherAlerts } = useWeatherAlertsStore(); + // Dispatch console store const { selectedCallId, @@ -79,6 +85,7 @@ export default function DispatchConsole() { const [isAddNoteSheetOpen, setIsAddNoteSheetOpen] = useState(false); const [selectedPersonnelData, setSelectedPersonnelData] = useState(null); const [selectedUnitData, setSelectedUnitData] = useState(null); + const [isCloseCallSheetOpen, setIsCloseCallSheetOpen] = useState(false); // Update time every second useEffect(() => { @@ -98,6 +105,7 @@ export default function DispatchConsole() { fetchPersonnel(); fetchNotes(); fetchMapCenter(); + fetchWeatherAlerts(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -120,7 +128,7 @@ export default function DispatchConsole() { } }; - // Track analytics when view becomes visible + // Refresh data and track analytics when view becomes visible useFocusEffect( useCallback(() => { trackEvent('dispatch_console_viewed', { @@ -128,23 +136,48 @@ export default function DispatchConsole() { isLandscape, isTablet, }); - }, [trackEvent, isLandscape, isTablet]) + // Re-fetch core data so downstream panels (including check-in timers) refresh + fetchCalls(); + fetchUnits(); + fetchPersonnel(); + }, [trackEvent, isLandscape, isTablet, fetchCalls, fetchUnits, fetchPersonnel]) ); - // Listen for SignalR updates and refresh data + // Listen for SignalR personnel updates useEffect(() => { - if (lastUpdateTimestamp > 0) { - // Add activity log entry for update + if (lastPersonnelUpdateTimestamp > 0) { + fetchPersonnel(); addActivityLogEntry({ type: 'system', action: t('dispatch.system_update'), description: t('dispatch.data_refreshed'), }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [lastPersonnelUpdateTimestamp]); - // Refresh data - fetchCalls(); + // Listen for SignalR unit updates + useEffect(() => { + if (lastUnitsUpdateTimestamp > 0) { fetchUnits(); - fetchPersonnel(); + addActivityLogEntry({ + type: 'system', + action: t('dispatch.system_update'), + description: t('dispatch.data_refreshed'), + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [lastUnitsUpdateTimestamp]); + + // Listen for SignalR call updates + useEffect(() => { + if (lastCallsUpdateTimestamp > 0) { + fetchCalls(); + addActivityLogEntry({ + type: 'system', + action: t('dispatch.system_update'), + description: t('dispatch.data_refreshed'), + }); // Refresh call data if filter is active if (isCallFilterActive && selectedCallId) { @@ -152,7 +185,7 @@ export default function DispatchConsole() { } } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [lastUpdateTimestamp]); + }, [lastCallsUpdateTimestamp]); // Fetch call extra data and notes when a call is selected for filtering useEffect(() => { @@ -291,6 +324,29 @@ export default function DispatchConsole() { setIsAddNoteSheetOpen(true); }; + // Call action handlers for the actions panel + const handleCreateCall = useCallback(() => { + router.push('/call/new' as Href); + }, []); + + const handleViewCallDetails = useCallback(() => { + if (selectedCallId) { + router.push(`/call/${selectedCallId}` as Href); + } + }, [selectedCallId]); + + const handleCloseCallAction = useCallback(() => { + if (selectedCallId) { + setIsCloseCallSheetOpen(true); + } + }, [selectedCallId]); + + const handleAddCallNoteAction = useCallback(() => { + if (selectedCallId) { + setIsAddNoteSheetOpen(true); + } + }, [selectedCallId]); + // Handle note added from bottom sheet const handleNoteAdded = () => { fetchNotes(); @@ -435,6 +491,10 @@ export default function DispatchConsole() { onStatusUpdated={handleStatusUpdated} onStaffingUpdated={handleStaffingUpdated} onUnitStatusUpdated={handleUnitStatusUpdated} + onCreateCall={handleCreateCall} + onViewCallDetails={handleViewCallDetails} + onCloseCall={handleCloseCallAction} + onAddCallNote={handleAddCallNoteAction} /> @@ -527,6 +587,10 @@ export default function DispatchConsole() { onStatusUpdated={handleStatusUpdated} onStaffingUpdated={handleStaffingUpdated} onUnitStatusUpdated={handleUnitStatusUpdated} + onCreateCall={handleCreateCall} + onViewCallDetails={handleViewCallDetails} + onCloseCall={handleCloseCallAction} + onAddCallNote={handleAddCallNoteAction} /> @@ -598,6 +662,10 @@ export default function DispatchConsole() { onStatusUpdated={handleStatusUpdated} onStaffingUpdated={handleStaffingUpdated} onUnitStatusUpdated={handleUnitStatusUpdated} + onCreateCall={handleCreateCall} + onViewCallDetails={handleViewCallDetails} + onCloseCall={handleCloseCallAction} + onAddCallNote={handleAddCallNoteAction} /> @@ -623,6 +691,9 @@ export default function DispatchConsole() { weatherLongitude={mapCenterLongitude} /> + {/* Weather Alert Banner */} + {weatherSettings?.WeatherAlertsEnabled !== false && weatherAlerts.length > 0 && router.push('/(app)/weather-alerts' as Href)} />} + {/* Active Call Filter Banner */} {isCallFilterActive && selectedCall && } @@ -634,6 +705,9 @@ export default function DispatchConsole() { {/* Add Note Bottom Sheet */} setIsAddNoteSheetOpen(false)} onNoteAdded={handleNoteAdded} /> + + {/* Close Call Bottom Sheet */} + {selectedCallId && setIsCloseCallSheetOpen(false)} callId={selectedCallId} />} ); } diff --git a/src/app/(app)/home.web.tsx b/src/app/(app)/home.web.tsx index 9c7f654..ca8ae2e 100644 --- a/src/app/(app)/home.web.tsx +++ b/src/app/(app)/home.web.tsx @@ -9,6 +9,7 @@ import { getCallNotes, saveCallNote } from '@/api/calls/callNotes'; import { getCallExtraData } from '@/api/calls/calls'; import { getMapDataAndMarkers } from '@/api/mapping/mapping'; import { AudioStreamBottomSheet } from '@/components/audio-stream/audio-stream-bottom-sheet'; +import { CloseCallBottomSheet } from '@/components/calls/close-call-bottom-sheet'; import { ActiveCallFilterBanner, ActiveCallsPanel, ActivityLogPanel, AddNoteBottomSheet, MapWidget, NotesPanel, PersonnelPanel, PTTInterface, StatsHeader, UnitsPanel } from '@/components/dispatch-console'; import { Box } from '@/components/ui/box'; import { FocusAwareStatusBar } from '@/components/ui/focus-aware-status-bar'; @@ -81,6 +82,7 @@ export default function DispatchConsoleWeb() { const [isAddNoteSheetOpen, setIsAddNoteSheetOpen] = useState(false); const [selectedPersonnelData, setSelectedPersonnelData] = useState(null); const [selectedUnitData, setSelectedUnitData] = useState(null); + const [isCloseCallSheetOpen, setIsCloseCallSheetOpen] = useState(false); // Track previous timestamps to detect changes const prevPersonnelTimestamp = useRef(0); @@ -454,6 +456,29 @@ export default function DispatchConsoleWeb() { }); }; + // Call action handlers for the actions panel + const handleCreateCall = useCallback(() => { + router.push('/call/new' as Href); + }, []); + + const handleViewCallDetails = useCallback(() => { + if (selectedCallId) { + router.push(`/call/${selectedCallId}` as Href); + } + }, [selectedCallId]); + + const handleCloseCallAction = useCallback(() => { + if (selectedCallId) { + setIsCloseCallSheetOpen(true); + } + }, [selectedCallId]); + + const handleAddCallNoteAction = useCallback(() => { + if (selectedCallId) { + setIsAddNoteSheetOpen(true); + } + }, [selectedCallId]); + // Handle setting unit status for call const handleSetUnitStatusForCall = (unitId: string) => { const unit = units.find((u) => u.UnitId === unitId); @@ -522,6 +547,10 @@ export default function DispatchConsoleWeb() { onStatusUpdated={handleStatusUpdated} onStaffingUpdated={handleStaffingUpdated} onUnitStatusUpdated={handleUnitStatusUpdated} + onCreateCall={handleCreateCall} + onViewCallDetails={handleViewCallDetails} + onCloseCall={handleCloseCallAction} + onAddCallNote={handleAddCallNoteAction} /> @@ -614,6 +643,10 @@ export default function DispatchConsoleWeb() { onStatusUpdated={handleStatusUpdated} onStaffingUpdated={handleStaffingUpdated} onUnitStatusUpdated={handleUnitStatusUpdated} + onCreateCall={handleCreateCall} + onViewCallDetails={handleViewCallDetails} + onCloseCall={handleCloseCallAction} + onAddCallNote={handleAddCallNoteAction} /> @@ -685,6 +718,10 @@ export default function DispatchConsoleWeb() { onStatusUpdated={handleStatusUpdated} onStaffingUpdated={handleStaffingUpdated} onUnitStatusUpdated={handleUnitStatusUpdated} + onCreateCall={handleCreateCall} + onViewCallDetails={handleViewCallDetails} + onCloseCall={handleCloseCallAction} + onAddCallNote={handleAddCallNoteAction} /> @@ -721,6 +758,9 @@ export default function DispatchConsoleWeb() { {/* Add Note Bottom Sheet */} setIsAddNoteSheetOpen(false)} onNoteAdded={handleNoteAdded} /> + + {/* Close Call Bottom Sheet */} + {selectedCallId && setIsCloseCallSheetOpen(false)} callId={selectedCallId} />} ); } diff --git a/src/app/(app)/personnel.tsx b/src/app/(app)/personnel.tsx index b3975d0..0ba7f08 100644 --- a/src/app/(app)/personnel.tsx +++ b/src/app/(app)/personnel.tsx @@ -1,33 +1,187 @@ -// Removed useFocusEffect to simplify analytics tracking on mount -import { FlashList } from '@shopify/flash-list'; -import { Filter, Search, Users, X } from 'lucide-react-native'; -import * as React from 'react'; +import { useFocusEffect } from '@react-navigation/native'; +import { type Href, router } from 'expo-router'; +import { Circle, Search, User, Users, X } from 'lucide-react-native'; +import React, { useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { View } from 'react-native'; +import { Pressable, RefreshControl, StyleSheet, View } from 'react-native'; import { Loading } from '@/components/common/loading'; import ZeroState from '@/components/common/zero-state'; -import { Badge } from '@/components/ui/badge'; import { Box } from '@/components/ui/box'; -import { Button } from '@/components/ui/button'; +import { FlatList } from '@/components/ui/flat-list'; import { FocusAwareStatusBar } from '@/components/ui/focus-aware-status-bar'; import { HStack } from '@/components/ui/hstack'; -import { Input } from '@/components/ui/input'; -import { InputField, InputIcon, InputSlot } from '@/components/ui/input'; -import { RefreshControl } from '@/components/ui/refresh-control'; +import { Icon } from '@/components/ui/icon'; +import { Input, InputField, InputIcon, InputSlot } from '@/components/ui/input'; import { Text } from '@/components/ui/text'; +import { VStack } from '@/components/ui/vstack'; import { useAnalytics } from '@/hooks/use-analytics'; +import { type PersonnelInfoResultData } from '@/models/v4/personnel/personnelInfoResultData'; +import { usePersonnelStore } from '@/stores/personnel/store'; + +const PersonnelCard: React.FC<{ + person: PersonnelInfoResultData; + onPress: () => void; +}> = React.memo(({ person, onPress }) => { + const { t } = useTranslation(); + const statusColor = person.StatusColor || '#6b7280'; + const staffingColor = person.StaffingColor || '#6b7280'; + + return ( + + + + {/* Avatar */} + + + + + {/* Info */} + + + {person.FirstName} {person.LastName} + + + {person.GroupName || t('personnel.no_group')} + + {person.Roles && person.Roles.length > 0 ? ( + + {person.Roles.join(', ')} + + ) : null} + + + {/* Status badges */} + + + + + {person.Status || t('personnel.unknown_status')} + + + + + + {person.Staffing || t('personnel.unknown_status')} + + + + + + + ); +}); + +PersonnelCard.displayName = 'PersonnelCard'; export default function Personnel() { const { t } = useTranslation(); const { trackEvent } = useAnalytics(); - const [refreshing, setRefreshing] = React.useState(false); + const { personnel, isLoading, error, fetchPersonnel } = usePersonnelStore(); + const [searchQuery, setSearchQuery] = useState(''); + + useFocusEffect( + useCallback(() => { + fetchPersonnel(); + }, [fetchPersonnel]) + ); + + const filteredPersonnel = useMemo(() => { + if (!searchQuery.trim()) return personnel; + const query = searchQuery.toLowerCase().trim(); + return personnel.filter((p) => { + const fullName = `${p.FirstName} ${p.LastName}`.toLowerCase(); + const group = (p.GroupName || '').toLowerCase(); + const status = (p.Status || '').toLowerCase(); + const staffing = (p.Staffing || '').toLowerCase(); + const roles = (p.Roles || []).join(' ').toLowerCase(); + return fullName.includes(query) || group.includes(query) || status.includes(query) || staffing.includes(query) || roles.includes(query); + }); + }, [personnel, searchQuery]); + + const handlePersonPress = useCallback( + (person: PersonnelInfoResultData) => { + trackEvent('personnel_detail_opened', { userId: person.UserId }); + router.push(`/personnel/${person.UserId}` as Href); + }, + [trackEvent] + ); + + const renderItem = useCallback(({ item }: { item: PersonnelInfoResultData }) => handlePersonPress(item)} />, [handlePersonPress]); + + const keyExtractor = useCallback((item: PersonnelInfoResultData) => item.UserId, []); + + const renderContent = () => { + if (isLoading && personnel.length === 0) { + return ; + } + + if (error) { + return ; + } + + const hasSearch = searchQuery.trim().length > 0; + const emptyHeading = hasSearch ? t('personnel.no_results') : t('personnel.empty'); + const emptyDescription = hasSearch ? t('personnel.no_results_description') : t('personnel.empty_description'); + + return ( + + testID="personnel-list" + data={filteredPersonnel} + renderItem={renderItem} + keyExtractor={keyExtractor} + refreshControl={} + ListEmptyComponent={} + contentContainerStyle={styles.listContent} + /> + ); + }; return ( - <> - - - - + + + + {/* Search input */} + + + + + + {searchQuery ? ( + setSearchQuery('')}> + + + ) : null} + + + {/* Results count */} + {personnel.length > 0 && !isLoading ? ( + + {searchQuery.trim() ? `${filteredPersonnel.length} of ${personnel.length}` : `${personnel.length} personnel`} + + ) : null} + + {/* Main content */} + {renderContent()} + + ); } + +const styles = StyleSheet.create({ + cardPressable: { + // Ensures the pressable area covers the full card + }, + avatar: { + width: 40, + height: 40, + borderRadius: 20, + borderWidth: 2, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: 'rgba(139, 92, 246, 0.08)', + }, + listContent: { + paddingBottom: 20, + }, +}); diff --git a/src/app/(app)/units.tsx b/src/app/(app)/units.tsx index 97f41c8..5810486 100644 --- a/src/app/(app)/units.tsx +++ b/src/app/(app)/units.tsx @@ -1,34 +1,198 @@ import { useFocusEffect } from '@react-navigation/native'; -import { FlashList } from '@shopify/flash-list'; -import { Filter, Search, Truck, X } from 'lucide-react-native'; -import * as React from 'react'; +import { type Href, router } from 'expo-router'; +import { Circle, MapPin, Search, Truck, X } from 'lucide-react-native'; +import React, { useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { View } from 'react-native'; +import { Pressable, RefreshControl, StyleSheet, View } from 'react-native'; import { Loading } from '@/components/common/loading'; import ZeroState from '@/components/common/zero-state'; -import { Badge } from '@/components/ui/badge'; import { Box } from '@/components/ui/box'; -import { Button } from '@/components/ui/button'; +import { FlatList } from '@/components/ui/flat-list'; import { FocusAwareStatusBar } from '@/components/ui/focus-aware-status-bar'; import { HStack } from '@/components/ui/hstack'; -import { Input } from '@/components/ui/input'; -import { InputField, InputIcon, InputSlot } from '@/components/ui/input'; -import { RefreshControl } from '@/components/ui/refresh-control'; +import { Icon } from '@/components/ui/icon'; +import { Input, InputField, InputIcon, InputSlot } from '@/components/ui/input'; import { Text } from '@/components/ui/text'; +import { VStack } from '@/components/ui/vstack'; import { useAnalytics } from '@/hooks/use-analytics'; +import { type UnitInfoResultData } from '@/models/v4/units/unitInfoResultData'; import { useUnitsStore } from '@/stores/units/store'; +const UnitCard: React.FC<{ + unit: UnitInfoResultData; + onPress: () => void; +}> = React.memo(({ unit, onPress }) => { + const { t } = useTranslation(); + const statusColor = unit.CurrentStatusColor || '#6b7280'; + const hasDestination = unit.CurrentDestinationName && unit.CurrentDestinationName.trim() !== ''; + + return ( + + + + {/* Icon */} + + + + + {/* Info */} + + + {unit.Name} + + + {unit.Type || unit.GroupName || t('units.unknown_status')} + + {unit.Roles && unit.Roles.length > 0 ? ( + + {unit.Roles.map((r) => `${r.RoleName}: ${r.Name}`).join(', ')} + + ) : null} + {hasDestination ? ( + + + + {unit.CurrentDestinationName} + + + ) : null} + + + {/* Status */} + + + + + {unit.CurrentStatus || t('units.unknown_status')} + + + {unit.Latitude && unit.Longitude ? ( + + + GPS + + ) : null} + + + + + ); +}); + +UnitCard.displayName = 'UnitCard'; + export default function Units() { const { t } = useTranslation(); const { trackEvent } = useAnalytics(); - const [refreshing, setRefreshing] = React.useState(false); + const { units, isLoading, error, fetchUnits } = useUnitsStore(); + const [searchQuery, setSearchQuery] = useState(''); + + useFocusEffect( + useCallback(() => { + fetchUnits(); + }, [fetchUnits]) + ); + + const filteredUnits = useMemo(() => { + if (!searchQuery.trim()) return units; + const query = searchQuery.toLowerCase().trim(); + return units.filter((u) => { + const name = (u.Name || '').toLowerCase(); + const type = (u.Type || '').toLowerCase(); + const groupName = (u.GroupName || '').toLowerCase(); + const status = (u.CurrentStatus || '').toLowerCase(); + const note = (u.Note || '').toLowerCase(); + const roles = (u.Roles || []) + .map((r) => `${r.RoleName} ${r.Name}`) + .join(' ') + .toLowerCase(); + return name.includes(query) || type.includes(query) || groupName.includes(query) || status.includes(query) || note.includes(query) || roles.includes(query); + }); + }, [units, searchQuery]); + + const handleUnitPress = useCallback( + (unit: UnitInfoResultData) => { + trackEvent('unit_detail_opened', { unitId: unit.UnitId }); + router.push(`/units/${unit.UnitId}` as Href); + }, + [trackEvent] + ); + + const renderItem = useCallback(({ item }: { item: UnitInfoResultData }) => handleUnitPress(item)} />, [handleUnitPress]); + + const keyExtractor = useCallback((item: UnitInfoResultData) => item.UnitId, []); + + const renderContent = () => { + if (isLoading && units.length === 0) { + return ; + } + + if (error) { + return ; + } + + const hasSearch = searchQuery.trim().length > 0; + const emptyHeading = hasSearch ? t('units.no_results') : t('units.empty'); + const emptyDescription = hasSearch ? t('units.no_results_description') : t('units.empty_description'); + + return ( + + testID="units-list" + data={filteredUnits} + renderItem={renderItem} + keyExtractor={keyExtractor} + refreshControl={} + ListEmptyComponent={} + contentContainerStyle={styles.listContent} + /> + ); + }; return ( - <> - - - - + + + + {/* Search input */} + + + + + + {searchQuery ? ( + setSearchQuery('')}> + + + ) : null} + + + {/* Results count */} + {units.length > 0 && !isLoading ? ( + + {searchQuery.trim() ? `${filteredUnits.length} of ${units.length}` : `${units.length} units`} + + ) : null} + + {/* Main content */} + {renderContent()} + + ); } + +const styles = StyleSheet.create({ + cardPressable: { + // Ensures the pressable area covers the full card + }, + unitIcon: { + width: 40, + height: 40, + borderRadius: 10, + borderWidth: 2, + alignItems: 'center', + justifyContent: 'center', + }, + listContent: { + paddingBottom: 20, + }, +}); diff --git a/src/app/(app)/weather-alerts/index.tsx b/src/app/(app)/weather-alerts/index.tsx new file mode 100644 index 0000000..eec65eb --- /dev/null +++ b/src/app/(app)/weather-alerts/index.tsx @@ -0,0 +1,240 @@ +import { useFocusEffect } from '@react-navigation/native'; +import { type Href, router } from 'expo-router'; +import { useColorScheme } from 'nativewind'; +import React, { useCallback, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { ActivityIndicator, FlatList, Pressable, RefreshControl, ScrollView, StyleSheet, Text as RNText, View } from 'react-native'; + +import { Text } from '@/components/ui/text'; +import { VStack } from '@/components/ui/vstack'; +import { WeatherAlertCard } from '@/components/weatherAlerts/weather-alert-card'; +import { WeatherAlertSeverity } from '@/models/v4/weatherAlerts/weatherAlertEnums'; +import { type WeatherAlertResultData } from '@/models/v4/weatherAlerts/weatherAlertResultData'; +import { useWeatherAlertsStore } from '@/stores/weatherAlerts/store'; + +type FilterType = 'all' | 'extreme' | 'severe' | 'moderate' | 'minor'; +type SortType = 'severity' | 'expires' | 'newest'; + +export default function WeatherAlertsListScreen() { + const { t } = useTranslation(); + const { colorScheme } = useColorScheme(); + const isDark = colorScheme === 'dark'; + + const { alerts, isLoading, fetchActiveAlerts } = useWeatherAlertsStore(); + + const [filter, setFilter] = useState('all'); + const [sort, setSort] = useState('severity'); + + useFocusEffect( + useCallback(() => { + fetchActiveAlerts(); + }, [fetchActiveAlerts]) + ); + + const filteredAlerts = useMemo(() => { + let filtered = [...alerts]; + + switch (filter) { + case 'extreme': + filtered = filtered.filter((a) => a.Severity === WeatherAlertSeverity.Extreme); + break; + case 'severe': + filtered = filtered.filter((a) => a.Severity === WeatherAlertSeverity.Severe); + break; + case 'moderate': + filtered = filtered.filter((a) => a.Severity === WeatherAlertSeverity.Moderate); + break; + case 'minor': + filtered = filtered.filter((a) => a.Severity === WeatherAlertSeverity.Minor); + break; + } + + switch (sort) { + case 'severity': + filtered.sort((a, b) => a.Severity - b.Severity || new Date(b.EffectiveUtc).getTime() - new Date(a.EffectiveUtc).getTime()); + break; + case 'expires': + filtered.sort((a, b) => { + const aExp = a.ExpiresUtc ? new Date(a.ExpiresUtc).getTime() : Infinity; + const bExp = b.ExpiresUtc ? new Date(b.ExpiresUtc).getTime() : Infinity; + return aExp - bExp; + }); + break; + case 'newest': + filtered.sort((a, b) => new Date(b.EffectiveUtc).getTime() - new Date(a.EffectiveUtc).getTime()); + break; + } + + return filtered; + }, [alerts, filter, sort]); + + const handleAlertPress = (alertId: string) => { + router.push(`/weather-alerts/${alertId}` as Href); + }; + + const renderFilterButton = (key: FilterType, label: string) => { + const isActive = filter === key; + return ( + setFilter(key)} style={[styles.filterPill, isActive ? styles.filterPillActive : isDark ? styles.filterPillDark : styles.filterPillLight]}> + + {label} + + + ); + }; + + const renderSortButton = (key: SortType, label: string) => { + const isActive = sort === key; + return ( + setSort(key)} style={[styles.sortPill, isActive && styles.sortPillActive]}> + {label} + + ); + }; + + const renderEmpty = () => { + if (isLoading) return null; + return ( + + {t('weatherAlerts.noActiveAlerts')} + + ); + }; + + const renderItem = useCallback(({ item }: { item: WeatherAlertResultData }) => , []); + + return ( + + {/* Header */} + {t('weatherAlerts.title')} + + {/* Filter pills */} + + {renderFilterButton('all', t('weatherAlerts.filter.all'))} + {renderFilterButton('extreme', t('weatherAlerts.severity.extreme'))} + {renderFilterButton('severe', t('weatherAlerts.severity.severe'))} + {renderFilterButton('moderate', t('weatherAlerts.severity.moderate'))} + {renderFilterButton('minor', t('weatherAlerts.severity.minor'))} + + + {/* Sort row */} + + {renderSortButton('severity', t('weatherAlerts.sort.severity'))} + {renderSortButton('expires', t('weatherAlerts.sort.expires'))} + {renderSortButton('newest', t('weatherAlerts.sort.newest'))} + + + {/* List */} + {isLoading && alerts.length === 0 ? ( + + ) : ( + item.WeatherAlertId} + renderItem={renderItem} + contentContainerStyle={styles.listContent} + ListEmptyComponent={renderEmpty} + refreshControl={} + /> + )} + + ); +} + +const styles = StyleSheet.create({ + container: { flex: 1 }, + containerLight: { backgroundColor: '#f3f4f6' }, + containerDark: { backgroundColor: '#030712' }, + + title: { + fontSize: 18, + fontWeight: '700', + paddingHorizontal: 16, + paddingTop: 16, + paddingBottom: 8, + }, + titleLight: { color: '#111827' }, + titleDark: { color: '#f3f4f6' }, + + filterScroll: { + flexGrow: 0, + }, + filterRow: { + flexDirection: 'row', + alignItems: 'center', + paddingHorizontal: 16, + paddingVertical: 4, + gap: 8, + }, + filterPill: { + paddingHorizontal: 14, + paddingVertical: 7, + borderRadius: 20, + }, + filterPillLight: { + backgroundColor: '#e5e7eb', + }, + filterPillDark: { + backgroundColor: '#374151', + }, + filterPillActive: { + backgroundColor: '#2563eb', + }, + filterPillText: { + fontSize: 13, + fontWeight: '500', + lineHeight: 16, + }, + filterPillTextActive: { + color: '#ffffff', + }, + filterPillTextLight: { + color: '#4b5563', + }, + filterPillTextDark: { + color: '#9ca3af', + }, + + sortRow: { + flexDirection: 'row', + alignItems: 'center', + paddingHorizontal: 16, + paddingTop: 6, + paddingBottom: 4, + gap: 12, + }, + sortPill: { + paddingVertical: 4, + paddingHorizontal: 6, + }, + sortPillActive: { + borderBottomWidth: 2, + borderBottomColor: '#2563eb', + }, + sortPillText: { + fontSize: 12, + lineHeight: 16, + }, + sortPillTextActive: { + color: '#2563eb', + fontWeight: '600', + }, + sortPillTextActiveDark: { + color: '#60a5fa', + fontWeight: '600', + }, + sortPillTextLight: { + color: '#6b7280', + }, + sortPillTextDark: { + color: '#9ca3af', + }, + + listContent: { + paddingHorizontal: 16, + paddingBottom: 24, + }, + loader: { + marginTop: 40, + }, +}); diff --git a/src/app/_layout.tsx b/src/app/_layout.tsx index 3b56578..c9effd0 100644 --- a/src/app/_layout.tsx +++ b/src/app/_layout.tsx @@ -211,12 +211,21 @@ function Providers({ children }: { children: React.ReactNode }) { - - {children} - - - - + {Platform.OS === 'web' ? ( + <> + {children} + + + + + ) : ( + + {children} + + + + + )} diff --git a/src/app/call/[id].tsx b/src/app/call/[id].tsx index 01044b1..ce77726 100644 --- a/src/app/call/[id].tsx +++ b/src/app/call/[id].tsx @@ -1,12 +1,14 @@ import { format } from 'date-fns'; import { type Href, Stack, useLocalSearchParams, useRouter } from 'expo-router'; -import { ClockIcon, FileTextIcon, ImageIcon, InfoIcon, LoaderIcon, PaperclipIcon, RouteIcon, UserIcon, UsersIcon } from 'lucide-react-native'; +import { ClockIcon, FileTextIcon, ImageIcon, InfoIcon, LoaderIcon, PaperclipIcon, RouteIcon, ShieldCheckIcon, UserIcon, UsersIcon, VideoIcon } from 'lucide-react-native'; import { useColorScheme } from 'nativewind'; import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ScrollView, StyleSheet, useWindowDimensions, View } from 'react-native'; import WebView from 'react-native-webview'; +import { VideoFeedsTab } from '@/components/callVideoFeeds/video-feeds-tab'; +import { CheckInTab } from '@/components/checkIn/check-in-tab'; import { Loading } from '@/components/common/loading'; import ZeroState from '@/components/common/zero-state'; // Import a static map component instead of react-native-maps @@ -25,6 +27,7 @@ import { openMapsWithDirections } from '@/lib/navigation'; import { useCoreStore } from '@/stores/app/core-store'; import { useLocationStore } from '@/stores/app/location-store'; import { useCallDetailStore } from '@/stores/calls/detail-store'; +import { useCheckInStore } from '@/stores/checkIn/store'; import { useSecurityStore } from '@/stores/security/store'; import { useStatusBottomSheetStore } from '@/stores/status/store'; import { useToastStore } from '@/stores/toast/store'; @@ -55,6 +58,7 @@ export default function CallDetail() { const { canUserCreateCalls } = useSecurityStore(); const { activeCall, activeStatuses, activeUnit } = useCoreStore(); const { setIsOpen: setStatusBottomSheetOpen, setSelectedCall } = useStatusBottomSheetStore(); + const timerStatuses = useCheckInStore((state) => state.timerStatuses); const [isNotesModalOpen, setIsNotesModalOpen] = useState(false); const [isImagesModalOpen, setIsImagesModalOpen] = useState(false); const [isFilesModalOpen, setIsFilesModalOpen] = useState(false); @@ -469,6 +473,28 @@ export default function CallDetail() { }, ]; + // Video feeds tab + tabs.push({ + key: 'video', + title: t('call_detail.tabs.video'), + icon: , + content: , + }); + + if (call?.CheckInTimersEnabled) { + const overdueCount = timerStatuses.filter((s) => s.Status === 'Overdue').length; + const warningCount = timerStatuses.filter((s) => s.Status === 'Warning').length; + const badgeCount = overdueCount + warningCount; + + tabs.push({ + key: 'checkin', + title: t('check_in.tab_title'), + icon: , + badge: badgeCount, + content: , + }); + } + return tabs; }; diff --git a/src/app/call/[id].web.tsx b/src/app/call/[id].web.tsx index befbe72..c76acda 100644 --- a/src/app/call/[id].web.tsx +++ b/src/app/call/[id].web.tsx @@ -1,12 +1,14 @@ import { format } from 'date-fns'; import DOMPurify from 'dompurify'; import { type Href, Stack, useLocalSearchParams, useRouter } from 'expo-router'; -import { ClockIcon, EditIcon, FileTextIcon, ImageIcon, InfoIcon, LoaderIcon, MapPinIcon, PaperclipIcon, RouteIcon, UserIcon, UsersIcon, XCircleIcon } from 'lucide-react-native'; +import { ClockIcon, EditIcon, FileTextIcon, ImageIcon, InfoIcon, LoaderIcon, MapPinIcon, PaperclipIcon, RouteIcon, ShieldCheckIcon, UserIcon, UsersIcon, VideoIcon, XCircleIcon } from 'lucide-react-native'; import { useColorScheme } from 'nativewind'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Pressable, ScrollView, StyleSheet, useWindowDimensions, View } from 'react-native'; +import { VideoFeedsTab } from '@/components/callVideoFeeds/video-feeds-tab'; +import { CheckInTab } from '@/components/checkIn/check-in-tab'; import { Loading } from '@/components/common/loading'; import ZeroState from '@/components/common/zero-state'; import StaticMap from '@/components/maps/static-map'; @@ -21,6 +23,7 @@ import { openMapsWithDirections } from '@/lib/navigation'; import { useCoreStore } from '@/stores/app/core-store'; import { useLocationStore } from '@/stores/app/location-store'; import { useCallDetailStore } from '@/stores/calls/detail-store'; +import { useCheckInStore } from '@/stores/checkIn/store'; import { useSecurityStore } from '@/stores/security/store'; import { useStatusBottomSheetStore } from '@/stores/status/store'; import { useToastStore } from '@/stores/toast/store'; @@ -32,7 +35,7 @@ import CallNotesModal from '../../components/calls/call-notes-modal'; import { CloseCallBottomSheet } from '../../components/calls/close-call-bottom-sheet'; import { StatusBottomSheet } from '../../components/status/status-bottom-sheet'; -type TabKey = 'info' | 'contact' | 'protocols' | 'dispatched' | 'timeline'; +type TabKey = 'info' | 'contact' | 'protocols' | 'dispatched' | 'timeline' | 'video' | 'checkin'; export default function CallDetailWeb() { const { id } = useLocalSearchParams(); @@ -137,8 +140,8 @@ export default function CallDetailWeb() { handleEditCall(); } // Tab navigation with number keys - if (e.key >= '1' && e.key <= '5' && !e.ctrlKey && !e.metaKey && !e.altKey) { - const tabs: TabKey[] = ['info', 'contact', 'protocols', 'dispatched', 'timeline']; + if (e.key >= '1' && e.key <= '7' && !e.ctrlKey && !e.metaKey && !e.altKey) { + const tabs: TabKey[] = ['info', 'contact', 'protocols', 'dispatched', 'timeline', 'video', 'checkin']; const index = parseInt(e.key) - 1; if (tabs[index]) setActiveTab(tabs[index]); } @@ -162,16 +165,29 @@ export default function CallDetailWeb() { } }; - const tabs = useMemo( - () => [ - { key: 'info' as const, title: t('call_detail.tabs.info'), icon: InfoIcon }, - { key: 'contact' as const, title: t('call_detail.tabs.contact'), icon: UserIcon }, - { key: 'protocols' as const, title: t('call_detail.tabs.protocols'), icon: FileTextIcon }, - { key: 'dispatched' as const, title: t('call_detail.tabs.dispatched'), icon: UsersIcon }, - { key: 'timeline' as const, title: t('call_detail.tabs.timeline'), icon: ClockIcon, badge: callExtraData?.Activity?.length || 0 }, - ], - [t, callExtraData?.Activity?.length] - ); + const checkInStatuses = useCheckInStore((s) => s.timerStatuses); + const overdueCount = checkInStatuses.filter((s) => s.Status === 'Overdue').length; + const warningCount = checkInStatuses.filter((s) => s.Status === 'Warning').length; + + const tabs = useMemo(() => { + const baseTabs: { key: TabKey; title: string; icon: typeof InfoIcon; badge?: number }[] = [ + { key: 'info', title: t('call_detail.tabs.info'), icon: InfoIcon }, + { key: 'contact', title: t('call_detail.tabs.contact'), icon: UserIcon }, + { key: 'protocols', title: t('call_detail.tabs.protocols'), icon: FileTextIcon }, + { key: 'dispatched', title: t('call_detail.tabs.dispatched'), icon: UsersIcon }, + { key: 'timeline', title: t('call_detail.tabs.timeline'), icon: ClockIcon, badge: callExtraData?.Activity?.length || 0 }, + { key: 'video', title: t('call_detail.tabs.video'), icon: VideoIcon }, + ]; + if (call?.CheckInTimersEnabled) { + baseTabs.push({ + key: 'checkin' as const, + title: t('check_in.tab_title'), + icon: ShieldCheckIcon, + badge: overdueCount + warningCount, + }); + } + return baseTabs; + }, [t, callExtraData?.Activity?.length, call?.CheckInTimersEnabled, overdueCount, warningCount]); if (isLoading) { return ( @@ -279,6 +295,18 @@ export default function CallDetailWeb() { )} ); + case 'video': + return ( + + + + ); + case 'checkin': + return call?.CheckInTimersEnabled ? ( + + + + ) : null; case 'timeline': return ( diff --git a/src/app/call/[id]/edit.tsx b/src/app/call/[id]/edit.tsx index ccd15d5..2c8c8e5 100644 --- a/src/app/call/[id]/edit.tsx +++ b/src/app/call/[id]/edit.tsx @@ -81,7 +81,7 @@ export default function EditCall() { const { id } = useLocalSearchParams(); const callId = Array.isArray(id) ? id[0] : id; const { callPriorities, callTypes, isLoading: callDataLoading, error: callDataError, fetchCallPriorities, fetchCallTypes } = useCallsStore(); - const { call, isLoading: callDetailLoading, error: callDetailError, fetchCallDetail } = useCallDetailStore(); + const { call, callExtraData, isLoading: callDetailLoading, error: callDetailError, fetchCallDetail } = useCallDetailStore(); const { config } = useCoreStore(); const toast = useToast(); const [showLocationPicker, setShowLocationPicker] = useState(false); @@ -153,6 +153,32 @@ export default function EditCall() { const priority = callPriorities.find((p) => p.Id === call.Priority); const type = callTypes.find((t) => t.Id === call.Type); + // Build dispatch selection from existing dispatches + const initialDispatch: DispatchSelection = { + everyone: false, + users: [], + groups: [], + roles: [], + units: [], + }; + + if (callExtraData?.Dispatches) { + callExtraData.Dispatches.forEach((dispatch) => { + const dispatchType = (dispatch.Type || '').toLowerCase(); + if (dispatchType === 'personnel' || dispatchType === 'p' || dispatchType === 'user') { + initialDispatch.users.push(dispatch.Id); + } else if (dispatchType === 'group' || dispatchType === 'groups' || dispatchType === 'g') { + initialDispatch.groups.push(dispatch.Id); + } else if (dispatchType === 'role' || dispatchType === 'roles' || dispatchType === 'r') { + initialDispatch.roles.push(dispatch.Id); + } else if (dispatchType === 'unit' || dispatchType === 'units' || dispatchType === 'u') { + initialDispatch.units.push(dispatch.Id); + } + }); + } + + setDispatchSelection(initialDispatch); + reset({ name: call.Name || '', nature: call.Nature || '', @@ -167,13 +193,7 @@ export default function EditCall() { type: type?.Name || '', contactName: call.ContactName || '', contactInfo: call.ContactInfo || '', - dispatchSelection: { - everyone: false, - users: [], - groups: [], - roles: [], - units: [], - }, + dispatchSelection: initialDispatch, }); // Set selected location if coordinates exist @@ -185,7 +205,7 @@ export default function EditCall() { }); } } - }, [call, callPriorities, callTypes, reset]); + }, [call, callExtraData, callPriorities, callTypes, reset]); // Track when edit call view is rendered useEffect(() => { @@ -220,7 +240,7 @@ export default function EditCall() { name: data.name, nature: data.nature, priority: priority?.Id || 0, - type: type?.Id || '', + type: type?.Name || '', note: data.note, address: data.address, latitude: data.latitude, diff --git a/src/app/call/[id]/edit.web.tsx b/src/app/call/[id]/edit.web.tsx index 955ff63..c659546 100644 --- a/src/app/call/[id]/edit.web.tsx +++ b/src/app/call/[id]/edit.web.tsx @@ -180,7 +180,7 @@ export default function EditCallWeb() { const { width } = useWindowDimensions(); const { callPriorities, callTypes, isLoading: callDataLoading, error: callDataError, fetchCallPriorities, fetchCallTypes } = useCallsStore(); - const { call, isLoading: callDetailLoading, error: callDetailError, fetchCallDetail } = useCallDetailStore(); + const { call, callExtraData, isLoading: callDetailLoading, error: callDetailError, fetchCallDetail } = useCallDetailStore(); const { config } = useCoreStore(); const toast = useToast(); @@ -255,6 +255,32 @@ export default function EditCallWeb() { const priority = callPriorities.find((p) => p.Id === call.Priority); const type = callTypes.find((t) => t.Id === call.Type); + // Build dispatch selection from existing dispatches + const initialDispatch: DispatchSelection = { + everyone: false, + users: [], + groups: [], + roles: [], + units: [], + }; + + if (callExtraData?.Dispatches) { + callExtraData.Dispatches.forEach((dispatch) => { + const dispatchType = (dispatch.Type || '').toLowerCase(); + if (dispatchType === 'personnel' || dispatchType === 'p' || dispatchType === 'user') { + initialDispatch.users.push(dispatch.Id); + } else if (dispatchType === 'group' || dispatchType === 'groups' || dispatchType === 'g') { + initialDispatch.groups.push(dispatch.Id); + } else if (dispatchType === 'role' || dispatchType === 'roles' || dispatchType === 'r') { + initialDispatch.roles.push(dispatch.Id); + } else if (dispatchType === 'unit' || dispatchType === 'units' || dispatchType === 'u') { + initialDispatch.units.push(dispatch.Id); + } + }); + } + + setDispatchSelection(initialDispatch); + reset({ name: call.Name || '', nature: call.Nature || '', @@ -269,13 +295,7 @@ export default function EditCallWeb() { type: type?.Name || '', contactName: call.ContactName || '', contactInfo: call.ContactInfo || '', - dispatchSelection: { - everyone: false, - users: [], - groups: [], - roles: [], - units: [], - }, + dispatchSelection: initialDispatch, }); if (call.Latitude && call.Longitude) { @@ -286,7 +306,7 @@ export default function EditCallWeb() { }); } } - }, [call, callPriorities, callTypes, reset]); + }, [call, callExtraData, callPriorities, callTypes, reset]); useEffect(() => { if (call) { @@ -352,7 +372,7 @@ export default function EditCallWeb() { name: data.name, nature: data.nature, priority: priority?.Id || 0, - type: type?.Id || '', + type: type?.Name || '', note: data.note, address: data.address, latitude: data.latitude, diff --git a/src/app/call/new/index.tsx b/src/app/call/new/index.tsx index 0535147..cd609b7 100644 --- a/src/app/call/new/index.tsx +++ b/src/app/call/new/index.tsx @@ -250,7 +250,7 @@ export default function NewCall() { name: data.name, nature: data.nature, priority: priority.Id, - type: type.Id, + type: type.Name, note: data.note, address: data.address, latitude: data.latitude, @@ -277,8 +277,8 @@ export default function NewCall() { // Show success toast toast.success(t('calls.create_success')); - // Navigate back to calls list - router.push('/calls' as Href); + // Navigate back to home dashboard + router.push('/(app)/home' as Href); } catch (error) { console.error('Error creating call:', error); diff --git a/src/app/call/new/index.web.tsx b/src/app/call/new/index.web.tsx index 9b21655..2431085 100644 --- a/src/app/call/new/index.web.tsx +++ b/src/app/call/new/index.web.tsx @@ -411,7 +411,7 @@ export default function NewCallWeb() { name: data.name, nature: data.nature, priority: priority.Id, - type: type.Id, + type: type.Name, note: data.note, address: data.address, latitude: data.latitude, @@ -437,7 +437,7 @@ export default function NewCallWeb() { } toast.success(t('calls.create_success')); - router.push('/calls' as Href); + router.push('/(app)/home' as Href); } catch (err) { console.error('Error creating call:', err); toast.error(t('calls.create_error')); diff --git a/src/app/personnel/[id].tsx b/src/app/personnel/[id].tsx new file mode 100644 index 0000000..2039d7c --- /dev/null +++ b/src/app/personnel/[id].tsx @@ -0,0 +1,360 @@ +import { format } from 'date-fns'; +import { Stack, useLocalSearchParams, useRouter } from 'expo-router'; +import { Building2, Circle, Clock, Mail, MapPin, Phone, Shield, User, Users } from 'lucide-react-native'; +import React, { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Linking, Pressable, ScrollView, StyleSheet, View } from 'react-native'; + +import { getPersonnelInfo } from '@/api/personnel/personnel'; +import { Loading } from '@/components/common/loading'; +import ZeroState from '@/components/common/zero-state'; +import { Box } from '@/components/ui/box'; +import { Button, ButtonText } from '@/components/ui/button'; +import { FocusAwareStatusBar } from '@/components/ui/focus-aware-status-bar'; +import { Heading } from '@/components/ui/heading'; +import { HStack } from '@/components/ui/hstack'; +import { Icon } from '@/components/ui/icon'; +import { Text } from '@/components/ui/text'; +import { VStack } from '@/components/ui/vstack'; +import { useAnalytics } from '@/hooks/use-analytics'; +import { type PersonnelInfoResultData } from '@/models/v4/personnel/personnelInfoResultData'; + +export default function PersonnelDetail() { + const { id } = useLocalSearchParams(); + const userId = Array.isArray(id) ? id[0] : id; + const router = useRouter(); + const { t } = useTranslation(); + const { trackEvent } = useAnalytics(); + + const [person, setPerson] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchDetail = async () => { + if (!userId) return; + try { + setIsLoading(true); + setError(null); + const response = await getPersonnelInfo(userId); + setPerson(response.Data || null); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load personnel details'); + } finally { + setIsLoading(false); + } + }; + fetchDetail(); + }, [userId]); + + useEffect(() => { + if (person) { + trackEvent('personnel_detail_viewed', { + userId: person.UserId, + name: `${person.FirstName} ${person.LastName}`, + }); + } + }, [person, trackEvent]); + + const handleBack = () => { + router.back(); + }; + + const handleCall = () => { + if (person?.MobilePhone) { + Linking.openURL(`tel:${person.MobilePhone}`); + } + }; + + const handleEmail = () => { + if (person?.EmailAddress) { + Linking.openURL(`mailto:${person.EmailAddress}`); + } + }; + + const formatTimestamp = (timestamp: string) => { + if (!timestamp) return ''; + try { + return format(new Date(timestamp), 'MMM d, yyyy h:mm a'); + } catch { + return timestamp; + } + }; + + const screenTitle = person ? `${person.FirstName} ${person.LastName}` : t('personnel.details'); + + if (isLoading) { + return ( + <> + + + + + ); + } + + if (error) { + return ( + <> + + + + + ); + } + + if (!person) { + return ( + <> + + + + + ); + } + + const statusColor = person.StatusColor || '#6b7280'; + const staffingColor = person.StaffingColor || '#6b7280'; + + return ( + <> + + + + + ); +} + +const styles = StyleSheet.create({ + scrollContent: { + flexGrow: 1, + }, + avatarLarge: { + width: 72, + height: 72, + borderRadius: 36, + borderWidth: 3, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: 'rgba(139, 92, 246, 0.08)', + }, + bottomSpacer: { + height: 32, + }, +}); diff --git a/src/app/units/[id].tsx b/src/app/units/[id].tsx new file mode 100644 index 0000000..dafa767 --- /dev/null +++ b/src/app/units/[id].tsx @@ -0,0 +1,337 @@ +import { format } from 'date-fns'; +import { Stack, useLocalSearchParams, useRouter } from 'expo-router'; +import { Building2, Circle, Clock, MapPin, Shield, Truck, User } from 'lucide-react-native'; +import React, { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { ScrollView, StyleSheet, View } from 'react-native'; + +import { getUnitsInfos } from '@/api/units/units'; +import { Loading } from '@/components/common/loading'; +import ZeroState from '@/components/common/zero-state'; +import { Box } from '@/components/ui/box'; +import { Button, ButtonText } from '@/components/ui/button'; +import { FocusAwareStatusBar } from '@/components/ui/focus-aware-status-bar'; +import { Heading } from '@/components/ui/heading'; +import { HStack } from '@/components/ui/hstack'; +import { Icon } from '@/components/ui/icon'; +import { Text } from '@/components/ui/text'; +import { VStack } from '@/components/ui/vstack'; +import { useAnalytics } from '@/hooks/use-analytics'; +import { type UnitInfoResultData } from '@/models/v4/units/unitInfoResultData'; + +export default function UnitDetail() { + const { id } = useLocalSearchParams(); + const unitId = Array.isArray(id) ? id[0] : id; + const router = useRouter(); + const { t } = useTranslation(); + const { trackEvent } = useAnalytics(); + + const [unit, setUnit] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchDetail = async () => { + if (!unitId) { + setIsLoading(false); + setError('Missing unit id'); + setUnit(null); + return; + } + try { + setIsLoading(true); + setError(null); + const response = await getUnitsInfos(''); + const found = response.Data?.find((u: UnitInfoResultData) => u.UnitId === unitId) || null; + setUnit(found); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load unit details'); + } finally { + setIsLoading(false); + } + }; + fetchDetail(); + }, [unitId]); + + useEffect(() => { + if (unit) { + trackEvent('unit_detail_viewed', { + unitId: unit.UnitId, + name: unit.Name, + }); + } + }, [unit, trackEvent]); + + const handleBack = () => { + router.back(); + }; + + const formatTimestamp = (timestamp: string) => { + if (!timestamp) return ''; + try { + return format(new Date(timestamp), 'MMM d, yyyy h:mm a'); + } catch { + return timestamp; + } + }; + + const screenTitle = unit ? unit.Name : t('units.details'); + + if (isLoading) { + return ( + <> + + + + + ); + } + + if (error) { + return ( + <> + + + + + ); + } + + if (!unit) { + return ( + <> + + + + + ); + } + + const statusColor = unit.CurrentStatusColor || '#6b7280'; + + return ( + <> + + + + + ); +} + +const styles = StyleSheet.create({ + scrollContent: { + flexGrow: 1, + }, + iconLarge: { + width: 72, + height: 72, + borderRadius: 18, + borderWidth: 3, + alignItems: 'center', + justifyContent: 'center', + }, + bottomSpacer: { + height: 32, + }, +}); diff --git a/src/app/weather-alerts/[id].tsx b/src/app/weather-alerts/[id].tsx new file mode 100644 index 0000000..bfbf577 --- /dev/null +++ b/src/app/weather-alerts/[id].tsx @@ -0,0 +1,236 @@ +import { Stack, useLocalSearchParams } from 'expo-router'; +import { AlertTriangle, Cloud, Flame, Heart, Leaf } from 'lucide-react-native'; +import { useColorScheme } from 'nativewind'; +import React, { useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { ActivityIndicator, Pressable, ScrollView, StyleSheet, View } from 'react-native'; + +import { HStack } from '@/components/ui/hstack'; +import { Text } from '@/components/ui/text'; +import { VStack } from '@/components/ui/vstack'; +import { SEVERITY_COLORS, WeatherAlertCategory, WeatherAlertCertainty, WeatherAlertSeverity, WeatherAlertUrgency } from '@/models/v4/weatherAlerts/weatherAlertEnums'; +import { useWeatherAlertsStore } from '@/stores/weatherAlerts/store'; + +const getCategoryIcon = (category: number) => { + switch (category) { + case WeatherAlertCategory.Fire: + return Flame; + case WeatherAlertCategory.Health: + return Heart; + case WeatherAlertCategory.Env: + return Leaf; + case WeatherAlertCategory.Met: + return Cloud; + default: + return AlertTriangle; + } +}; + +const getSeverityLabel = (severity: number, t: (key: string) => string): string => { + const map: Record = { + [WeatherAlertSeverity.Extreme]: t('weatherAlerts.severity.extreme'), + [WeatherAlertSeverity.Severe]: t('weatherAlerts.severity.severe'), + [WeatherAlertSeverity.Moderate]: t('weatherAlerts.severity.moderate'), + [WeatherAlertSeverity.Minor]: t('weatherAlerts.severity.minor'), + [WeatherAlertSeverity.Unknown]: t('weatherAlerts.severity.unknown'), + }; + return map[severity] ?? map[WeatherAlertSeverity.Unknown]; +}; + +const getUrgencyLabel = (urgency: number, t: (key: string) => string): string => { + const map: Record = { + [WeatherAlertUrgency.Immediate]: t('weatherAlerts.urgency.immediate'), + [WeatherAlertUrgency.Expected]: t('weatherAlerts.urgency.expected'), + [WeatherAlertUrgency.Future]: t('weatherAlerts.urgency.future'), + [WeatherAlertUrgency.Past]: t('weatherAlerts.urgency.past'), + [WeatherAlertUrgency.Unknown]: t('weatherAlerts.urgency.unknown'), + }; + return map[urgency] ?? map[WeatherAlertUrgency.Unknown]; +}; + +const getCertaintyLabel = (certainty: number, t: (key: string) => string): string => { + const map: Record = { + [WeatherAlertCertainty.Observed]: t('weatherAlerts.certainty.observed'), + [WeatherAlertCertainty.Likely]: t('weatherAlerts.certainty.likely'), + [WeatherAlertCertainty.Possible]: t('weatherAlerts.certainty.possible'), + [WeatherAlertCertainty.Unlikely]: t('weatherAlerts.certainty.unlikely'), + [WeatherAlertCertainty.Unknown]: t('weatherAlerts.certainty.unknown'), + }; + return map[certainty] ?? map[WeatherAlertCertainty.Unknown]; +}; + +export default function WeatherAlertDetailScreen() { + const { id } = useLocalSearchParams<{ id: string }>(); + const { t } = useTranslation(); + const { colorScheme } = useColorScheme(); + const isDark = colorScheme === 'dark'; + + const { selectedAlert, isLoadingDetail, fetchAlertDetail } = useWeatherAlertsStore(); + + useEffect(() => { + if (id) { + fetchAlertDetail(id); + } + }, [id, fetchAlertDetail]); + + if (isLoadingDetail) { + return ( + <> + + + + + + ); + } + + if (!selectedAlert) { + return ( + <> + + + {t('common.no_results_found')} + { + if (id) fetchAlertDetail(id); + }} + style={{ paddingHorizontal: 16, paddingVertical: 8, backgroundColor: '#3b82f6', borderRadius: 8 }} + > + {t('common.retry')} + + + + ); + } + + const alert = selectedAlert; + const severityColor = SEVERITY_COLORS[alert.Severity] ?? SEVERITY_COLORS[WeatherAlertSeverity.Unknown]; + const CategoryIcon = getCategoryIcon(alert.AlertCategory); + + return ( + <> + + + + {/* Header */} + + + + + {alert.Event} + {getSeverityLabel(alert.Severity, t)} + + + + + {/* Headline */} + {alert.Headline ? ( + + {t('weatherAlerts.detail.headline')} + {alert.Headline} + + ) : null} + + {/* Timing */} + + + + {alert.OnsetUtc && } + {alert.ExpiresUtc && } + {alert.SentUtc && } + + + + {/* Area */} + {alert.AreaDescription ? ( + + {t('weatherAlerts.detail.area')} + {alert.AreaDescription} + + ) : null} + + {/* Description */} + {alert.Description ? ( + + {t('weatherAlerts.detail.description')} + {alert.Description} + + ) : null} + + {/* Instructions */} + {alert.Instruction ? ( + + {t('weatherAlerts.detail.instruction')} + {alert.Instruction} + + ) : null} + + {/* Metadata */} + + + + + {alert.Sender ? : null} + + + + + + ); +} + +const DetailRow: React.FC<{ label: string; value: string; isDark: boolean }> = ({ label, value, isDark }) => ( + + {label} + + {value} + + +); + +const styles = StyleSheet.create({ + container: { flex: 1 }, + containerLight: { backgroundColor: '#f3f4f6' }, + containerDark: { backgroundColor: '#030712' }, + centered: { justifyContent: 'center', alignItems: 'center' }, + scrollContent: { paddingBottom: 32 }, + header: { + padding: 16, + }, + section: { + marginHorizontal: 12, + marginTop: 10, + padding: 12, + borderRadius: 8, + }, + sectionLight: { backgroundColor: '#ffffff' }, + sectionDark: { backgroundColor: '#1f2937' }, + instructionSection: { + marginHorizontal: 12, + marginTop: 10, + padding: 12, + borderRadius: 8, + borderLeftWidth: 4, + borderLeftColor: '#F57C00', + }, + instructionLight: { backgroundColor: '#FFF8E1' }, + instructionDark: { backgroundColor: '#2d2200' }, +}); diff --git a/src/components/callVideoFeeds/video-feed-card.tsx b/src/components/callVideoFeeds/video-feed-card.tsx new file mode 100644 index 0000000..a3e20e0 --- /dev/null +++ b/src/components/callVideoFeeds/video-feed-card.tsx @@ -0,0 +1,190 @@ +import { CameraIcon, CircleIcon, CloudIcon, EllipsisVerticalIcon, EyeIcon, GlobeIcon, MonitorIcon, PencilIcon, PlayIcon, SatelliteIcon, TrafficConeIcon, Trash2Icon, VideoIcon } from 'lucide-react-native'; +import { useColorScheme } from 'nativewind'; +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Pressable } from 'react-native'; + +import { Box } from '@/components/ui/box'; +import { Button, ButtonIcon, ButtonText } from '@/components/ui/button'; +import { HStack } from '@/components/ui/hstack'; +import { Text } from '@/components/ui/text'; +import { VStack } from '@/components/ui/vstack'; +import { CallVideoFeedFormat, CallVideoFeedStatus, CallVideoFeedType } from '@/models/v4/callVideoFeeds/callVideoFeedEnums'; +import { type CallVideoFeedResultData } from '@/models/v4/callVideoFeeds/callVideoFeedResultData'; + +interface VideoFeedCardProps { + feed: CallVideoFeedResultData; + onWatch: (feed: CallVideoFeedResultData) => void; + onEdit?: (feed: CallVideoFeedResultData) => void; + onDelete?: (feedId: string) => void; + canEdit: boolean; +} + +const getFeedTypeIcon = (feedType: number | null) => { + switch (feedType) { + case CallVideoFeedType.Drone: + return GlobeIcon; + case CallVideoFeedType.FixedCamera: + return CameraIcon; + case CallVideoFeedType.BodyCam: + return EyeIcon; + case CallVideoFeedType.TrafficCam: + return TrafficConeIcon; + case CallVideoFeedType.WeatherCam: + return CloudIcon; + case CallVideoFeedType.SatelliteFeed: + return SatelliteIcon; + case CallVideoFeedType.WebCam: + return MonitorIcon; + default: + return VideoIcon; + } +}; + +const getFeedTypeName = (feedType: number | null, t: (key: string) => string): string => { + switch (feedType) { + case CallVideoFeedType.Drone: + return t('videoFeeds.type.drone'); + case CallVideoFeedType.FixedCamera: + return t('videoFeeds.type.fixedCamera'); + case CallVideoFeedType.BodyCam: + return t('videoFeeds.type.bodyCam'); + case CallVideoFeedType.TrafficCam: + return t('videoFeeds.type.trafficCam'); + case CallVideoFeedType.WeatherCam: + return t('videoFeeds.type.weatherCam'); + case CallVideoFeedType.SatelliteFeed: + return t('videoFeeds.type.satelliteFeed'); + case CallVideoFeedType.WebCam: + return t('videoFeeds.type.webCam'); + default: + return t('videoFeeds.type.other'); + } +}; + +const getFeedFormatName = (feedFormat: number | null, t: (key: string) => string): string => { + switch (feedFormat) { + case CallVideoFeedFormat.RTSP: + return t('videoFeeds.format.rtsp'); + case CallVideoFeedFormat.HLS: + return t('videoFeeds.format.hls'); + case CallVideoFeedFormat.MJPEG: + return t('videoFeeds.format.mjpeg'); + case CallVideoFeedFormat.YouTubeLive: + return t('videoFeeds.format.youtubeLive'); + case CallVideoFeedFormat.WebRTC: + return t('videoFeeds.format.webrtc'); + case CallVideoFeedFormat.DASH: + return t('videoFeeds.format.dash'); + case CallVideoFeedFormat.Embed: + return t('videoFeeds.format.embed'); + default: + return t('videoFeeds.format.other'); + } +}; + +const getStatusColor = (status: number): string => { + switch (status) { + case CallVideoFeedStatus.Active: + return '#22c55e'; + case CallVideoFeedStatus.Inactive: + return '#9ca3af'; + case CallVideoFeedStatus.Error: + return '#ef4444'; + default: + return '#9ca3af'; + } +}; + +const getStatusName = (status: number, t: (key: string) => string): string => { + switch (status) { + case CallVideoFeedStatus.Active: + return t('videoFeeds.status.active'); + case CallVideoFeedStatus.Inactive: + return t('videoFeeds.status.inactive'); + case CallVideoFeedStatus.Error: + return t('videoFeeds.status.error'); + default: + return t('videoFeeds.status.inactive'); + } +}; + +export const VideoFeedCard: React.FC = ({ feed, onWatch, onEdit, onDelete, canEdit }) => { + const { t } = useTranslation(); + const { colorScheme } = useColorScheme(); + const isDark = colorScheme === 'dark'; + const [showMenu, setShowMenu] = useState(false); + const TypeIcon = getFeedTypeIcon(feed.FeedType); + + return ( + + + + + + + {feed.Name} + + + + {getStatusName(feed.Status, t)} + + + + + {getFeedTypeName(feed.FeedType, t)} · {getFeedFormatName(feed.FeedFormat, t)} + + + {feed.FullName} · {feed.AddedOnFormatted} + + + + + + + + {canEdit && ( + setShowMenu(!showMenu)}> + + + )} + + + + {showMenu && canEdit && ( + + {onEdit && ( + + )} + {onDelete && ( + + )} + + )} + + ); +}; diff --git a/src/components/callVideoFeeds/video-feed-form-sheet.tsx b/src/components/callVideoFeeds/video-feed-form-sheet.tsx new file mode 100644 index 0000000..94c15d1 --- /dev/null +++ b/src/components/callVideoFeeds/video-feed-form-sheet.tsx @@ -0,0 +1,366 @@ +import { useColorScheme } from 'nativewind'; +import React, { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { ScrollView } from 'react-native'; + +import { CustomBottomSheet } from '@/components/ui/bottom-sheet'; +import { Box } from '@/components/ui/box'; +import { Button, ButtonText } from '@/components/ui/button'; +import { FormControl, FormControlError, FormControlLabel, FormControlLabelText } from '@/components/ui/form-control'; +import { Heading } from '@/components/ui/heading'; +import { HStack } from '@/components/ui/hstack'; +import { Input, InputField } from '@/components/ui/input'; +import { Select, SelectBackdrop, SelectContent, SelectIcon, SelectInput, SelectItem, SelectPortal, SelectTrigger } from '@/components/ui/select'; +import { Text } from '@/components/ui/text'; +import { Textarea, TextareaInput } from '@/components/ui/textarea'; +import { VStack } from '@/components/ui/vstack'; +import { CallVideoFeedFormat, CallVideoFeedStatus, CallVideoFeedType } from '@/models/v4/callVideoFeeds/callVideoFeedEnums'; +import { type CallVideoFeedResultData } from '@/models/v4/callVideoFeeds/callVideoFeedResultData'; +import { useLocationStore } from '@/stores/app/location-store'; +import { useCallVideoFeedsStore } from '@/stores/callVideoFeeds/store'; +import { useToastStore } from '@/stores/toast/store'; + +interface VideoFeedFormSheetProps { + isOpen: boolean; + onClose: () => void; + callId: string; + editFeed?: CallVideoFeedResultData | null; +} + +function autoDetectFormat(url: string): string | undefined { + if (!url) return undefined; + const lower = url.toLowerCase(); + if (lower.includes('.m3u8')) return 'HLS'; + if (lower.startsWith('rtsp://')) return 'RTSP'; + if (lower.includes('youtube.com/live') || lower.includes('youtu.be') || lower.includes('youtube.com/watch')) return 'YouTubeLive'; + if (lower.includes('.mpd')) return 'DASH'; + if (lower.includes('mjpeg') || lower.includes('mjpg')) return 'MJPEG'; + return undefined; +} + +const FEED_TYPE_OPTIONS = [ + { value: 'Drone', numericValue: CallVideoFeedType.Drone, labelKey: 'videoFeeds.type.drone' }, + { value: 'FixedCamera', numericValue: CallVideoFeedType.FixedCamera, labelKey: 'videoFeeds.type.fixedCamera' }, + { value: 'BodyCam', numericValue: CallVideoFeedType.BodyCam, labelKey: 'videoFeeds.type.bodyCam' }, + { value: 'TrafficCam', numericValue: CallVideoFeedType.TrafficCam, labelKey: 'videoFeeds.type.trafficCam' }, + { value: 'WeatherCam', numericValue: CallVideoFeedType.WeatherCam, labelKey: 'videoFeeds.type.weatherCam' }, + { value: 'SatelliteFeed', numericValue: CallVideoFeedType.SatelliteFeed, labelKey: 'videoFeeds.type.satelliteFeed' }, + { value: 'WebCam', numericValue: CallVideoFeedType.WebCam, labelKey: 'videoFeeds.type.webCam' }, + { value: 'Other', numericValue: CallVideoFeedType.Other, labelKey: 'videoFeeds.type.other' }, +]; + +const FEED_FORMAT_OPTIONS = [ + { value: 'RTSP', numericValue: CallVideoFeedFormat.RTSP, labelKey: 'videoFeeds.format.rtsp' }, + { value: 'HLS', numericValue: CallVideoFeedFormat.HLS, labelKey: 'videoFeeds.format.hls' }, + { value: 'MJPEG', numericValue: CallVideoFeedFormat.MJPEG, labelKey: 'videoFeeds.format.mjpeg' }, + { value: 'YouTubeLive', numericValue: CallVideoFeedFormat.YouTubeLive, labelKey: 'videoFeeds.format.youtubeLive' }, + { value: 'WebRTC', numericValue: CallVideoFeedFormat.WebRTC, labelKey: 'videoFeeds.format.webrtc' }, + { value: 'DASH', numericValue: CallVideoFeedFormat.DASH, labelKey: 'videoFeeds.format.dash' }, + { value: 'Embed', numericValue: CallVideoFeedFormat.Embed, labelKey: 'videoFeeds.format.embed' }, + { value: 'Other_Format', numericValue: CallVideoFeedFormat.Other, labelKey: 'videoFeeds.format.other' }, +]; + +const FEED_STATUS_OPTIONS = [ + { value: 'Active', numericValue: CallVideoFeedStatus.Active, labelKey: 'videoFeeds.status.active' }, + { value: 'Inactive', numericValue: CallVideoFeedStatus.Inactive, labelKey: 'videoFeeds.status.inactive' }, + { value: 'Error', numericValue: CallVideoFeedStatus.Error, labelKey: 'videoFeeds.status.error' }, +]; + +function feedTypeNumToName(num: number | null): string { + return FEED_TYPE_OPTIONS.find((o) => o.numericValue === num)?.value ?? 'Other'; +} + +function feedFormatNumToName(num: number | null): string { + return FEED_FORMAT_OPTIONS.find((o) => o.numericValue === num)?.value ?? 'Other_Format'; +} + +function feedStatusNumToName(num: number): string { + return FEED_STATUS_OPTIONS.find((o) => o.numericValue === num)?.value ?? 'Active'; +} + +function feedTypeNameToNum(name: string): number { + return FEED_TYPE_OPTIONS.find((o) => o.value === name)?.numericValue ?? CallVideoFeedType.Other; +} + +function feedFormatNameToNum(name: string): number { + return FEED_FORMAT_OPTIONS.find((o) => o.value === name)?.numericValue ?? CallVideoFeedFormat.Other; +} + +function feedStatusNameToNum(name: string): number { + return FEED_STATUS_OPTIONS.find((o) => o.value === name)?.numericValue ?? CallVideoFeedStatus.Active; +} + +export const VideoFeedFormSheet: React.FC = ({ isOpen, onClose, callId, editFeed }) => { + const { t } = useTranslation(); + const { colorScheme } = useColorScheme(); + const showToast = useToastStore((state) => state.showToast); + const { addFeed, updateFeed, isSaving } = useCallVideoFeedsStore(); + const userLocation = useLocationStore((state) => ({ + latitude: state.latitude, + longitude: state.longitude, + })); + + const isEditing = !!editFeed; + + const [name, setName] = useState(''); + const [url, setUrl] = useState(''); + const [feedType, setFeedType] = useState('Other'); + const [feedFormat, setFeedFormat] = useState('Other_Format'); + const [description, setDescription] = useState(''); + const [status, setStatus] = useState('Active'); + const [latitude, setLatitude] = useState(''); + const [longitude, setLongitude] = useState(''); + const [sortOrder, setSortOrder] = useState('0'); + const [errors, setErrors] = useState<{ name?: string; url?: string }>({}); + + useEffect(() => { + if (isOpen) { + if (editFeed) { + setName(editFeed.Name); + setUrl(editFeed.Url); + setFeedType(feedTypeNumToName(editFeed.FeedType)); + setFeedFormat(feedFormatNumToName(editFeed.FeedFormat)); + setDescription(editFeed.Description || ''); + setStatus(feedStatusNumToName(editFeed.Status)); + setLatitude(editFeed.Latitude != null ? String(editFeed.Latitude) : ''); + setLongitude(editFeed.Longitude != null ? String(editFeed.Longitude) : ''); + setSortOrder(String(editFeed.SortOrder)); + } else { + setName(''); + setUrl(''); + setFeedType('Other'); + setFeedFormat('Other_Format'); + setDescription(''); + setStatus('Active'); + setLatitude(''); + setLongitude(''); + setSortOrder('0'); + } + setErrors({}); + } + }, [isOpen, editFeed]); + + const handleUrlChange = (newUrl: string) => { + setUrl(newUrl); + const detected = autoDetectFormat(newUrl); + if (detected !== undefined) { + setFeedFormat(String(detected)); + } + }; + + const handleUseCurrentLocation = () => { + if (userLocation.latitude && userLocation.longitude) { + setLatitude(String(userLocation.latitude)); + setLongitude(String(userLocation.longitude)); + } + }; + + const validate = (): boolean => { + const newErrors: { name?: string; url?: string } = {}; + if (!name.trim()) newErrors.name = t('videoFeeds.form.name') + ' is required'; + if (!url.trim()) newErrors.url = t('videoFeeds.form.url') + ' is required'; + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleSubmit = async () => { + if (!validate()) return; + + if (isEditing && editFeed) { + const success = await updateFeed({ + CallVideoFeedId: editFeed.CallVideoFeedId, + CallId: callId, + Name: name.trim(), + Url: url.trim(), + FeedType: feedTypeNameToNum(feedType), + FeedFormat: feedFormatNameToNum(feedFormat), + Description: description.trim() || undefined, + Status: feedStatusNameToNum(status), + Latitude: latitude || undefined, + Longitude: longitude || undefined, + SortOrder: parseInt(sortOrder) || 0, + }); + if (success) { + showToast('success', t('videoFeeds.feedUpdated')); + onClose(); + } else { + showToast('error', t('videoFeeds.feedError')); + } + } else { + const feedId = await addFeed({ + CallId: callId, + Name: name.trim(), + Url: url.trim(), + FeedType: feedTypeNameToNum(feedType), + FeedFormat: feedFormatNameToNum(feedFormat), + Description: description.trim() || undefined, + Latitude: latitude || undefined, + Longitude: longitude || undefined, + SortOrder: parseInt(sortOrder) || 0, + }); + if (feedId) { + showToast('success', t('videoFeeds.feedAdded')); + onClose(); + } else { + showToast('error', t('videoFeeds.feedError')); + } + } + }; + + return ( + + + + {isEditing ? t('videoFeeds.editFeed') : t('videoFeeds.addFeed')} + + {/* Name */} + + + {t('videoFeeds.form.name')} * + + + + + {errors.name && ( + + {errors.name} + + )} + + + {/* URL */} + + + {t('videoFeeds.form.url')} * + + + + + {errors.url && ( + + {errors.url} + + )} + + + {/* Feed Type */} + + + {t('videoFeeds.form.feedType')} + + + + + {/* Feed Format */} + + + {t('videoFeeds.form.feedFormat')} + + + + + {/* Description */} + + + {t('videoFeeds.form.description')} + + + + + {/* Status (edit only) */} + {isEditing && ( + + + {t('videoFeeds.form.status')} + + + + )} + + {/* Camera Location */} + + + {t('videoFeeds.form.cameraLocation')} + + + + + + + + + + + + + + + + + {/* Sort Order */} + + + {t('videoFeeds.form.sortOrder')} + + + + + + + {/* Submit */} + + + + + + + + ); +}; diff --git a/src/components/callVideoFeeds/video-feeds-tab.tsx b/src/components/callVideoFeeds/video-feeds-tab.tsx new file mode 100644 index 0000000..a6d4e0f --- /dev/null +++ b/src/components/callVideoFeeds/video-feeds-tab.tsx @@ -0,0 +1,133 @@ +import { PlusIcon } from 'lucide-react-native'; +import { useColorScheme } from 'nativewind'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Alert, FlatList } from 'react-native'; + +import { Box } from '@/components/ui/box'; +import { Button, ButtonIcon, ButtonText } from '@/components/ui/button'; +import { Text } from '@/components/ui/text'; +import { VStack } from '@/components/ui/vstack'; +import { type CallVideoFeedResultData } from '@/models/v4/callVideoFeeds/callVideoFeedResultData'; +import { useCallVideoFeedsStore } from '@/stores/callVideoFeeds/store'; +import { useToastStore } from '@/stores/toast/store'; + +import { VideoFeedCard } from './video-feed-card'; +import { VideoFeedFormSheet } from './video-feed-form-sheet'; +import { VideoPlayer } from './video-player'; + +interface VideoFeedsTabProps { + callId: string; + canEdit: boolean; +} + +export const VideoFeedsTab: React.FC = ({ callId, canEdit }) => { + const { t } = useTranslation(); + const { colorScheme } = useColorScheme(); + const showToast = useToastStore((state) => state.showToast); + const { feeds, isLoading, error, fetchFeeds, removeFeed, reset } = useCallVideoFeedsStore(); + + const [isFormOpen, setIsFormOpen] = useState(false); + const [editingFeed, setEditingFeed] = useState(null); + const [watchingFeed, setWatchingFeed] = useState(null); + + useEffect(() => { + if (callId) { + fetchFeeds(callId); + } + return () => reset(); + }, [callId, fetchFeeds, reset]); + + const handleWatch = useCallback((feed: CallVideoFeedResultData) => { + setWatchingFeed(feed); + }, []); + + const handleEdit = useCallback((feed: CallVideoFeedResultData) => { + setEditingFeed(feed); + setIsFormOpen(true); + }, []); + + const handleDelete = useCallback( + (feedId: string) => { + Alert.alert(t('videoFeeds.deleteFeed'), t('videoFeeds.deleteConfirm'), [ + { text: t('common.cancel'), style: 'cancel' }, + { + text: t('common.delete'), + style: 'destructive', + onPress: async () => { + const success = await removeFeed(feedId, callId); + if (success) { + showToast('success', t('videoFeeds.feedDeleted')); + } else { + showToast('error', t('videoFeeds.feedError')); + } + }, + }, + ]); + }, + [callId, removeFeed, showToast, t] + ); + + const handleAddNew = useCallback(() => { + setEditingFeed(null); + setIsFormOpen(true); + }, []); + + const handleCloseForm = useCallback(() => { + setIsFormOpen(false); + setEditingFeed(null); + }, []); + + const renderFeed = useCallback( + ({ item }: { item: CallVideoFeedResultData }) => , + [canEdit, handleWatch, handleEdit, handleDelete] + ); + + if (isLoading) { + return ( + + {t('common.loading')} + + ); + } + + if (error) { + return ( + + {error} + + ); + } + + return ( + + {feeds.length === 0 ? ( + + {t('videoFeeds.noFeeds')} + {canEdit && ( + + )} + + ) : ( + <> + item.CallVideoFeedId} scrollEnabled={false} /> + {canEdit && ( + + )} + + )} + + {/* Add/Edit Form */} + + + {/* Video Player Modal */} + {watchingFeed && setWatchingFeed(null)} />} + + ); +}; diff --git a/src/components/callVideoFeeds/video-player.tsx b/src/components/callVideoFeeds/video-player.tsx new file mode 100644 index 0000000..00c108b --- /dev/null +++ b/src/components/callVideoFeeds/video-player.tsx @@ -0,0 +1,227 @@ +import { useColorScheme } from 'nativewind'; +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { ActivityIndicator, Alert, Modal, Platform, Pressable, StyleSheet, View } from 'react-native'; +import WebView from 'react-native-webview'; + +import { Box } from '@/components/ui/box'; +import { Button, ButtonText } from '@/components/ui/button'; +import { Text } from '@/components/ui/text'; +import { CallVideoFeedFormat } from '@/models/v4/callVideoFeeds/callVideoFeedEnums'; +import { type CallVideoFeedResultData } from '@/models/v4/callVideoFeeds/callVideoFeedResultData'; + +interface VideoPlayerProps { + feed: CallVideoFeedResultData; + visible: boolean; + onClose: () => void; +} + +function extractYouTubeId(url: string): string | null { + const patterns = [/(?:youtube\.com\/watch\?v=)([a-zA-Z0-9_-]{11})/, /(?:youtu\.be\/)([a-zA-Z0-9_-]{11})/, /(?:youtube\.com\/live\/)([a-zA-Z0-9_-]{11})/, /(?:youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/]; + for (const pattern of patterns) { + const match = url.match(pattern); + if (match) return match[1]; + } + return null; +} + +function sanitizeUrl(url: string): string | null { + try { + const parsed = new URL(url); + if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') { + return null; + } + return parsed.href; + } catch { + return null; + } +} + +function getPlayerHtml(feed: CallVideoFeedResultData, isDark: boolean): string | null { + const bgColor = isDark ? '#171717' : '#ffffff'; + const textColor = isDark ? '#d1d5db' : '#374151'; + + switch (feed.FeedFormat) { + case CallVideoFeedFormat.HLS: + case CallVideoFeedFormat.DASH: + return ` + + + + + + + + + + `; + + case CallVideoFeedFormat.YouTubeLive: { + const videoId = extractYouTubeId(feed.Url); + if (!videoId) return null; + return ` + + + + + + + + `; + } + + case CallVideoFeedFormat.MJPEG: { + const mjpegUrl = sanitizeUrl(feed.Url); + if (!mjpegUrl) return null; + return ` + + + + + + MJPEG Stream + + `; + } + + case CallVideoFeedFormat.Embed: + case CallVideoFeedFormat.Other: { + const embedUrl = sanitizeUrl(feed.Url); + if (!embedUrl) return null; + return ` + + + + + + + + `; + } + + default: + return null; + } +} + +export const VideoPlayer: React.FC = ({ feed, visible, onClose }) => { + const { t } = useTranslation(); + const { colorScheme } = useColorScheme(); + const isDark = colorScheme === 'dark'; + const [isLoading, setIsLoading] = useState(true); + + const playerHtml = getPlayerHtml(feed, isDark); + const isUnsupported = feed.FeedFormat === CallVideoFeedFormat.RTSP || feed.FeedFormat === CallVideoFeedFormat.WebRTC; + + const handleCopyUrl = async () => { + if (Platform.OS === 'web' && typeof navigator !== 'undefined' && navigator.clipboard) { + await navigator.clipboard.writeText(feed.Url); + } else { + Alert.alert(t('videoFeeds.copyUrl'), feed.Url); + } + }; + + return ( + + + {/* Header */} + + + {feed.Name} + {feed.Url} + + + {t('common.close')} + + + + {/* Player */} + + {isUnsupported ? ( + + {t('videoFeeds.unsupportedFormat')} + {feed.Url} + + + ) : playerHtml ? ( + <> + {isLoading && ( + + + + )} + setIsLoading(false)} /> + + ) : ( + + {t('videoFeeds.feedError')} + + + )} + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + containerDark: { + backgroundColor: '#0a0a0a', + }, + containerLight: { + backgroundColor: '#ffffff', + }, + header: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: 16, + paddingVertical: 12, + borderBottomWidth: 1, + }, + headerDark: { + backgroundColor: '#171717', + borderBottomColor: '#262626', + }, + headerLight: { + backgroundColor: '#ffffff', + borderBottomColor: '#e5e7eb', + }, + headerContent: { + flex: 1, + marginRight: 16, + }, + closeButton: { + padding: 8, + }, + playerContainer: { + flex: 1, + }, + loadingOverlay: { + ...StyleSheet.absoluteFillObject, + alignItems: 'center', + justifyContent: 'center', + zIndex: 1, + }, + webview: { + flex: 1, + backgroundColor: 'transparent', + }, +}); diff --git a/src/components/calls/__tests__/call-files-modal.test.tsx b/src/components/calls/__tests__/call-files-modal.test.tsx index d28458e..2797c8b 100644 --- a/src/components/calls/__tests__/call-files-modal.test.tsx +++ b/src/components/calls/__tests__/call-files-modal.test.tsx @@ -55,8 +55,8 @@ jest.mock('@/hooks/use-analytics', () => ({ }), })); -// Mock expo modules -jest.mock('expo-file-system', () => ({ +// Mock expo modules — component imports from 'expo-file-system/legacy' +jest.mock('expo-file-system/legacy', () => ({ documentDirectory: '/mock/documents/', writeAsStringAsync: jest.fn(), EncodingType: { @@ -90,14 +90,13 @@ Object.defineProperty(global, 'FileReader', { result: string | ArrayBuffer | null = null; readyState = 0; onload: ((event: any) => void) | null = null; + onerror: ((event: any) => void) | null = null; - readAsDataURL(blob: Blob) { - // Simulate successful file read - setTimeout(() => { - this.result = 'data:application/pdf;base64,dGVzdCBjb250ZW50'; // base64 for "test content" - this.readyState = 2; // DONE - if (this.onload) this.onload(new Event('load') as any); - }, 0); + readAsDataURL(_blob: Blob) { + // Fire synchronously — onload is set before readAsDataURL is called + this.result = 'data:application/pdf;base64,dGVzdCBjb250ZW50'; // base64 for "test content" + this.readyState = 2; // DONE + if (this.onload) this.onload(new Event('load') as any); } } }); @@ -492,7 +491,7 @@ describe('CallFilesModal', () => { describe('File Download', () => { const mockGetCallAttachmentFile = require('@/api/calls/callFiles').getCallAttachmentFile; - const mockWriteAsStringAsync = require('expo-file-system').writeAsStringAsync; + const mockWriteAsStringAsync = require('expo-file-system/legacy').writeAsStringAsync; const mockShareAsync = require('expo-sharing').shareAsync; beforeEach(() => { diff --git a/src/components/calls/__tests__/dispatch-selection-modal.test.tsx b/src/components/calls/__tests__/dispatch-selection-modal.test.tsx index 4cb8a8c..cba6e21 100644 --- a/src/components/calls/__tests__/dispatch-selection-modal.test.tsx +++ b/src/components/calls/__tests__/dispatch-selection-modal.test.tsx @@ -7,60 +7,10 @@ import { DispatchSelectionModal } from '../dispatch-selection-modal'; // Mock the dispatch store with proper typing const mockDispatchStore = { data: { - users: [ - { - Id: '1', - UserId: '1', - Name: 'John Doe', - FirstName: 'John', - LastName: 'Doe', - EmailAddress: 'john.doe@example.com', - GroupName: 'Group A', - IdentificationNumber: '', - DepartmentId: '', - MobilePhone: '', - GroupId: '', - StatusId: '', - Status: '', - StatusColor: '', - StatusTimestamp: '', - StatusDestinationId: '', - StatusDestinationName: '', - StaffingId: '', - Staffing: '', - StaffingColor: '', - StaffingTimestamp: '', - Roles: [], - }, - ], - groups: [ - { GroupId: '1', Name: 'Fire Department', TypeId: 1, Address: '', GroupType: 'Fire' }, - ], - roles: [ - { UnitRoleId: '1', Name: 'Captain', UnitId: '1' }, - ], - units: [ - { - UnitId: '1', - Name: 'Engine 1', - GroupName: 'Station 1', - DepartmentId: '', - Type: '', - TypeId: 0, - CustomStatusSetId: '', - GroupId: '', - Vin: '', - PlateNumber: '', - FourWheelDrive: false, - SpecialPermit: false, - CurrentDestinationId: '', - CurrentStatusId: '', - CurrentStatusTimestamp: '', - Latitude: '', - Longitude: '', - Note: '', - }, - ], + users: [{ Id: '1', Name: 'John Doe' }], + groups: [{ Id: '1', Name: 'Fire Department' }], + roles: [{ Id: 'Captain', Name: 'Captain' }], + units: [{ Id: '1', Name: 'Engine 1' }], }, selection: { everyone: false, @@ -82,60 +32,10 @@ const mockDispatchStore = { setSearchQuery: jest.fn(), clearSelection: jest.fn(), getFilteredData: jest.fn().mockReturnValue({ - users: [ - { - Id: '1', - UserId: '1', - Name: 'John Doe', - FirstName: 'John', - LastName: 'Doe', - EmailAddress: 'john.doe@example.com', - GroupName: 'Group A', - IdentificationNumber: '', - DepartmentId: '', - MobilePhone: '', - GroupId: '', - StatusId: '', - Status: '', - StatusColor: '', - StatusTimestamp: '', - StatusDestinationId: '', - StatusDestinationName: '', - StaffingId: '', - Staffing: '', - StaffingColor: '', - StaffingTimestamp: '', - Roles: [], - }, - ], - groups: [ - { GroupId: '1', Name: 'Fire Department', TypeId: 1, Address: '', GroupType: 'Fire' }, - ], - roles: [ - { UnitRoleId: '1', Name: 'Captain', UnitId: '1' }, - ], - units: [ - { - UnitId: '1', - Name: 'Engine 1', - GroupName: 'Station 1', - DepartmentId: '', - Type: '', - TypeId: 0, - CustomStatusSetId: '', - GroupId: '', - Vin: '', - PlateNumber: '', - FourWheelDrive: false, - SpecialPermit: false, - CurrentDestinationId: '', - CurrentStatusId: '', - CurrentStatusTimestamp: '', - Latitude: '', - Longitude: '', - Note: '', - }, - ], + users: [{ Id: '1', Name: 'John Doe' }], + groups: [{ Id: '1', Name: 'Fire Department' }], + roles: [{ Id: 'Captain', Name: 'Captain' }], + units: [{ Id: '1', Name: 'Engine 1' }], }), }; diff --git a/src/components/calls/call-card.tsx b/src/components/calls/call-card.tsx index a8b2a73..c882212 100644 --- a/src/components/calls/call-card.tsx +++ b/src/components/calls/call-card.tsx @@ -1,4 +1,4 @@ -import { AlertTriangle, Clock, MapPin } from 'lucide-react-native'; +import { AlertTriangle, Clock, MapPin, ShieldCheck } from 'lucide-react-native'; import React, { useMemo } from 'react'; import { StyleSheet, View } from 'react-native'; @@ -48,6 +48,7 @@ export const CallCard: React.FC = React.memo(({ call, priority }) + {call.CheckInTimersEnabled && } {getTimeAgoUtc(call.LoggedOnUtc)} diff --git a/src/components/calls/call-files-modal.tsx b/src/components/calls/call-files-modal.tsx index 909551c..5d8b4d3 100644 --- a/src/components/calls/call-files-modal.tsx +++ b/src/components/calls/call-files-modal.tsx @@ -1,6 +1,6 @@ import type { BottomSheetBackdropProps } from '@gorhom/bottom-sheet'; import BottomSheet, { BottomSheetBackdrop, BottomSheetView } from '@gorhom/bottom-sheet'; -import * as FileSystem from 'expo-file-system'; +import * as FileSystem from 'expo-file-system/legacy'; import * as Sharing from 'expo-sharing'; import { Download, File, X } from 'lucide-react-native'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; diff --git a/src/components/calls/call-images-modal.tsx b/src/components/calls/call-images-modal.tsx index a25aaa1..4635f26 100644 --- a/src/components/calls/call-images-modal.tsx +++ b/src/components/calls/call-images-modal.tsx @@ -1,4 +1,4 @@ -import * as FileSystem from 'expo-file-system'; +import * as FileSystem from 'expo-file-system/legacy'; import { Image } from 'expo-image'; import * as ImageManipulator from 'expo-image-manipulator'; import * as ImagePicker from 'expo-image-picker'; @@ -396,7 +396,6 @@ const CallImagesModal: React.FC = ({ isOpen, onClose, call itemVisiblePercentThreshold: 50, minimumViewTime: 100, }} - estimatedItemSize={width} className="w-full" contentContainerStyle={{ paddingHorizontal: 0 }} initialScrollIndex={0} diff --git a/src/components/calls/dispatch-selection-modal.tsx b/src/components/calls/dispatch-selection-modal.tsx index deb5c25..b67c0e3 100644 --- a/src/components/calls/dispatch-selection-modal.tsx +++ b/src/components/calls/dispatch-selection-modal.tsx @@ -2,14 +2,13 @@ import { CheckIcon, SearchIcon, UsersIcon, X } from 'lucide-react-native'; import { useColorScheme } from 'nativewind'; import React, { useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { ScrollView, TouchableOpacity, View } from 'react-native'; +import { ScrollView, TextInput, TouchableOpacity, View } from 'react-native'; import { Loading } from '@/components/common/loading'; import { Box } from '@/components/ui/box'; import { Button, ButtonText } from '@/components/ui/button'; import { Card } from '@/components/ui/card'; import { HStack } from '@/components/ui/hstack'; -import { Input, InputField } from '@/components/ui/input'; import { Text } from '@/components/ui/text'; import { VStack } from '@/components/ui/vstack'; import { type DispatchSelection, useDispatchStore } from '@/stores/dispatch/store'; @@ -69,7 +68,7 @@ export const DispatchSelectionModal: React.FC = ({ > {/* Header */} - + {t('calls.select_dispatch_recipients')} @@ -80,10 +79,21 @@ export const DispatchSelectionModal: React.FC = ({ {/* Search */} - - - - + + + + {/* Content */} @@ -98,7 +108,7 @@ export const DispatchSelectionModal: React.FC = ({ {/* Everyone Option */} - + {selection.everyone && } @@ -119,7 +129,7 @@ export const DispatchSelectionModal: React.FC = ({ {filteredData.users.map((user) => ( toggleUser(user.Id)}> - + = ({ {filteredData.groups.map((group) => ( toggleGroup(group.Id)}> - + = ({ {filteredData.roles.map((role) => ( toggleRole(role.Id)}> - + = ({ {filteredData.units.map((unit) => ( toggleUnit(unit.Id)}> - + = ({ {getSelectionCount()} {t('calls.selected')} - + diff --git a/src/components/checkIn/__tests__/check-in-bottom-sheet.test.tsx b/src/components/checkIn/__tests__/check-in-bottom-sheet.test.tsx new file mode 100644 index 0000000..7b5dd25 --- /dev/null +++ b/src/components/checkIn/__tests__/check-in-bottom-sheet.test.tsx @@ -0,0 +1,77 @@ +import { render, fireEvent } from '@testing-library/react-native'; +import React from 'react'; + +import { CheckInTimerStatusResultData } from '@/models/v4/checkIn/checkInTimerStatusResultData'; + +import { CheckInBottomSheet } from '../check-in-bottom-sheet'; + +jest.mock('react-i18next', () => ({ + useTranslation: () => ({ t: (key: string) => key }), +})); + +jest.mock('nativewind', () => ({ + cssInterop: jest.fn((Component: any) => Component), + useColorScheme: () => ({ colorScheme: 'light' }), +})); + +const mockPerformCheckIn = jest.fn().mockResolvedValue(true); + +jest.mock('@/stores/checkIn/store', () => ({ + useCheckInStore: jest.fn((selector) => + typeof selector === 'function' + ? selector({ performCheckIn: mockPerformCheckIn, isCheckingIn: false }) + : { performCheckIn: mockPerformCheckIn, isCheckingIn: false } + ), +})); + +jest.mock('@/stores/app/location-store', () => ({ + useLocationStore: jest.fn((selector) => + typeof selector === 'function' + ? selector({ latitude: 40.7, longitude: -74.0 }) + : { latitude: 40.7, longitude: -74.0 } + ), +})); + +jest.mock('@/stores/toast/store', () => ({ + useToastStore: jest.fn((selector) => + typeof selector === 'function' + ? selector({ showToast: jest.fn() }) + : { showToast: jest.fn() } + ), +})); + +jest.mock('@/components/ui/bottom-sheet', () => ({ + CustomBottomSheet: ({ children, isOpen }: { children: React.ReactNode; isOpen: boolean }) => + isOpen ? <>{children} : null, +})); + +function makeTimer(): CheckInTimerStatusResultData { + const timer = new CheckInTimerStatusResultData(); + timer.TargetType = 1; + timer.TargetTypeName = 'Personnel'; + timer.TargetEntityId = 'u1'; + timer.TargetName = 'John Doe'; + timer.DurationMinutes = 20; + timer.ElapsedMinutes = 5; + timer.Status = 'Ok'; + timer.CallId = 1; + return timer; +} + +describe('CheckInBottomSheet', () => { + it('shows target selection when no timer pre-selected', () => { + const timers = [makeTimer()]; + const { getByText } = render( + + ); + expect(getByText('check_in.select_target')).toBeTruthy(); + }); + + it('shows confirm step when timer is pre-selected', () => { + const timer = makeTimer(); + const { getByText } = render( + + ); + expect(getByText('check_in.confirm')).toBeTruthy(); + }); +}); diff --git a/src/components/checkIn/__tests__/check-in-history-list.test.tsx b/src/components/checkIn/__tests__/check-in-history-list.test.tsx new file mode 100644 index 0000000..c9ae725 --- /dev/null +++ b/src/components/checkIn/__tests__/check-in-history-list.test.tsx @@ -0,0 +1,52 @@ +import { render, fireEvent } from '@testing-library/react-native'; +import React from 'react'; + +import { CheckInRecordResultData } from '@/models/v4/checkIn/checkInRecordResultData'; + +import { CheckInHistoryList } from '../check-in-history-list'; + +jest.mock('react-i18next', () => ({ + useTranslation: () => ({ t: (key: string) => key }), +})); + +jest.mock('nativewind', () => ({ + useColorScheme: () => ({ colorScheme: 'light' }), +})); + +function makeRecord(): CheckInRecordResultData { + const record = new CheckInRecordResultData(); + record.CheckInRecordId = '1'; + record.CallId = 1; + record.CheckInType = 1; + record.CheckInTypeName = 'Personnel'; + record.UserId = 'user1'; + record.UnitId = 0; + record.Timestamp = '2026-01-01T12:00:00Z'; + record.Note = 'Radio confirmed'; + return record; +} + +describe('CheckInHistoryList', () => { + it('renders nothing when history is empty', () => { + const { toJSON } = render(); + expect(toJSON()).toBeNull(); + }); + + it('shows loading state', () => { + const { getByText } = render(); + expect(getByText('common.loading')).toBeTruthy(); + }); + + it('renders collapsed header when history exists', () => { + const { getByText, queryByText } = render(); + expect(getByText('check_in.history')).toBeTruthy(); + expect(queryByText('Personnel')).toBeNull(); + }); + + it('expands to show records on header press', () => { + const { getByText } = render(); + fireEvent.press(getByText('check_in.history')); + expect(getByText('Personnel')).toBeTruthy(); + expect(getByText('Radio confirmed')).toBeTruthy(); + }); +}); diff --git a/src/components/checkIn/__tests__/check-in-tab.test.tsx b/src/components/checkIn/__tests__/check-in-tab.test.tsx new file mode 100644 index 0000000..6d98989 --- /dev/null +++ b/src/components/checkIn/__tests__/check-in-tab.test.tsx @@ -0,0 +1,74 @@ +import { render } from '@testing-library/react-native'; +import React from 'react'; + +import { CheckInTab } from '../check-in-tab'; + +jest.mock('react-i18next', () => ({ + useTranslation: () => ({ + t: (key: string) => key, + }), +})); + +jest.mock('nativewind', () => ({ + cssInterop: jest.fn((Component: any) => Component), + useColorScheme: () => ({ colorScheme: 'light' }), +})); + +const mockStore = { + timerStatuses: [], + checkInHistory: [], + isLoadingStatuses: false, + isLoadingHistory: false, + statusError: null, + fetchTimerStatuses: jest.fn(), + fetchResolvedTimers: jest.fn(), + fetchCheckInHistory: jest.fn(), + toggleTimers: jest.fn(), + startPolling: jest.fn(), + stopPolling: jest.fn(), + reset: jest.fn(), +}; + +jest.mock('@/stores/checkIn/store', () => ({ + useCheckInStore: jest.fn((selector) => + typeof selector === 'function' ? selector(mockStore) : mockStore + ), +})); + +jest.mock('@/stores/signalr/signalr-store', () => ({ + useSignalRStore: jest.fn((selector) => + typeof selector === 'function' ? selector({ lastCheckInUpdateTimestamp: 0 }) : { lastCheckInUpdateTimestamp: 0 } + ), +})); + +describe('CheckInTab', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders disabled message when timers disabled', () => { + const { getByText } = render( + + ); + expect(getByText('check_in.timers_disabled')).toBeTruthy(); + }); + + it('renders empty state when no timers', () => { + const { getByText } = render( + + ); + expect(getByText('check_in.no_timers')).toBeTruthy(); + }); + + it('starts polling on mount and stops on unmount', () => { + const { unmount } = render( + + ); + expect(mockStore.fetchTimerStatuses).toHaveBeenCalledWith(1); + expect(mockStore.startPolling).toHaveBeenCalledWith(1); + + unmount(); + expect(mockStore.stopPolling).toHaveBeenCalled(); + expect(mockStore.reset).toHaveBeenCalled(); + }); +}); diff --git a/src/components/checkIn/__tests__/check-in-timer-card.test.tsx b/src/components/checkIn/__tests__/check-in-timer-card.test.tsx new file mode 100644 index 0000000..41275af --- /dev/null +++ b/src/components/checkIn/__tests__/check-in-timer-card.test.tsx @@ -0,0 +1,67 @@ +import { render, fireEvent } from '@testing-library/react-native'; +import React from 'react'; + +import { CheckInTimerStatusResultData } from '@/models/v4/checkIn/checkInTimerStatusResultData'; + +import { CheckInTimerCard } from '../check-in-timer-card'; + +jest.mock('react-i18next', () => ({ + useTranslation: () => ({ + t: (key: string, opts?: Record) => { + if (opts?.count !== undefined) return `${opts.count} min ago`; + return key; + }, + }), +})); + +jest.mock('nativewind', () => ({ + cssInterop: jest.fn((Component: any) => Component), + useColorScheme: () => ({ colorScheme: 'light' }), +})); + +function makeTimer(overrides: Partial = {}): CheckInTimerStatusResultData { + const timer = new CheckInTimerStatusResultData(); + timer.TargetType = 1; + timer.TargetTypeName = 'Personnel'; + timer.TargetEntityId = 'u1'; + timer.TargetName = 'John Doe'; + timer.DurationMinutes = 20; + timer.ElapsedMinutes = 5; + timer.Status = 'Ok'; + timer.LastCheckIn = '2026-01-01T00:00:00Z'; + timer.CallId = 1; + Object.assign(timer, overrides); + return timer; +} + +describe('CheckInTimerCard', () => { + it('renders Ok status correctly', () => { + const onCheckIn = jest.fn(); + const { getByText } = render( + + ); + expect(getByText('John Doe')).toBeTruthy(); + expect(getByText('check_in.status_ok')).toBeTruthy(); + }); + + it('renders Overdue status', () => { + const onCheckIn = jest.fn(); + const { getByText } = render( + + ); + expect(getByText('check_in.status_overdue')).toBeTruthy(); + }); + + it('calls onCheckIn when button pressed', () => { + const onCheckIn = jest.fn(); + const timer = makeTimer(); + const { getByText } = render( + + ); + fireEvent.press(getByText('check_in.perform_check_in')); + expect(onCheckIn).toHaveBeenCalledWith(timer); + }); +}); diff --git a/src/components/checkIn/check-in-bottom-sheet.tsx b/src/components/checkIn/check-in-bottom-sheet.tsx new file mode 100644 index 0000000..ba552ed --- /dev/null +++ b/src/components/checkIn/check-in-bottom-sheet.tsx @@ -0,0 +1,211 @@ +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useWindowDimensions } from 'react-native'; + +import { CustomBottomSheet } from '@/components/ui/bottom-sheet'; +import { Button, ButtonText } from '@/components/ui/button'; +import { FormControl, FormControlLabel, FormControlLabelText } from '@/components/ui/form-control'; +import { HStack } from '@/components/ui/hstack'; +import { Text } from '@/components/ui/text'; +import { Textarea, TextareaInput } from '@/components/ui/textarea'; +import { VStack } from '@/components/ui/vstack'; +import { type CheckInTimerStatusResultData } from '@/models/v4/checkIn/checkInTimerStatusResultData'; +import { useLocationStore } from '@/stores/app/location-store'; +import { useCheckInStore } from '@/stores/checkIn/store'; +import { usePersonnelStore } from '@/stores/personnel/store'; +import { useToastStore } from '@/stores/toast/store'; +import { useUnitsStore } from '@/stores/units/store'; + +/** Resolve the display name for a check-in timer entity */ +function resolveTimerDisplayName( + timer: CheckInTimerStatusResultData, + units: { UnitId: string; Name: string }[], + personnel: { UserId: string; IdentificationNumber: string; FirstName: string; LastName: string }[], + fallback: string +): string { + // TargetName from API is often the type label (e.g. "UnitType"), not the entity name + if (timer.TargetName && !/type$/i.test(timer.TargetName) && timer.TargetName !== timer.TargetTypeName) { + return timer.TargetName; + } + + const entityId = timer.TargetEntityId; + const unitId = timer.UnitId; + + for (const u of units) { + if (entityId && u.UnitId === entityId) return u.Name; + if (unitId > 0 && u.UnitId === String(unitId)) return u.Name; + if (unitId > 0 && parseInt(u.UnitId, 10) === unitId) return u.Name; + } + + if (entityId) { + for (const p of personnel) { + if (p.UserId === entityId || p.IdentificationNumber === entityId) { + return `${p.FirstName} ${p.LastName}`.trim(); + } + } + } + + return entityId || fallback; +} + +const CHECK_IN_TYPE_KEYS: Record = { + Personnel: 'check_in.type_personnel', + Unit: 'check_in.type_unit', + IC: 'check_in.type_ic', + PAR: 'check_in.type_par', + HazmatExposure: 'check_in.type_hazmat', + SectorRotation: 'check_in.type_sector_rotation', + Rehab: 'check_in.type_rehab', +}; + +interface CheckInBottomSheetProps { + isOpen: boolean; + onClose: () => void; + callId: number; + selectedTimer?: CheckInTimerStatusResultData | null; + timers: CheckInTimerStatusResultData[]; +} + +export const CheckInBottomSheet: React.FC = ({ isOpen, onClose, callId, selectedTimer, timers }) => { + const { t } = useTranslation(); + const { width, height } = useWindowDimensions(); + const isLandscape = width > height; + const showToast = useToastStore((state) => state.showToast); + const { performCheckIn, isCheckingIn } = useCheckInStore(); + const units = useUnitsStore((s) => s.units); + const personnel = usePersonnelStore((s) => s.personnel); + const userLocation = useLocationStore((state) => ({ + latitude: state.latitude, + longitude: state.longitude, + })); + + const typeLabel = (timer: CheckInTimerStatusResultData) => { + // Use TargetTypeName from API, fall back to translation key lookup + if (timer.TargetTypeName) return timer.TargetTypeName; + const key = CHECK_IN_TYPE_KEYS[String(timer.TargetType)]; + return key ? t(key) : String(timer.TargetType); + }; + + const [selected, setSelected] = useState(selectedTimer || null); + const [note, setNote] = useState(''); + const [step, setStep] = useState<'select' | 'confirm'>(selectedTimer ? 'confirm' : 'select'); + + React.useEffect(() => { + if (isOpen) { + if (selectedTimer) { + setSelected(selectedTimer); + setStep('confirm'); + } else { + setSelected(null); + setStep('select'); + } + setNote(''); + } + }, [isOpen, selectedTimer]); + + const handleSelectTarget = (timer: CheckInTimerStatusResultData) => { + setSelected(timer); + setStep('confirm'); + }; + + const handleConfirm = async () => { + if (!selected) return; + + // Use CallId from the selected timer (injected during aggregation) or fall back to prop + const effectiveCallId = selected.CallId || callId; + + // Resolve UnitId: prefer the explicit field, fall back to parsing TargetEntityId + const resolvedUnitId = selected.UnitId || parseInt(selected.TargetEntityId) || 0; + + console.log('[CheckIn] Performing check-in:', { + effectiveCallId, + TargetType: selected.TargetType, + TargetEntityId: selected.TargetEntityId, + UnitId: resolvedUnitId, + TargetName: selected.TargetName, + }); + + if (!effectiveCallId) { + console.error('[CheckIn] No callId available'); + showToast('error', t('check_in.check_in_error')); + return; + } + + try { + const success = await performCheckIn({ + CallId: effectiveCallId, + CheckInType: selected.TargetType, + UnitId: resolvedUnitId || undefined, + Latitude: userLocation.latitude?.toString() || undefined, + Longitude: userLocation.longitude?.toString() || undefined, + Note: note || undefined, + }); + + if (success) { + showToast('success', t('check_in.check_in_success')); + handleClose(); + } else { + showToast('error', t('check_in.check_in_error')); + } + } catch (error) { + console.error('Check-in failed:', error); + showToast('error', t('check_in.check_in_error')); + } + }; + + const handleClose = () => { + setSelected(null); + setNote(''); + setStep('select'); + onClose(); + }; + + return ( + + + {t('check_in.perform_check_in')} + + {step === 'select' && ( + + {t('check_in.select_target')} + {timers.map((timer) => ( + + ))} + + )} + + {step === 'confirm' && selected && ( + + + {resolveTimerDisplayName(selected, units, personnel, typeLabel(selected))} + {typeLabel(selected)} + + + + + {t('check_in.add_note')} + + + + + + + + + + )} + + + ); +}; diff --git a/src/components/checkIn/check-in-history-list.tsx b/src/components/checkIn/check-in-history-list.tsx new file mode 100644 index 0000000..bba2349 --- /dev/null +++ b/src/components/checkIn/check-in-history-list.tsx @@ -0,0 +1,65 @@ +import { format } from 'date-fns'; +import { useColorScheme } from 'nativewind'; +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Pressable } from 'react-native'; + +import { Box } from '@/components/ui/box'; +import { HStack } from '@/components/ui/hstack'; +import { Text } from '@/components/ui/text'; +import { VStack } from '@/components/ui/vstack'; +import { type CheckInRecordResultData } from '@/models/v4/checkIn/checkInRecordResultData'; + +interface CheckInHistoryListProps { + history: CheckInRecordResultData[]; + isLoading: boolean; +} + +export const CheckInHistoryList: React.FC = ({ history, isLoading }) => { + const { t } = useTranslation(); + const { colorScheme } = useColorScheme(); + const [isExpanded, setIsExpanded] = useState(false); + + if (isLoading) { + return ( + + {t('common.loading')} + + ); + } + + if (!history.length) { + return null; + } + + return ( + + setIsExpanded(!isExpanded)}> + + {t('check_in.history')} + {isExpanded ? '▲' : '▼'} + + + + {isExpanded && ( + + {history.map((record) => ( + + + + {record.CheckInTypeName || String(record.CheckInType)} + + {record.UserId ? `User: ${record.UserId}` : ''} + {record.UnitId ? ` Unit: ${record.UnitId}` : ''} + + {record.Note ? {record.Note} : null} + + {record.Timestamp ? format(new Date(record.Timestamp), 'MMM d, h:mm a') : ''} + + + ))} + + )} + + ); +}; diff --git a/src/components/checkIn/check-in-tab.tsx b/src/components/checkIn/check-in-tab.tsx new file mode 100644 index 0000000..fdb44f6 --- /dev/null +++ b/src/components/checkIn/check-in-tab.tsx @@ -0,0 +1,141 @@ +import { useColorScheme } from 'nativewind'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { FlatList } from 'react-native'; + +import { Box } from '@/components/ui/box'; +import { Button, ButtonText } from '@/components/ui/button'; +import { HStack } from '@/components/ui/hstack'; +import { Text } from '@/components/ui/text'; +import { VStack } from '@/components/ui/vstack'; +import { type CheckInTimerStatusResultData } from '@/models/v4/checkIn/checkInTimerStatusResultData'; +import { useCheckInStore } from '@/stores/checkIn/store'; +import { useSignalRStore } from '@/stores/signalr/signalr-store'; + +import { CheckInBottomSheet } from './check-in-bottom-sheet'; +import { CheckInHistoryList } from './check-in-history-list'; +import { CheckInTimerCard } from './check-in-timer-card'; + +interface CheckInTabProps { + callId: number; + checkInTimersEnabled: boolean; +} + +export const CheckInTab: React.FC = ({ callId, checkInTimersEnabled }) => { + const { t } = useTranslation(); + const { colorScheme } = useColorScheme(); + const [isBottomSheetOpen, setIsBottomSheetOpen] = useState(false); + const [selectedTimer, setSelectedTimer] = useState(null); + + const { timerStatuses, checkInHistory, isLoadingStatuses, isLoadingHistory, statusError, fetchTimerStatuses, fetchCheckInHistory, fetchResolvedTimers, toggleTimers, startPolling, stopPolling, reset } = + useCheckInStore(); + + const lastCheckInUpdateTimestamp = useSignalRStore((s) => s.lastCheckInUpdateTimestamp); + + // Initial data fetch + useEffect(() => { + if (callId && checkInTimersEnabled) { + fetchTimerStatuses(callId); + fetchResolvedTimers(callId); + fetchCheckInHistory(callId); + startPolling(callId); + } + return () => { + stopPolling(); + reset(); + }; + }, [callId, checkInTimersEnabled, fetchTimerStatuses, fetchResolvedTimers, fetchCheckInHistory, startPolling, stopPolling, reset]); + + // React to SignalR check-in updates + useEffect(() => { + if (lastCheckInUpdateTimestamp > 0 && callId) { + fetchTimerStatuses(callId); + fetchCheckInHistory(callId); + } + }, [lastCheckInUpdateTimestamp, callId, fetchTimerStatuses, fetchCheckInHistory]); + + const handleCheckIn = useCallback((timer: CheckInTimerStatusResultData) => { + setSelectedTimer(timer); + setIsBottomSheetOpen(true); + }, []); + + const handleCloseBottomSheet = useCallback(() => { + setIsBottomSheetOpen(false); + setSelectedTimer(null); + }, []); + + const criticalCount = timerStatuses.filter((s) => s.Status === 'Critical').length; + const overdueCount = timerStatuses.filter((s) => s.Status === 'Overdue' || s.Status === 'Red').length; + const warningCount = timerStatuses.filter((s) => s.Status === 'Warning' || s.Status === 'Yellow').length; + const okCount = timerStatuses.filter((s) => s.Status === 'Ok' || s.Status === 'Green').length; + + const renderTimer = ({ item }: { item: CheckInTimerStatusResultData }) => ; + + return ( + + {!checkInTimersEnabled && ( + + {t('check_in.timers_disabled')} + + )} + + {checkInTimersEnabled && !isLoadingStatuses && timerStatuses.length === 0 && !statusError && ( + + {t('check_in.no_timers')} + + )} + + {/* Summary header */} + {checkInTimersEnabled && timerStatuses.length > 0 && ( + + {criticalCount > 0 && ( + + + {criticalCount} {t('check_in.status_critical')} + + + )} + {overdueCount > 0 && ( + + {t('check_in.overdue_count', { count: overdueCount })} + + )} + {warningCount > 0 && ( + + {t('check_in.warning_count', { count: warningCount })} + + )} + {okCount > 0 && ( + + + {okCount} {t('check_in.status_ok')} + + + )} + + )} + + {statusError && ( + + {statusError} + + )} + + {/* Timer list */} + {checkInTimersEnabled && timerStatuses.length > 0 && `${item.TargetType}-${item.TargetEntityId}`} scrollEnabled={false} />} + + {/* Toggle timers button */} + + + + + {/* History */} + + + {/* Check-in bottom sheet */} + + + ); +}; diff --git a/src/components/checkIn/check-in-timer-card.tsx b/src/components/checkIn/check-in-timer-card.tsx new file mode 100644 index 0000000..1e7c6b2 --- /dev/null +++ b/src/components/checkIn/check-in-timer-card.tsx @@ -0,0 +1,146 @@ +import { ShieldCheckIcon } from 'lucide-react-native'; +import { useColorScheme } from 'nativewind'; +import React, { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import { View } from 'react-native'; + +import { Box } from '@/components/ui/box'; +import { Button, ButtonText } from '@/components/ui/button'; +import { HStack } from '@/components/ui/hstack'; +import { Text } from '@/components/ui/text'; +import { VStack } from '@/components/ui/vstack'; +import { type CheckInTimerStatusResultData } from '@/models/v4/checkIn/checkInTimerStatusResultData'; +import { usePersonnelStore } from '@/stores/personnel/store'; +import { useUnitsStore } from '@/stores/units/store'; + +const STATUS_COLORS: Record = { + Ok: '#22C55E', + Green: '#22C55E', + Warning: '#F59E0B', + Yellow: '#F59E0B', + Overdue: '#EF4444', + Red: '#EF4444', + Critical: '#DC2626', +}; + +const CHECK_IN_TYPE_KEYS: Record = { + Personnel: 'check_in.type_personnel', + Unit: 'check_in.type_unit', + IC: 'check_in.type_ic', + PAR: 'check_in.type_par', + HazmatExposure: 'check_in.type_hazmat', + SectorRotation: 'check_in.type_sector_rotation', + Rehab: 'check_in.type_rehab', +}; + +interface CheckInTimerCardProps { + timer: CheckInTimerStatusResultData; + onCheckIn: (timer: CheckInTimerStatusResultData) => void; +} + +export const CheckInTimerCard: React.FC = React.memo(({ timer, onCheckIn }) => { + const { t } = useTranslation(); + const { colorScheme } = useColorScheme(); + const units = useUnitsStore((s) => s.units); + const personnel = usePersonnelStore((s) => s.personnel); + + const statusColor = STATUS_COLORS[timer.Status] || '#6B7280'; + const progress = timer.DurationMinutes > 0 ? Math.min((timer.ElapsedMinutes / timer.DurationMinutes) * 100, 100) : 0; + + // Use TargetTypeName from API, fall back to translation key lookup + const typeLabel = timer.TargetTypeName || (CHECK_IN_TYPE_KEYS[String(timer.TargetType)] ? t(CHECK_IN_TYPE_KEYS[String(timer.TargetType)]) : String(timer.TargetType)); + + // The API's TargetName often contains the check-in TYPE name (e.g. "UnitType") + // rather than the actual entity name (e.g. "Engine 1"). Detect this and look up + // the real name from the units/personnel stores using TargetEntityId. + const isTargetNameActuallyTypeName = !timer.TargetName + || timer.TargetName === timer.TargetTypeName + || /type$/i.test(timer.TargetName) + || Object.keys(CHECK_IN_TYPE_KEYS).some((k) => k.toLowerCase() === timer.TargetName.toLowerCase()) + || timer.TargetName.toLowerCase() === 'unittype' + || timer.TargetName.toLowerCase() === 'personneltype'; + + const displayName = useMemo(() => { + // If TargetName is a real entity name (not a type label), use it directly + if (timer.TargetName && !isTargetNameActuallyTypeName) return timer.TargetName; + + const entityId = timer.TargetEntityId; + const unitId = timer.UnitId; + + // Look up from units store by TargetEntityId or UnitId + if (units.length > 0) { + for (const u of units) { + if (entityId && u.UnitId === entityId) return u.Name; + if (unitId > 0 && u.UnitId === String(unitId)) return u.Name; + if (unitId > 0 && parseInt(u.UnitId, 10) === unitId) return u.Name; + if (entityId && !isNaN(parseInt(entityId, 10)) && parseInt(u.UnitId, 10) === parseInt(entityId, 10)) return u.Name; + } + } + + // Look up from personnel store by TargetEntityId + if (personnel.length > 0 && entityId) { + for (const p of personnel) { + if (p.UserId === entityId || p.IdentificationNumber === entityId) { + return `${p.FirstName} ${p.LastName}`.trim(); + } + } + } + + return entityId || typeLabel; + }, [timer.TargetName, timer.TargetEntityId, timer.UnitId, isTargetNameActuallyTypeName, units, personnel, typeLabel]); + + const statusKey = `check_in.status_${timer.Status.toLowerCase()}` as const; + + const elapsedText = timer.LastCheckIn ? t('check_in.minutes_ago', { count: Math.round(timer.ElapsedMinutes) }) : ''; + + return ( + + + + + + + {displayName} + {typeLabel} + + + + + {t(statusKey)} + + + + + {/* Progress bar */} + + + + + + + + {t('check_in.elapsed')}: {Math.round(timer.ElapsedMinutes)}m / {timer.DurationMinutes}m + + {elapsedText ? ( + + {t('check_in.last_check_in')}: {elapsedText} + + ) : null} + + + + + + ); +}); + +CheckInTimerCard.displayName = 'CheckInTimerCard'; diff --git a/src/components/dispatch-console/__tests__/active-calls-panel.test.tsx b/src/components/dispatch-console/__tests__/active-calls-panel.test.tsx index 5bd7c40..f37042a 100644 --- a/src/components/dispatch-console/__tests__/active-calls-panel.test.tsx +++ b/src/components/dispatch-console/__tests__/active-calls-panel.test.tsx @@ -151,6 +151,7 @@ const mockCalls: CallResultData[] = [ DispatchedOnUtc: '2024-01-15T10:05:00Z', Latitude: '40.7128', Longitude: '-74.0060', + CheckInTimersEnabled: false, }, { CallId: 'call-2', @@ -180,6 +181,7 @@ const mockCalls: CallResultData[] = [ DispatchedOnUtc: '2024-01-15T11:05:00Z', Latitude: '40.7589', Longitude: '-73.9851', + CheckInTimersEnabled: false, }, { CallId: 'call-3', @@ -209,6 +211,7 @@ const mockCalls: CallResultData[] = [ DispatchedOnUtc: '2024-01-15T09:05:00Z', Latitude: '', Longitude: '', + CheckInTimersEnabled: false, }, ]; diff --git a/src/components/dispatch-console/active-calls-panel.tsx b/src/components/dispatch-console/active-calls-panel.tsx index bb302b6..109d963 100644 --- a/src/components/dispatch-console/active-calls-panel.tsx +++ b/src/components/dispatch-console/active-calls-panel.tsx @@ -2,7 +2,7 @@ import { type Href, router } from 'expo-router'; import { AlertTriangle, Clock, ExternalLink, MapPin, Plus, Radio, Search, X } from 'lucide-react-native'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Animated, Platform, Pressable, ScrollView, StyleSheet, Text as RNText, TextInput, View } from 'react-native'; +import { Animated, Easing, Platform, Pressable, ScrollView, StyleSheet, Text as RNText, TextInput, View } from 'react-native'; import { getCallExtraData } from '@/api/calls/calls'; import { Badge } from '@/components/ui/badge'; @@ -16,6 +16,7 @@ import { type CallPriorityResultData } from '@/models/v4/callPriorities/callPrio import { type CallResultData } from '@/models/v4/calls/callResultData'; import { type DispatchedEventResultData } from '@/models/v4/calls/dispatchedEventResultData'; import { useCallsStore } from '@/stores/calls/store'; +import { useCheckInStore } from '@/stores/checkIn/store'; import { useDispatchConsoleStore } from '@/stores/dispatch/dispatch-console-store'; import { useSecurityStore } from '@/stores/security/store'; @@ -32,16 +33,35 @@ const getDispatchTypeStyle = (type: string): { bg: string; fg: string; label: st return { bg: '#6b7280', fg: '#ffffff', label: '•' }; }; -const DispatchBadge: React.FC<{ dispatch: DispatchedEventResultData }> = React.memo(({ dispatch }) => { +const DispatchBadge: React.FC<{ dispatch: DispatchedEventResultData; isOverdue?: boolean }> = React.memo(({ dispatch, isOverdue }) => { const ts = getDispatchTypeStyle(dispatch.Type); + const opacity = useRef(new Animated.Value(1)).current; + + useEffect(() => { + if (isOverdue) { + const blink = Animated.loop( + Animated.sequence([ + Animated.timing(opacity, { toValue: 0.3, duration: 500, easing: Easing.linear, useNativeDriver: Platform.OS !== 'web' }), + Animated.timing(opacity, { toValue: 1, duration: 500, easing: Easing.linear, useNativeDriver: Platform.OS !== 'web' }), + ]) + ); + blink.start(); + return () => blink.stop(); + } else { + opacity.setValue(1); + } + }, [isOverdue, opacity]); + + const bgColor = isOverdue ? '#dc2626' : ts.bg; + return ( - + {ts.label} {dispatch.Name} - + ); }); @@ -52,7 +72,19 @@ const DispatchTicker: React.FC<{ dispatches: DispatchedEventResultData[]; isLoading?: boolean; textColor?: string; -}> = React.memo(({ dispatches, isLoading, textColor = '#ffffff' }) => { + overdueEntityIds?: Set; +}> = React.memo(({ dispatches, isLoading, textColor = '#ffffff', overdueEntityIds }) => { + // Deduplicate dispatches by Id (or Type+Name as fallback key) + const uniqueDispatches = useMemo(() => { + const seen = new Set(); + return dispatches.filter((d) => { + const key = d.Id || `${d.Type}:${d.Name}`; + if (seen.has(key)) return false; + seen.add(key); + return true; + }); + }, [dispatches]); + const translateX = useRef(new Animated.Value(0)).current; const containerWidthRef = useRef(0); const contentWidthRef = useRef(0); @@ -72,6 +104,7 @@ const DispatchTicker: React.FC<{ Animated.timing(translateX, { toValue: -contentWidthRef.current, duration: (totalDistance / 60) * 1000, + easing: Easing.linear, useNativeDriver: Platform.OS !== 'web', }) ); @@ -79,7 +112,7 @@ const DispatchTicker: React.FC<{ }, [translateX]); useEffect(() => { - if (isLoading || dispatches.length === 0) { + if (isLoading || uniqueDispatches.length === 0) { if (animRef.current) { animRef.current.stop(); animRef.current = null; @@ -91,7 +124,7 @@ const DispatchTicker: React.FC<{ animRef.current = null; } }; - }, [isLoading, dispatches.length]); + }, [isLoading, uniqueDispatches.length]); return ( {isLoading ? ( - ) : dispatches.length === 0 ? ( + ) : uniqueDispatches.length === 0 ? ( ) : ( @@ -114,10 +147,10 @@ const DispatchTicker: React.FC<{ startAnim(); }} > - {dispatches.map((d, i) => ( - + {uniqueDispatches.map((d, i) => ( + {i > 0 ? : null} - + ))} @@ -135,7 +168,8 @@ const DashboardCallCard: React.FC<{ priority?: CallPriorityResultData; dispatches?: DispatchedEventResultData[]; isLoadingDispatches?: boolean; -}> = React.memo(({ call, priority, dispatches, isLoadingDispatches }) => { + overdueEntityIds?: Set; +}> = React.memo(({ call, priority, dispatches, isLoadingDispatches, overdueEntityIds }) => { const bgColor = priority?.Color || '#6b7280'; const textColor = invertColor(bgColor, true); @@ -188,7 +222,7 @@ const DashboardCallCard: React.FC<{ - + ); @@ -210,9 +244,10 @@ const CallItemWrapper: React.FC<{ isFilterActive: boolean; dispatches?: DispatchedEventResultData[]; isLoadingDispatches?: boolean; + overdueEntityIds?: Set; onPress: () => void; onOpenDetails: () => void; -}> = ({ call, priority, isSelected, isFilterActive, dispatches, isLoadingDispatches, onPress, onOpenDetails }) => { +}> = ({ call, priority, isSelected, isFilterActive, dispatches, isLoadingDispatches, overdueEntityIds, onPress, onOpenDetails }) => { void isFilterActive; // kept in props for selection badge only const { t } = useTranslation(); const bgColor = priority?.Color || '#6b7280'; @@ -235,7 +270,7 @@ const CallItemWrapper: React.FC<{ {/* Dashboard Card with action button overlay */} - + { e.stopPropagation(); @@ -255,6 +290,18 @@ const CallItemWrapper: React.FC<{ export const ActiveCallsPanel: React.FC = ({ selectedCallId, onSelectCall, isFilterActive = false }) => { const { t } = useTranslation(); const { canUserCreateCalls } = useSecurityStore(); + + // Check-in timer statuses for overdue detection + const allTimerStatuses = useCheckInStore((s) => s.timerStatuses); + const overdueEntityIds = useMemo(() => { + const ids = new Set(); + allTimerStatuses.forEach((timer) => { + if (timer.Status === 'Overdue' || timer.Status === 'Red' || timer.Status === 'Critical') { + ids.add(timer.TargetEntityId); + } + }); + return ids; + }, [allTimerStatuses]); const [isCollapsed, setIsCollapsed] = useState(false); const [searchQuery, setSearchQuery] = useState(''); @@ -467,6 +514,7 @@ export const ActiveCallsPanel: React.FC = ({ selectedCall isFilterActive={isFilterActive} dispatches={callDispatchesMap[call.CallId]} isLoadingDispatches={loadingCallIds.has(call.CallId)} + overdueEntityIds={overdueEntityIds} onPress={() => { onSelectCall?.(call.CallId); }} diff --git a/src/components/dispatch-console/activity-log-panel.tsx b/src/components/dispatch-console/activity-log-panel.tsx index c27dd1e..c593bc2 100644 --- a/src/components/dispatch-console/activity-log-panel.tsx +++ b/src/components/dispatch-console/activity-log-panel.tsx @@ -1,6 +1,7 @@ -import { AlertTriangle, ArrowRight, Clock, Filter, Info, Mic, Phone, Plus, Radio, Settings, Truck, User, Zap } from 'lucide-react-native'; +import { type Href, router } from 'expo-router'; +import { AlertTriangle, ArrowRight, ChevronRight, Clock, CloudLightning, Filter, Info, Mic, Phone, Plus, Radio, Settings, ShieldCheck, Truck, User, Zap } from 'lucide-react-native'; import { useColorScheme } from 'nativewind'; -import React, { useCallback, useEffect, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Pressable, ScrollView, StyleSheet, View } from 'react-native'; @@ -13,10 +14,16 @@ import { VStack } from '@/components/ui/vstack'; import { type DispatchedEventResultData } from '@/models/v4/calls/dispatchedEventResultData'; import { type PersonnelInfoResultData } from '@/models/v4/personnel/personnelInfoResultData'; import { type UnitInfoResultData } from '@/models/v4/units/unitInfoResultData'; +import { SEVERITY_COLORS, WeatherAlertSeverity } from '@/models/v4/weatherAlerts/weatherAlertEnums'; +import { useCallsStore } from '@/stores/calls/store'; +import { useCheckInStore } from '@/stores/checkIn/store'; import { type RadioLogEntry } from '@/stores/dispatch/dispatch-console-store'; import { usePersonnelActionsStore } from '@/stores/dispatch/personnel-actions-store'; import { useUnitActionsStore } from '@/stores/dispatch/unit-actions-store'; +import { useWeatherAlertsStore } from '@/stores/weatherAlerts/store'; +import { CheckInBottomSheet } from '../checkIn/check-in-bottom-sheet'; +import { CheckInTimerCard } from '../checkIn/check-in-timer-card'; import { PanelHeader } from './panel-header'; import { PersonnelActionsPanel } from './personnel-actions-panel'; import { UnitActionsPanel } from './unit-actions-panel'; @@ -35,7 +42,7 @@ export interface ActivityLogEntry { }; } -type TabType = 'activity' | 'radio' | 'actions'; +type TabType = 'activity' | 'radio' | 'actions' | 'checkins' | 'weather'; interface ActivityLogPanelProps { entries: ActivityLogEntry[]; @@ -311,6 +318,13 @@ export const ActivityLogPanel: React.FC = ({ // Track previous call selection to avoid unnecessary tab switches const prevSelectedCallIdRef = useRef(undefined); + // Reset to activity tab if check-ins tab is active but call filter is cleared + useEffect(() => { + if (!isCallFilterActive && activeTab === 'checkins') { + setActiveTab('activity'); + } + }, [isCallFilterActive, activeTab]); + // Switch to actions tab when a call is selected (for quick note/close actions) useEffect(() => { const prevId = prevSelectedCallIdRef.current; @@ -374,6 +388,52 @@ export const ActivityLogPanel: React.FC = ({ // Active transmissions count const activeTransmissions = radioLog.filter((entry) => entry.isActive).length; + // Check-in timers — fetch timer statuses across all active calls + const calls = useCallsStore((s) => s.calls); + const { timerStatuses: allTimerStatuses, fetchTimerStatusesForCalls, startPollingForCalls: startCheckInPolling, stopPolling: stopCheckInPolling, isLoadingStatuses: isCheckInsLoading } = useCheckInStore(); + + const [isCheckInSheetOpen, setIsCheckInSheetOpen] = useState(false); + const [checkInSheetCallId, setCheckInSheetCallId] = useState(0); + const [checkInSheetTimer, setCheckInSheetTimer] = useState<(typeof allTimerStatuses)[0] | null>(null); + + // Get all active call IDs to fetch timer statuses for + const activeCallIds = useMemo(() => calls.map((c) => parseInt(c.CallId)).filter((id) => !isNaN(id) && id > 0), [calls]); + + // Fetch check-in timer statuses and restart polling whenever the calls list updates. + // Using the `calls` reference as a dependency ensures re-fetch after navigation back + // (when calls are refetched on focus) even if the call IDs themselves haven't changed. + useEffect(() => { + if (activeCallIds.length > 0) { + fetchTimerStatusesForCalls(activeCallIds); + startCheckInPolling(activeCallIds); + } + return () => { + stopCheckInPolling(); + }; + }, [calls, activeCallIds, fetchTimerStatusesForCalls, startCheckInPolling, stopCheckInPolling]); + + // When call filter is active, show only timers for the selected call + const filteredTimerStatuses = useMemo(() => { + if (!isCallFilterActive || !selectedCallId) return allTimerStatuses; + const callIdNum = parseInt(selectedCallId); + if (isNaN(callIdNum)) return allTimerStatuses; + return allTimerStatuses.filter((s) => s.CallId === callIdNum); + }, [allTimerStatuses, isCallFilterActive, selectedCallId]); + + const criticalCheckInCount = filteredTimerStatuses.filter((s) => s.Status === 'Critical').length; + const overdueCheckInCount = filteredTimerStatuses.filter((s) => s.Status === 'Overdue' || s.Status === 'Red').length; + const urgentCheckInCount = criticalCheckInCount + overdueCheckInCount; + + // Weather alerts + const { alerts: weatherAlerts, settings: weatherSettings } = useWeatherAlertsStore(); + const weatherAlertCount = weatherAlerts.length; + + const handleCheckInFromDashboard = useCallback((timer: (typeof allTimerStatuses)[0]) => { + setCheckInSheetCallId(timer.CallId); + setCheckInSheetTimer(timer); + setIsCheckInSheetOpen(true); + }, []); + // Determine what actions are available based on selection context const hasSelectedCall = isCallFilterActive && selectedCallId; const hasSelectedUnit = !!selectedUnitId; @@ -385,6 +445,10 @@ export const ActivityLogPanel: React.FC = ({ return t('dispatch.radio_log'); case 'actions': return t('dispatch.actions'); + case 'checkins': + return t('dispatch.check_ins'); + case 'weather': + return t('weatherAlerts.title'); default: return isCallFilterActive ? t('dispatch.call_activity') : t('dispatch.activity_log'); } @@ -395,11 +459,15 @@ export const ActivityLogPanel: React.FC = ({ case 'radio': return radioLog.length; case 'actions': - return undefined; + return urgentCheckInCount > 0 ? urgentCheckInCount : undefined; + case 'checkins': + return filteredTimerStatuses.length; + case 'weather': + return weatherAlertCount > 0 ? weatherAlertCount : undefined; default: return activityCount; } - }, [activeTab, radioLog.length, activityCount]); + }, [activeTab, radioLog.length, activityCount, filteredTimerStatuses.length, urgentCheckInCount, weatherAlertCount]); const renderActivityContent = () => { if (useCallActivity) { @@ -486,12 +554,104 @@ export const ActivityLogPanel: React.FC = ({ ); }; + const renderCheckInsContent = () => { + if (isCheckInsLoading && filteredTimerStatuses.length === 0) { + return ( + + {t('common.loading')} + + ); + } + + if (filteredTimerStatuses.length === 0) { + return ( + + + {t('check_in.no_timers')} + + ); + } + + return ( + <> + {/* Summary badges */} + {urgentCheckInCount > 0 && ( + + {criticalCheckInCount > 0 && ( + + + {criticalCheckInCount} {t('check_in.status_critical')} + + + )} + {overdueCheckInCount > 0 && ( + + {t('check_in.overdue_count', { count: overdueCheckInCount })} + + )} + + )} + + {filteredTimerStatuses.map((timer) => ( + + ))} + + ); + }; + + const renderWeatherContent = () => { + if (weatherAlerts.length === 0) { + return ( + + + {t('weatherAlerts.noActiveAlerts')} + + ); + } + + return weatherAlerts.map((alert) => { + const severityColor = SEVERITY_COLORS[alert.Severity] ?? SEVERITY_COLORS[WeatherAlertSeverity.Unknown]; + const itemStyle = StyleSheet.flatten([styles.weatherAlertItem, { borderLeftColor: severityColor }]); + return ( + router.push(`/(app)/weather-alerts/${alert.WeatherAlertId}` as Href)}> + + + {alert.Severity === 0 + ? t('weatherAlerts.severity.extreme') + : alert.Severity === 1 + ? t('weatherAlerts.severity.severe') + : alert.Severity === 2 + ? t('weatherAlerts.severity.moderate') + : t('weatherAlerts.severity.minor')} + + + + + {alert.Event} + + + {alert.AreaDescription} + + {alert.ExpiresUtc ? ( + + {t('weatherAlerts.detail.expires')}: {alert.ExpiresUtc} + + ) : null} + + ); + }); + }; + const renderTabContent = () => { switch (activeTab) { case 'radio': return renderRadioContent(); case 'actions': return renderActionsContent(); + case 'checkins': + return renderCheckInsContent(); + case 'weather': + return renderWeatherContent(); default: return renderActivityContent(); } @@ -501,8 +661,8 @@ export const ActivityLogPanel: React.FC = ({ setIsCollapsed(!isCollapsed)} @@ -542,7 +702,13 @@ export const ActivityLogPanel: React.FC = ({ setActiveTab('activity')} /> 0 ? activeTransmissions : undefined} onPress={() => setActiveTab('radio')} /> - setActiveTab('actions')} /> + {isCallFilterActive && ( + 0 ? urgentCheckInCount : undefined} onPress={() => setActiveTab('checkins')} /> + )} + 0 ? urgentCheckInCount : undefined} onPress={() => setActiveTab('actions')} /> + {weatherSettings?.WeatherAlertsEnabled !== false && ( + 0 ? weatherAlertCount : undefined} onPress={() => setActiveTab('weather')} /> + )} {/* Tab Content */} @@ -551,6 +717,18 @@ export const ActivityLogPanel: React.FC = ({ ) : null} + + {/* Check-in bottom sheet for performing check-ins from the dashboard */} + { + setIsCheckInSheetOpen(false); + setCheckInSheetTimer(null); + }} + callId={checkInSheetCallId} + selectedTimer={checkInSheetTimer} + timers={allTimerStatuses} + /> ); }; @@ -622,4 +800,11 @@ const styles = StyleSheet.create({ alignItems: 'center', justifyContent: 'center', }, + weatherAlertItem: { + borderLeftWidth: 3, + borderRadius: 6, + padding: 8, + marginBottom: 6, + backgroundColor: 'rgba(0,0,0,0.02)', + }, }); diff --git a/src/components/dispatch-console/personnel-panel.tsx b/src/components/dispatch-console/personnel-panel.tsx index b6711ae..43de7b1 100644 --- a/src/components/dispatch-console/personnel-panel.tsx +++ b/src/components/dispatch-console/personnel-panel.tsx @@ -1,4 +1,5 @@ -import { Building2, Circle, Filter, Phone, Plus, Search, User, Users, X } from 'lucide-react-native'; +import { type Href, router } from 'expo-router'; +import { Building2, Circle, ExternalLink, Filter, Phone, Plus, Search, User, Users, X } from 'lucide-react-native'; import { useColorScheme } from 'nativewind'; import React, { useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -80,6 +81,16 @@ const PersonnelItem: React.FC<{ + { + e.stopPropagation(); + router.push(`/personnel/${person.UserId}` as Href); + }} + hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }} + style={styles.detailsButton} + > + + {person.Status || t('dispatch.unknown')} @@ -138,14 +149,38 @@ export const PersonnelPanel: React.FC = ({ let filtered = personnel; if (isCallFilterActive && callDispatches && callDispatches.length > 0) { - // Get personnel names from dispatches (dispatches contain personnel info by name) - const dispatchedPersonnelNames = callDispatches.filter((d) => d.Type === 'Personnel' || d.Type === 'p').map((d) => d.Name.toLowerCase()); + // Log dispatch types for debugging + if (__DEV__) { + console.log( + '[PersonnelPanel] callDispatches types:', + callDispatches.map((d) => ({ Type: d.Type, Name: d.Name })) + ); + } + + // Match personnel to dispatches — prefer stable ID matching, fall back to name + const personnelTypes = new Set(['Personnel', 'personnel', 'p', 'P', 'User', 'user']); + const personnelDispatches = callDispatches.filter((d) => personnelTypes.has(d.Type)); + const dispatchedIds = new Set(personnelDispatches.map((d) => d.Id).filter(Boolean)); + const dispatchedNames = new Set(personnelDispatches.map((d) => d.Name.toLowerCase())); - // Also check personnel whose StatusDestinationId matches the call filtered = personnel.filter((p) => { + if (dispatchedIds.size > 0 && dispatchedIds.has(p.UserId)) return true; + if (selectedCallId && p.StatusDestinationId === selectedCallId) return true; const fullName = `${p.FirstName} ${p.LastName}`.toLowerCase(); - return dispatchedPersonnelNames.includes(fullName) || (selectedCallId && p.StatusDestinationId === selectedCallId); + return dispatchedNames.has(fullName); }); + + // If strict type matching found nothing, try matching ALL dispatch names/IDs as fallback + if (filtered.length === 0) { + const allDispatchIds = new Set(callDispatches.map((d) => d.Id).filter(Boolean)); + const allDispatchNames = new Set(callDispatches.map((d) => d.Name.toLowerCase())); + filtered = personnel.filter((p) => { + if (allDispatchIds.has(p.UserId)) return true; + if (selectedCallId && p.StatusDestinationId === selectedCallId) return true; + const fullName = `${p.FirstName} ${p.LastName}`.toLowerCase(); + return allDispatchNames.has(fullName); + }); + } } // Apply search filter @@ -164,10 +199,26 @@ export const PersonnelPanel: React.FC = ({ return filtered; }, [personnel, isCallFilterActive, callDispatches, selectedCallId, searchQuery]); - // Get list of personnel names that are dispatched to the call + // Get dispatched personnel IDs and names for highlight matching + const dispatchedPersonnelIds = useMemo(() => { + if (!callDispatches) return new Set(); + const personnelTypes = new Set(['Personnel', 'personnel', 'p', 'P', 'User', 'user']); + const byType = callDispatches.filter((d) => personnelTypes.has(d.Type)); + const ids = new Set(byType.map((d) => d.Id).filter(Boolean)); + if (ids.size === 0) { + return new Set(callDispatches.map((d) => d.Id).filter(Boolean)); + } + return ids; + }, [callDispatches]); + const dispatchedPersonnelNames = useMemo(() => { if (!callDispatches) return new Set(); - return new Set(callDispatches.filter((d) => d.Type === 'Personnel' || d.Type === 'p').map((d) => d.Name.toLowerCase())); + const personnelTypes = new Set(['Personnel', 'personnel', 'p', 'P', 'User', 'user']); + const byType = new Set(callDispatches.filter((d) => personnelTypes.has(d.Type)).map((d) => d.Name.toLowerCase())); + if (byType.size === 0) { + return new Set(callDispatches.map((d) => d.Name.toLowerCase())); + } + return byType; }, [callDispatches]); // Count on-duty personnel @@ -242,7 +293,7 @@ export const PersonnelPanel: React.FC = ({ key={person.UserId} person={person} isSelected={selectedPersonnelId === person.UserId} - isOnCall={dispatchedPersonnelNames.has(fullName) || Boolean(selectedCallId && person.StatusDestinationId === selectedCallId)} + isOnCall={dispatchedPersonnelIds.has(person.UserId) || dispatchedPersonnelNames.has(fullName) || Boolean(selectedCallId && person.StatusDestinationId === selectedCallId)} onPress={() => handleSelectPersonnel(person.UserId)} onSetStatus={isCallFilterActive && onSetPersonnelStatusForCall ? () => onSetPersonnelStatusForCall(person.UserId, `${person.FirstName} ${person.LastName}`) : undefined} /> @@ -284,6 +335,11 @@ const styles = StyleSheet.create({ iconButton: { padding: 4, }, + detailsButton: { + padding: 4, + borderRadius: 4, + backgroundColor: 'rgba(107, 114, 128, 0.1)', + }, statusButton: { padding: 4, backgroundColor: 'rgba(99, 102, 241, 0.1)', diff --git a/src/components/dispatch-console/units-panel.tsx b/src/components/dispatch-console/units-panel.tsx index 3bf230b..8f4331f 100644 --- a/src/components/dispatch-console/units-panel.tsx +++ b/src/components/dispatch-console/units-panel.tsx @@ -1,4 +1,5 @@ -import { Building2, Circle, Filter, MapPin, Phone, Plus, Search, Truck, X } from 'lucide-react-native'; +import { type Href, router } from 'expo-router'; +import { Building2, Circle, ExternalLink, Filter, MapPin, Phone, Plus, Search, Truck, X } from 'lucide-react-native'; import React, { useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Pressable, ScrollView, StyleSheet, TextInput, View } from 'react-native'; @@ -89,6 +90,16 @@ const UnitItem: React.FC<{ + { + e.stopPropagation(); + router.push(`/units/${unit.UnitId}` as Href); + }} + hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }} + style={styles.detailsButton} + > + + {unit.CurrentStatus || unit.Note || t('dispatch.available')} @@ -265,6 +276,11 @@ const styles = StyleSheet.create({ iconButton: { padding: 4, }, + detailsButton: { + padding: 4, + borderRadius: 4, + backgroundColor: 'rgba(107, 114, 128, 0.1)', + }, statusButton: { padding: 4, backgroundColor: 'rgba(99, 102, 241, 0.1)', diff --git a/src/components/maps/unified-map-view.tsx b/src/components/maps/unified-map-view.tsx index b76adfd..a9b6105 100644 --- a/src/components/maps/unified-map-view.tsx +++ b/src/components/maps/unified-map-view.tsx @@ -9,6 +9,7 @@ import { logger } from '@/lib/logging'; import { type MapMakerInfoData } from '@/models/v4/mapping/getMapDataAndMarkersData'; import { type GetMapLayersData } from '@/models/v4/mapping/getMapLayersResultData'; import { useLocationStore } from '@/stores/app/location-store'; +import { useSignalRStore } from '@/stores/signalr/signalr-store'; import MapPins from './map-pins'; @@ -53,6 +54,9 @@ export const UnifiedMapView: React.FC = ({ const cameraRef = useRef(null); const [isMapReady, setIsMapReady] = useState(false); const [internalPins, setInternalPins] = useState([]); + const lastUpdateTimestamp = useSignalRStore((state) => state.lastUpdateTimestamp); + const signalRDebounceTimer = useRef | null>(null); + const signalRAbortController = useRef(null); const location = useLocationStore((state) => ({ latitude: state.latitude, @@ -148,6 +152,55 @@ export const UnifiedMapView: React.FC = ({ }; }, [autoFetchPins]); + // Refresh pins when SignalR updates come in (only when autoFetchPins is enabled) + useEffect(() => { + if (!autoFetchPins || lastUpdateTimestamp <= 0) return; + + // Clear any existing debounce timer + if (signalRDebounceTimer.current) { + clearTimeout(signalRDebounceTimer.current); + } + + // Debounce to prevent rapid consecutive API calls from multiple SignalR events + signalRDebounceTimer.current = setTimeout(async () => { + // Abort any in-flight SignalR-triggered fetch + if (signalRAbortController.current) { + signalRAbortController.current.abort(); + } + const controller = new AbortController(); + signalRAbortController.current = controller; + + try { + logger.debug({ + message: 'Refreshing map pins from SignalR update', + context: { timestamp: lastUpdateTimestamp }, + }); + + const mapDataAndMarkers = await getMapDataAndMarkers(controller.signal); + if (!controller.signal.aborted && mapDataAndMarkers?.Data) { + setInternalPins(mapDataAndMarkers.Data.MapMakerInfos); + } + } catch (error) { + if (error instanceof Error && (error.name === 'AbortError' || error.message === 'canceled')) { + return; + } + logger.error({ + message: 'Failed to refresh map pins from SignalR update', + context: { error }, + }); + } + }, 1500); + + return () => { + if (signalRDebounceTimer.current) { + clearTimeout(signalRDebounceTimer.current); + } + if (signalRAbortController.current) { + signalRAbortController.current.abort(); + } + }; + }, [autoFetchPins, lastUpdateTimestamp]); + // Helper function to get layer style based on type const getLayerStyle = (layer: GetMapLayersData): FillLayerStyle | LineLayerStyle | CircleLayerStyle => { const color = layer.Color || '#3b82f6'; diff --git a/src/components/notifications/NotificationInbox.tsx b/src/components/notifications/NotificationInbox.tsx index 17ce312..43daf40 100644 --- a/src/components/notifications/NotificationInbox.tsx +++ b/src/components/notifications/NotificationInbox.tsx @@ -307,7 +307,6 @@ export const NotificationInbox = ({ isOpen, onClose }: NotificationInboxProps) = ListFooterComponent={renderFooter} ListEmptyComponent={renderEmpty} refreshControl={} - estimatedItemSize={80} /> )} diff --git a/src/components/settings/language-item.tsx b/src/components/settings/language-item.tsx index 8e78c86..2ec7f41 100644 --- a/src/components/settings/language-item.tsx +++ b/src/components/settings/language-item.tsx @@ -24,6 +24,13 @@ export const LanguageItem = () => { () => [ { label: translate('settings.english'), value: 'en' }, { label: translate('settings.spanish'), value: 'es' }, + { label: translate('settings.swedish'), value: 'sv' }, + { label: translate('settings.german'), value: 'de' }, + { label: translate('settings.french'), value: 'fr' }, + { label: translate('settings.italian'), value: 'it' }, + { label: translate('settings.polish'), value: 'pl' }, + { label: translate('settings.ukrainian'), value: 'uk' }, + { label: translate('settings.arabic'), value: 'ar' }, ], [] ); diff --git a/src/components/sidebar/side-menu.tsx b/src/components/sidebar/side-menu.tsx index 862a1fa..342736e 100644 --- a/src/components/sidebar/side-menu.tsx +++ b/src/components/sidebar/side-menu.tsx @@ -1,5 +1,5 @@ import { type Href, useRouter } from 'expo-router'; -import { Contact, FileText, Home, List, type LucideIcon, Map as MapIcon, MessageCircle, Phone, Plus, Settings, Truck, Users } from 'lucide-react-native'; +import { CloudLightning, Contact, FileText, Home, List, type LucideIcon, Map as MapIcon, MessageCircle, Phone, Plus, Settings, Truck, Users } from 'lucide-react-native'; import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'; @@ -31,6 +31,7 @@ const getMenuItems = (t: (key: string) => string): MenuItem[] => [ { id: 'map', label: t('menu.map'), icon: MapIcon, route: '/map' }, { id: 'personnel', label: t('menu.personnel'), icon: Users, route: '/personnel' }, { id: 'units', label: t('menu.units'), icon: Truck, route: '/units' }, + { id: 'weather-alerts', label: t('menu.weatherAlerts'), icon: CloudLightning, route: '/weather-alerts' }, { id: 'protocols', label: t('menu.protocols'), icon: FileText, route: '/protocols' }, { id: 'contacts', label: t('menu.contacts'), icon: Contact, route: '/contacts' }, { id: 'settings', label: t('menu.settings'), icon: Settings, route: '/settings' }, diff --git a/src/components/ui/flat-list.tsx b/src/components/ui/flat-list.tsx new file mode 100644 index 0000000..bc5e26f --- /dev/null +++ b/src/components/ui/flat-list.tsx @@ -0,0 +1 @@ +export { FlatList } from 'react-native'; diff --git a/src/components/weatherAlerts/weather-alert-banner.tsx b/src/components/weatherAlerts/weather-alert-banner.tsx new file mode 100644 index 0000000..d2ed2f4 --- /dev/null +++ b/src/components/weatherAlerts/weather-alert-banner.tsx @@ -0,0 +1,78 @@ +import { AlertTriangle, Cloud, Flame, Heart, Leaf } from 'lucide-react-native'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Platform, Pressable, StyleSheet, View } from 'react-native'; + +import { HStack } from '@/components/ui/hstack'; +import { Icon } from '@/components/ui/icon'; +import { Text } from '@/components/ui/text'; +import { SEVERITY_COLORS, WeatherAlertCategory, WeatherAlertSeverity } from '@/models/v4/weatherAlerts/weatherAlertEnums'; +import { type WeatherAlertResultData } from '@/models/v4/weatherAlerts/weatherAlertResultData'; + +const getCategoryIcon = (category: number) => { + switch (category) { + case WeatherAlertCategory.Fire: + return Flame; + case WeatherAlertCategory.Health: + return Heart; + case WeatherAlertCategory.Env: + return Leaf; + case WeatherAlertCategory.Met: + return Cloud; + default: + return AlertTriangle; + } +}; + +interface WeatherAlertBannerProps { + alerts: WeatherAlertResultData[]; + onPress: () => void; +} + +export const WeatherAlertBanner: React.FC = ({ alerts, onPress }) => { + const { t } = useTranslation(); + + if (alerts.length === 0) return null; + + // Sort by severity to show highest first + const sorted = [...alerts].sort((a, b) => a.Severity - b.Severity); + const topAlert = sorted[0]; + const remaining = alerts.length - 1; + const bgColor = SEVERITY_COLORS[topAlert.Severity] ?? SEVERITY_COLORS[WeatherAlertSeverity.Unknown]; + const CategoryIcon = getCategoryIcon(topAlert.AlertCategory); + + const containerStyle = StyleSheet.flatten([styles.container, { backgroundColor: bgColor }]); + + return ( + + + + + + + {topAlert.Event} + + {remaining > 0 && {t('weatherAlerts.moreAlerts', { count: remaining })}} + + + {topAlert.AreaDescription} + {topAlert.ExpiresUtc ? ` — ${t('weatherAlerts.detail.expires')} ${topAlert.ExpiresUtc}` : ''} + + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + marginHorizontal: 8, + marginVertical: 4, + borderRadius: 8, + paddingHorizontal: 12, + paddingVertical: 10, + }, + textContainer: { + flex: 1, + }, +}); diff --git a/src/components/weatherAlerts/weather-alert-card.tsx b/src/components/weatherAlerts/weather-alert-card.tsx new file mode 100644 index 0000000..3a30cc5 --- /dev/null +++ b/src/components/weatherAlerts/weather-alert-card.tsx @@ -0,0 +1,139 @@ +import { AlertTriangle, Cloud, Flame, Heart, Leaf } from 'lucide-react-native'; +import { useColorScheme } from 'nativewind'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Pressable, StyleSheet, View } from 'react-native'; + +import { HStack } from '@/components/ui/hstack'; +import { Icon } from '@/components/ui/icon'; +import { Text } from '@/components/ui/text'; +import { VStack } from '@/components/ui/vstack'; +import { SEVERITY_COLORS, WeatherAlertCategory, WeatherAlertCertainty, WeatherAlertSeverity, WeatherAlertUrgency } from '@/models/v4/weatherAlerts/weatherAlertEnums'; +import { type WeatherAlertResultData } from '@/models/v4/weatherAlerts/weatherAlertResultData'; + +const getCategoryIcon = (category: number) => { + switch (category) { + case WeatherAlertCategory.Fire: + return Flame; + case WeatherAlertCategory.Health: + return Heart; + case WeatherAlertCategory.Env: + return Leaf; + case WeatherAlertCategory.Met: + return Cloud; + default: + return AlertTriangle; + } +}; + +const getSeverityLabel = (severity: number, t: (key: string) => string): string => { + switch (severity) { + case WeatherAlertSeverity.Extreme: + return t('weatherAlerts.severity.extreme'); + case WeatherAlertSeverity.Severe: + return t('weatherAlerts.severity.severe'); + case WeatherAlertSeverity.Moderate: + return t('weatherAlerts.severity.moderate'); + case WeatherAlertSeverity.Minor: + return t('weatherAlerts.severity.minor'); + default: + return t('weatherAlerts.severity.unknown'); + } +}; + +const getUrgencyLabel = (urgency: number, t: (key: string) => string): string => { + switch (urgency) { + case WeatherAlertUrgency.Immediate: + return t('weatherAlerts.urgency.immediate'); + case WeatherAlertUrgency.Expected: + return t('weatherAlerts.urgency.expected'); + case WeatherAlertUrgency.Future: + return t('weatherAlerts.urgency.future'); + case WeatherAlertUrgency.Past: + return t('weatherAlerts.urgency.past'); + default: + return t('weatherAlerts.urgency.unknown'); + } +}; + +const getCertaintyLabel = (certainty: number, t: (key: string) => string): string => { + switch (certainty) { + case WeatherAlertCertainty.Observed: + return t('weatherAlerts.certainty.observed'); + case WeatherAlertCertainty.Likely: + return t('weatherAlerts.certainty.likely'); + case WeatherAlertCertainty.Possible: + return t('weatherAlerts.certainty.possible'); + case WeatherAlertCertainty.Unlikely: + return t('weatherAlerts.certainty.unlikely'); + default: + return t('weatherAlerts.certainty.unknown'); + } +}; + +interface WeatherAlertCardProps { + alert: WeatherAlertResultData; + onPress: (alertId: string) => void; +} + +export const WeatherAlertCard: React.FC = ({ alert, onPress }) => { + const { t } = useTranslation(); + const { colorScheme } = useColorScheme(); + const isDark = colorScheme === 'dark'; + const severityColor = SEVERITY_COLORS[alert.Severity] ?? SEVERITY_COLORS[WeatherAlertSeverity.Unknown]; + const CategoryIcon = getCategoryIcon(alert.AlertCategory); + + const cardStyle = StyleSheet.flatten([styles.card, { borderLeftColor: severityColor }, isDark ? styles.cardDark : styles.cardLight]); + + return ( + onPress(alert.WeatherAlertId)} style={cardStyle}> + + + + + {getSeverityLabel(alert.Severity, t)} + + + + + + {alert.Event} + + + + {alert.AreaDescription} + + + {alert.ExpiresUtc && ( + + {t('weatherAlerts.detail.expires')}: {alert.ExpiresUtc} + + )} + + + {t('weatherAlerts.detail.urgency')}: {getUrgencyLabel(alert.Urgency, t)} · {t('weatherAlerts.detail.certainty')}: {getCertaintyLabel(alert.Certainty, t)} + + + + ); +}; + +const styles = StyleSheet.create({ + card: { + borderLeftWidth: 4, + borderRadius: 8, + padding: 12, + marginBottom: 8, + }, + cardLight: { + backgroundColor: '#ffffff', + }, + cardDark: { + backgroundColor: '#1f2937', + }, + severityDot: { + width: 8, + height: 8, + borderRadius: 4, + }, +}); diff --git a/src/lib/form-render/form-render-source.ts b/src/lib/form-render/form-render-source.ts index 10bff0e..cdd47dc 100644 --- a/src/lib/form-render/form-render-source.ts +++ b/src/lib/form-render/form-render-source.ts @@ -1,4 +1,5 @@ // Auto-generated – do not edit manually. // Regenerate via: node scripts/generate-vendor-sources.js -const formRenderSource: string = "/*! For license information please see form-render.min.js.LICENSE.txt */\n!function(e){\"use strict\";!function(){var t={962:function(e,t,r){r.r(t);var n=r(645),o=r.n(n),i=r(278),s=r.n(i)()(o());s.push([e.id,'.rendered-form *{box-sizing:border-box}.rendered-form.formbuilder-embedded-bootstrap button,.rendered-form.formbuilder-embedded-bootstrap input,.rendered-form.formbuilder-embedded-bootstrap select,.rendered-form.formbuilder-embedded-bootstrap textarea{font-family:inherit;font-size:inherit;line-height:inherit}.rendered-form.formbuilder-embedded-bootstrap input{line-height:normal}.rendered-form.formbuilder-embedded-bootstrap textarea{overflow:auto}.rendered-form.formbuilder-embedded-bootstrap button,.rendered-form.formbuilder-embedded-bootstrap input,.rendered-form.formbuilder-embedded-bootstrap select,.rendered-form.formbuilder-embedded-bootstrap textarea{font-family:inherit;font-size:inherit;line-height:inherit}.rendered-form.formbuilder-embedded-bootstrap .btn-group{position:relative;display:inline-block;vertical-align:middle}.rendered-form.formbuilder-embedded-bootstrap .btn-group>.btn{position:relative;float:left}.rendered-form.formbuilder-embedded-bootstrap .btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.rendered-form.formbuilder-embedded-bootstrap .btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.rendered-form.formbuilder-embedded-bootstrap .btn-group .btn+.btn,.rendered-form.formbuilder-embedded-bootstrap .btn-group .btn+.btn-group,.rendered-form.formbuilder-embedded-bootstrap .btn-group .btn-group+.btn,.rendered-form.formbuilder-embedded-bootstrap .btn-group .btn-group+.btn-group{margin-left:-1px}.rendered-form.formbuilder-embedded-bootstrap .btn-group>.btn:last-child:not(:first-child),.rendered-form.formbuilder-embedded-bootstrap .btn-group>.dropdown-toggle:not(:first-child),.rendered-form.formbuilder-embedded-bootstrap .btn-group .input-group .form-control:last-child,.rendered-form.formbuilder-embedded-bootstrap .btn-group .input-group-addon:last-child,.rendered-form.formbuilder-embedded-bootstrap .btn-group .input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.rendered-form.formbuilder-embedded-bootstrap .btn-group .input-group-btn:first-child>.btn:not(:first-child),.rendered-form.formbuilder-embedded-bootstrap .btn-group .input-group-btn:last-child>.btn,.rendered-form.formbuilder-embedded-bootstrap .btn-group .input-group-btn:last-child>.btn-group>.btn,.rendered-form.formbuilder-embedded-bootstrap .btn-group .input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.rendered-form.formbuilder-embedded-bootstrap .btn-group>.btn.active,.rendered-form.formbuilder-embedded-bootstrap .btn-group>.btn:active,.rendered-form.formbuilder-embedded-bootstrap .btn-group>.btn:focus,.rendered-form.formbuilder-embedded-bootstrap .btn-group>.btn:hover{z-index:2}.rendered-form.formbuilder-embedded-bootstrap .btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-image:none;border-radius:4px}.rendered-form.formbuilder-embedded-bootstrap .btn.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.rendered-form.formbuilder-embedded-bootstrap .btn.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.rendered-form.formbuilder-embedded-bootstrap .btn.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.rendered-form.formbuilder-embedded-bootstrap .btn.active,.rendered-form.formbuilder-embedded-bootstrap .btn.btn-active,.rendered-form.formbuilder-embedded-bootstrap .btn:active{background-image:none}.rendered-form.formbuilder-embedded-bootstrap .input-group .form-control:last-child,.rendered-form.formbuilder-embedded-bootstrap .input-group-addon:last-child,.rendered-form.formbuilder-embedded-bootstrap .input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.rendered-form.formbuilder-embedded-bootstrap .input-group-btn:first-child>.btn:not(:first-child),.rendered-form.formbuilder-embedded-bootstrap .input-group-btn:last-child>.btn,.rendered-form.formbuilder-embedded-bootstrap .input-group-btn:last-child>.btn-group>.btn,.rendered-form.formbuilder-embedded-bootstrap .input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.rendered-form.formbuilder-embedded-bootstrap .input-group .form-control,.rendered-form.formbuilder-embedded-bootstrap .input-group-addon,.rendered-form.formbuilder-embedded-bootstrap .input-group-btn{display:table-cell}.rendered-form.formbuilder-embedded-bootstrap .input-group-lg>.form-control,.rendered-form.formbuilder-embedded-bootstrap .input-group-lg>.input-group-addon,.rendered-form.formbuilder-embedded-bootstrap .input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333}.rendered-form.formbuilder-embedded-bootstrap .input-group{position:relative;display:table;border-collapse:separate}.rendered-form.formbuilder-embedded-bootstrap .input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.rendered-form.formbuilder-embedded-bootstrap .form-control,.rendered-form.formbuilder-embedded-bootstrap output{font-size:14px;line-height:1.42857143;display:block}.rendered-form.formbuilder-embedded-bootstrap textarea.form-control{height:auto}.rendered-form.formbuilder-embedded-bootstrap .form-control{height:34px;display:block;width:100%;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px}.rendered-form.formbuilder-embedded-bootstrap .form-control:focus{outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.rendered-form.formbuilder-embedded-bootstrap .form-group{margin-left:0px;margin-bottom:15px}.rendered-form.formbuilder-embedded-bootstrap .btn,.rendered-form.formbuilder-embedded-bootstrap .form-control{background-image:none}.rendered-form.formbuilder-embedded-bootstrap .pull-right{float:right}.rendered-form.formbuilder-embedded-bootstrap .pull-left{float:left}.rendered-form .formbuilder-required,.rendered-form .required-asterisk{color:#c10000}.rendered-form .formbuilder-checkbox-group input[type=checkbox],.rendered-form .formbuilder-checkbox-group input[type=radio],.rendered-form .formbuilder-radio-group input[type=checkbox],.rendered-form .formbuilder-radio-group input[type=radio]{margin:0 4px 0 0}.rendered-form .formbuilder-checkbox-inline,.rendered-form .formbuilder-radio-inline{margin-right:8px;display:inline-block;vertical-align:middle;padding-left:0}.rendered-form .formbuilder-checkbox-inline label input[type=text],.rendered-form .formbuilder-radio-inline label input[type=text]{margin-top:0}.rendered-form .formbuilder-checkbox-inline:first-child,.rendered-form .formbuilder-radio-inline:first-child{padding-left:0}.rendered-form .formbuilder-autocomplete-list{background-color:#fff;display:none;list-style:none;padding:0;border:1px solid #ccc;border-width:0 1px 1px;position:absolute;z-index:20;max-height:200px;overflow-y:auto}.rendered-form .formbuilder-autocomplete-list li{display:none;cursor:default;padding:5px;margin:0;transition:background-color 200ms ease-in-out}.rendered-form .formbuilder-autocomplete-list li:hover,.rendered-form .formbuilder-autocomplete-list li.active-option{background-color:rgba(0,0,0,.075)}.rendered-form *[tooltip]{position:relative}.rendered-form *[tooltip]:hover::after{background:rgba(0,0,0,.9);border-radius:5px 5px 5px 0;bottom:23px;color:#fff;content:attr(tooltip);padding:10px 5px;position:absolute;z-index:98;left:2px;width:230px;text-shadow:none;font-size:12px;line-height:1.5em;cursor:default}.rendered-form *[tooltip]:hover::before{border:solid;border-color:#222 rgba(0,0,0,0);border-width:6px 6px 0;bottom:17px;content:\"\";left:2px;position:absolute;z-index:99;cursor:default}.rendered-form .tooltip-element{visibility:visible;color:#fff;background:#000;width:16px;height:16px;border-radius:8px;display:inline-block;text-align:center;line-height:16px;margin:0 5px;font-size:12px;cursor:default}.rendered-form .kc-toggle{padding-left:0 !important}.rendered-form .kc-toggle span{position:relative;width:48px;height:24px;background:#e6e6e6;display:inline-block;border-radius:4px;border:1px solid #ccc;padding:2px;overflow:hidden;float:left;margin-right:5px;will-change:transform}.rendered-form .kc-toggle span::after,.rendered-form .kc-toggle span::before{position:absolute;display:inline-block;top:0}.rendered-form .kc-toggle span::after{position:relative;content:\"\";width:50%;height:100%;left:0;border-radius:3px;background:linear-gradient(to bottom, white 0%, #ccc 100%);border:1px solid #999;transition:transform 100ms;transform:translateX(0)}.rendered-form .kc-toggle span::before{border-radius:4px;top:2px;left:2px;content:\"\";width:calc(100% - 4px);height:18px;box-shadow:0 0 1px 1px #b3b3b3 inset;background-color:rgba(0,0,0,0)}.rendered-form .kc-toggle input{height:0;overflow:hidden;width:0;opacity:0;pointer-events:none;margin:0}.rendered-form .kc-toggle input:checked+span::after{transform:translateX(100%)}.rendered-form .kc-toggle input:checked+span::before{background-color:#6fc665}.rendered-form label{font-weight:normal}.rendered-form .form-group .formbuilder-required{color:#c10000}.rendered-form .other-option:checked+label input{display:inline-block}.rendered-form .other-val{margin-left:5px;display:none}.rendered-form .form-control.number{width:auto}.rendered-form .form-control[type=color]{width:60px;padding:2px;display:inline-block}.rendered-form .form-control[multiple]{height:auto}',\"\"]),t.default=s},278:function(e){e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var r=\"\",n=void 0!==t[5];return t[4]&&(r+=\"@supports (\".concat(t[4],\") {\")),t[2]&&(r+=\"@media \".concat(t[2],\" {\")),n&&(r+=\"@layer\".concat(t[5].length>0?\" \".concat(t[5]):\"\",\" {\")),r+=e(t),n&&(r+=\"}\"),t[2]&&(r+=\"}\"),t[4]&&(r+=\"}\"),r})).join(\"\")},t.i=function(e,r,n,o,i){\"string\"==typeof e&&(e=[[null,e,void 0]]);var s={};if(n)for(var a=0;a0?\" \".concat(c[5]):\"\",\" {\").concat(c[1],\"}\")),c[5]=i),r&&(c[2]?(c[1]=\"@media \".concat(c[2],\" {\").concat(c[1],\"}\"),c[2]=r):c[2]=r),o&&(c[4]?(c[1]=\"@supports (\".concat(c[4],\") {\").concat(c[1],\"}\"),c[4]=o):c[4]=\"\".concat(o)),t.push(c))}},t}},645:function(e){e.exports=function(e){return e[1]}},252:function(e){e.exports=function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){\"undefined\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(e,\"__esModule\",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&\"object\"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,\"default\",{enumerable:!0,value:e}),2&t&&\"string\"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,\"a\",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p=\"\",r(r.s=7)}([function(e,t,r){var n=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&\"function\"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?\"symbol\":typeof e},o=r(2),i=r(10),s=Object.prototype.toString;function a(e){return\"[object Array]\"===s.call(e)}function l(e){return null!==e&&\"object\"===(void 0===e?\"undefined\":n(e))}function d(e){return\"[object Function]\"===s.call(e)}function c(e,t){if(null!=e)if(\"object\"!==(void 0===e?\"undefined\":n(e))&&(e=[e]),a(e))for(var r=0,o=e.length;r=200&&e<300},headers:{common:{Accept:\"application/json, text/plain, */*\"}}};n.forEach([\"delete\",\"get\",\"head\"],(function(e){a.headers[e]={}})),n.forEach([\"post\",\"put\",\"patch\"],(function(e){a.headers[e]=n.merge(i)})),e.exports=a}).call(this,r(12))},function(e,t,r){e.exports=function(e,t){return function(){for(var r=new Array(arguments.length),n=0;n0&&void 0!==arguments[0]?arguments[0]:s;!function(e,t){if(!(e instanceof t))throw new TypeError(\"Cannot call a class as a function\")}(this,e),this.langs=Object.create(null),this.loaded=[],this.processConfig(t)}return e.prototype.processConfig=function(e){var t=this,r=Object.assign({},s,e),n=r.location,o=function(e,t){var r={};for(var n in e)t.indexOf(n)>=0||Object.prototype.hasOwnProperty.call(e,n)&&(r[n]=e[n]);return r}(r,[\"location\"]),i=n.replace(/\\/?$/,\"/\");this.config=Object.assign({},{location:i},o);var a=this.config,l=a.override,d=a.preloaded,c=void 0===d?{}:d,u=Object.entries(this.langs).concat(Object.entries(l||c));this.langs=u.reduce((function(e,r){var n=r[0],o=r[1];return e[n]=t.applyLanguage.call(t,n,o),e}),{}),this.locale=this.config.locale||this.config.langs[0]},e.prototype.init=function(e){return this.processConfig.call(this,Object.assign({},this.config,e)),this.setCurrent(this.locale)},e.prototype.addLanguage=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};t=\"string\"==typeof t?this.processFile.call(this,t):t,this.applyLanguage.call(this,e,t),this.config.langs.push(\"locale\")},e.prototype.getValue=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.locale;return this.langs[t]&&this.langs[t][e]||this.getFallbackValue(e)},e.prototype.getFallbackValue=function(e){var t=Object.values(this.langs).find((function(t){return t[e]}));return t&&t[e]},e.prototype.makeSafe=function(e){var t={\"{\":\"\\\\{\",\"}\":\"\\\\}\",\"|\":\"\\\\|\"};return e=e.replace(/\\{|\\}|\\|/g,(function(e){return t[e]})),new RegExp(e,\"g\")},e.prototype.put=function(e,t){return this.current[e]=t},e.prototype.get=function(e,t){var r=this.getValue(e);if(r){var o=r.match(/\\{[^}]+?\\}/g),i=void 0;if(t&&o)if(\"object\"===(void 0===t?\"undefined\":n(t)))for(var s=0;s1&&void 0!==arguments[1])||arguments[1],r=this;return new Promise((function(n,o){if(-1!==r.loaded.indexOf(e)&&t)return r.applyLanguage.call(r,r.langs[e]),n(r.langs[e]);var s=[r.config.location,e,r.config.extension].join(\"\");return(0,i.get)(s).then((function(t){var o=t.data,i=r.processFile(o);return r.applyLanguage.call(r,e,i),r.loaded.push(e),n(r.langs[e])})).catch((function(){var t=r.applyLanguage.call(r,e);n(t)}))}))},e.prototype.applyLanguage=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=this.config.override[e]||{},n=this.langs[e]||{};return this.langs[e]=Object.assign({},n,t,r),this.langs[e]},e.prototype.setCurrent=function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:\"en-US\";return this.loadLang(t).then((function(){return e.locale=t,e.current=e.langs[t],e.current}))},o(e,[{key:\"getLangs\",get:function(){return this.config.langs}}]),e}();t.default=new a},function(e,t,r){e.exports=r(9)},function(e,t,r){var n=r(0),o=r(2),i=r(11),s=r(1);function a(e){var t=new i(e),r=o(i.prototype.request,t);return n.extend(r,i.prototype,t),n.extend(r,t),r}var l=a(s);l.Axios=i,l.create=function(e){return a(n.merge(s,e))},l.Cancel=r(6),l.CancelToken=r(26),l.isCancel=r(5),l.all=function(e){return Promise.all(e)},l.spread=r(27),e.exports=l,e.exports.default=l},function(e,t,r){function n(e){return!!e.constructor&&\"function\"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}e.exports=function(e){return null!=e&&(n(e)||function(e){return\"function\"==typeof e.readFloatLE&&\"function\"==typeof e.slice&&n(e.slice(0,0))}(e)||!!e._isBuffer)}},function(e,t,r){var n=r(1),o=r(0),i=r(21),s=r(22);function a(e){this.defaults=e,this.interceptors={request:new i,response:new i}}a.prototype.request=function(e){\"string\"==typeof e&&(e=o.merge({url:arguments[0]},arguments[1])),(e=o.merge(n,{method:\"get\"},this.defaults,e)).method=e.method.toLowerCase();var t=[s,void 0],r=Promise.resolve(e);for(this.interceptors.request.forEach((function(e){t.unshift(e.fulfilled,e.rejected)})),this.interceptors.response.forEach((function(e){t.push(e.fulfilled,e.rejected)}));t.length;)r=r.then(t.shift(),t.shift());return r},o.forEach([\"delete\",\"get\",\"head\",\"options\"],(function(e){a.prototype[e]=function(t,r){return this.request(o.merge(r||{},{method:e,url:t}))}})),o.forEach([\"post\",\"put\",\"patch\"],(function(e){a.prototype[e]=function(t,r,n){return this.request(o.merge(n||{},{method:e,url:t,data:r}))}})),e.exports=a},function(e,t,r){var n,o,i=e.exports={};function s(){throw new Error(\"setTimeout has not been defined\")}function a(){throw new Error(\"clearTimeout has not been defined\")}function l(e){if(n===setTimeout)return setTimeout(e,0);if((n===s||!n)&&setTimeout)return n=setTimeout,setTimeout(e,0);try{return n(e,0)}catch(t){try{return n.call(null,e,0)}catch(t){return n.call(this,e,0)}}}!function(){try{n=\"function\"==typeof setTimeout?setTimeout:s}catch(e){n=s}try{o=\"function\"==typeof clearTimeout?clearTimeout:a}catch(e){o=a}}();var d,c=[],u=!1,f=-1;function p(){u&&d&&(u=!1,d.length?c=d.concat(c):f=-1,c.length&&m())}function m(){if(!u){var e=l(p);u=!0;for(var t=c.length;t;){for(d=c,c=[];++f1)for(var r=1;r=0)return;s[t]=\"set-cookie\"===t?(s[t]?s[t]:[]).concat([r]):s[t]?s[t]+\", \"+r:r}})),s):s}},function(e,t,r){var n=r(0);e.exports=n.isStandardBrowserEnv()?function(){var e,t=/(msie|trident)/i.test(navigator.userAgent),r=document.createElement(\"a\");function o(e){var n=e;return t&&(r.setAttribute(\"href\",n),n=r.href),r.setAttribute(\"href\",n),{href:r.href,protocol:r.protocol?r.protocol.replace(/:$/,\"\"):\"\",host:r.host,search:r.search?r.search.replace(/^\\?/,\"\"):\"\",hash:r.hash?r.hash.replace(/^#/,\"\"):\"\",hostname:r.hostname,port:r.port,pathname:\"/\"===r.pathname.charAt(0)?r.pathname:\"/\"+r.pathname}}return e=o(window.location.href),function(t){var r=n.isString(t)?o(t):t;return r.protocol===e.protocol&&r.host===e.host}}():function(){return!0}},function(e,t,r){function n(){this.message=\"String contains an invalid character\"}n.prototype=new Error,n.prototype.code=5,n.prototype.name=\"InvalidCharacterError\",e.exports=function(e){for(var t,r,o=String(e),i=\"\",s=0,a=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\";o.charAt(0|s)||(a=\"=\",s%1);i+=a.charAt(63&t>>8-s%1*8)){if((r=o.charCodeAt(s+=.75))>255)throw new n;t=t<<8|r}return i}},function(e,t,r){var n=r(0);e.exports=n.isStandardBrowserEnv()?{write:function(e,t,r,o,i,s){var a=[];a.push(e+\"=\"+encodeURIComponent(t)),n.isNumber(r)&&a.push(\"expires=\"+new Date(r).toGMTString()),n.isString(o)&&a.push(\"path=\"+o),n.isString(i)&&a.push(\"domain=\"+i),!0===s&&a.push(\"secure\"),document.cookie=a.join(\"; \")},read:function(e){var t=document.cookie.match(new RegExp(\"(^|;\\\\s*)(\"+e+\")=([^;]*)\"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,\"\",Date.now()-864e5)}}:{write:function(){},read:function(){return null},remove:function(){}}},function(e,t,r){var n=r(0);function o(){this.handlers=[]}o.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},o.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},o.prototype.forEach=function(e){n.forEach(this.handlers,(function(t){null!==t&&e(t)}))},e.exports=o},function(e,t,r){var n=r(0),o=r(23),i=r(5),s=r(1),a=r(24),l=r(25);function d(e){e.cancelToken&&e.cancelToken.throwIfRequested()}e.exports=function(e){return d(e),e.baseURL&&!a(e.url)&&(e.url=l(e.baseURL,e.url)),e.headers=e.headers||{},e.data=o(e.data,e.headers,e.transformRequest),e.headers=n.merge(e.headers.common||{},e.headers[e.method]||{},e.headers||{}),n.forEach([\"delete\",\"get\",\"head\",\"post\",\"put\",\"patch\",\"common\"],(function(t){delete e.headers[t]})),(e.adapter||s.adapter)(e).then((function(t){return d(e),t.data=o(t.data,t.headers,e.transformResponse),t}),(function(t){return i(t)||(d(e),t&&t.response&&(t.response.data=o(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)}))}},function(e,t,r){var n=r(0);e.exports=function(e,t,r){return n.forEach(r,(function(r){e=r(e,t)})),e}},function(e,t,r){e.exports=function(e){return/^([a-z][a-z\\d\\+\\-\\.]*:)?\\/\\//i.test(e)}},function(e,t,r){e.exports=function(e,t){return t?e.replace(/\\/+$/,\"\")+\"/\"+t.replace(/^\\/+/,\"\"):e}},function(e,t,r){var n=r(6);function o(e){if(\"function\"!=typeof e)throw new TypeError(\"executor must be a function.\");var t;this.promise=new Promise((function(e){t=e}));var r=this;e((function(e){r.reason||(r.reason=new n(e),t(r.reason))}))}o.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},o.source=function(){var e;return{token:new o((function(t){e=t})),cancel:e}},e.exports=o},function(e,t,r){e.exports=function(e){return function(t){return e.apply(null,t)}}}])},700:function(e,t,r){var n=r(292),o=r(962);\"string\"==typeof(o=o.__esModule?o.default:o)&&(o=[[e.id,o,\"\"]]);n(o,{attributes:{class:\"formBuilder-injected-style\"},insert:\"head\",singleton:!1}),e.exports=o.locals||{}},292:function(e,t,r){var n,o=function(){var e={};return function(t){if(void 0===e[t]){var r=document.querySelector(t);if(window.HTMLIFrameElement&&r instanceof window.HTMLIFrameElement)try{r=r.contentDocument.head}catch(e){r=null}e[t]=r}return e[t]}}(),i=[];function s(e){for(var t=-1,r=0;re}};var i;const s=(e,t)=>{if(0===o.backendOrder.length)return!1;const r=e.toLowerCase();return t=t?t+\"\":\"\",r.startsWith(\"on\")||[\"form\",\"formaction\"].includes(r)||t.trim().toLowerCase().startsWith(\"javascript:\")};o.backends.fallback=function(t){const r=document.implementation.createHTMLDocument(\"\"),n=r.createElement(\"base\");n.href=document.location.href,r.head.appendChild(n);const o=[\"applet\",\"comment\",\"embed\",\"iframe\",\"link\",\"listing\",\"meta\",\"noscript\",\"object\",\"plaintext\",\"script\",\"style\",\"xmp\"],i=e.parseHTML(t,r,!1);e(i).find(\"*\").addBack().each(((t,r)=>{\"#text\"!==r.nodeName&&(r.tagName&&o.includes(r.tagName.toLowerCase())?r.parentElement?r.parentElement.removeChild(r):i.includes(r)&&i.splice(i.indexOf(r),1):r.attributes&&Array.from(r.attributes).forEach((t=>{s(t.name,t.value)&&e(r).removeAttr(t.name)})))}));const a=r.createElement(\"div\");return e(a).html(i),a.innerHTML};const a=e=>{const t=!!o.clobberingProtection.document&&document,r=!!o.clobberingProtection.form&&document.createElement(\"form\");return t&&e in t||r&&e in r?o.clobberingProtection.namespaceAttributes?\"user-content-\"+e:void 0:e},l={fallback:(e,t)=>{const r=o.backends.fallback,n=\"function\"==typeof r;return n&&(t=r(t)),e.innerHTML=t,n},dompurify:(e,t)=>{const r=o.backends.dompurify;return!(!1===r||!r.isSupported||(e.innerHTML=r.sanitize(t),0))},sanitizer:(e,t)=>{const r=o.backends.sanitizer;return!!r&&(e.setHTML(t,{sanitizer:r}),!0)}},d=[\"events\"],c=[\"tag\",\"content\"];function u(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function f(e){for(var t=1;t=0)continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||{}.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}window.fbLoaded={js:[],css:[]},window.fbEditors={quill:{},tinymce:{}};const h=function(e,t=!1){if(null==e||\"object\"!=typeof e)return e;const r=\"function\"==typeof window.structuredClone?window.structuredClone(e):Object.assign({},e),n=[null,void 0,\"\"];t&&n.push(!1);for(const e in r)n.includes(r[e])?delete r[e]:Array.isArray(r[e])&&(r[e].length||delete r[e]);return r},b=function(e){return![\"values\",\"enableOther\",\"other\",\"label\",\"subtype\"].includes(e)},g=(e,t)=>{let r;return e=y(e),t&&(Array.isArray(t)?r=T(t.join(\" \")):(\"boolean\"==typeof t&&(t=t.toString()),r=T(t.trim()))),{name:e,value:t=t?`=\"${r}\"`:\"\"}},y=e=>({className:\"class\"}[e]||v(e)),v=e=>(e=(e=(e=e.replace(/[^\\w\\s\\-\\[\\]]/gi,\"\")).replace(/\\[([^\\]]+)\\]/g,\"-$1\")).replace(/([A-Z])/g,(function(e){return\"-\"+e.toLowerCase()}))).replace(/\\s/g,\"-\").replace(/^-+/g,\"\"),w=e=>e.replace(/-([a-z])/g,((e,t)=>t.toUpperCase())),x=function(){let e,t=0;return function(r){const n=Date.now();return n===e?++t:(t=0,e=n),(r.type||v(r.label))+\"-\"+n+\"-\"+t}}(),O=e=>void 0===e?e:[[\"array\",e=>Array.isArray(e)],[\"node\",e=>e instanceof window.Node||e instanceof window.HTMLElement],[\"component\",()=>e?.dom],[typeof e,()=>!0]].find((t=>t[1](e)))[0],j=function(t,r=\"\",n={}){let i=O(r);const{events:u}=n,f=m(n,d),p=document.createElement(t),h={string:t=>{((t,r,n=!1)=>{if(!n){const n=document.createElement(t.tagName);return void 0!==o.backendOrder.find((e=>l[e](n,r)))?((t=>{e(t).find(\"*\").each(((e,t)=>{[\"embed\",\"form\",\"iframe\",\"image\",\"img\",\"object\"].includes(t.tagName.toLowerCase())&&t.removeAttribute(\"name\"),[\"id\",\"name\"].forEach((e=>{if(t.hasAttribute(e)){const r=a(t.getAttribute(e));void 0===r?t.removeAttribute(e):t.setAttribute(e,r)}}))}))})(n),t.innerHTML=n.innerHTML,t):(t.innerHTML=r,t)}t.textContent=r})(p,p.innerHTML+t)},object:e=>{const{tag:t,content:r}=e,n=m(e,c);return p.appendChild(j(t,r,n))},node:e=>p.appendChild(e),array:e=>{for(let t=0;t{e=e(),i=O(e),h[i](e)},undefined:()=>{}};for(const e in f)if(f.hasOwnProperty(e)){const t=y(e);let r=Array.isArray(f[e])?P(f[e].join(\" \").split(\" \")).join(\" \"):f[e];if(s(t,r))continue;if(\"boolean\"==typeof r){if(!0===r){const e=\"contenteditable\"===t||t;p.setAttribute(t,e)}}else\"id\"!==t&&\"name\"!==t||(r=a(r)),void 0!==r&&p.setAttribute(t,r)}return r&&h[i](r),((e,t)=>{if(t)for(const r in t)t.hasOwnProperty(r)&&e.addEventListener(r,(e=>t[r](e)))})(p,u),p},k=e=>{const t=e.attributes,r={};return L(t,(e=>{let n=t[e].value||\"\";n.match(/false|true/g)?n=\"true\"===n:n.match(/undefined/g)&&(n=void 0),n&&(r[w(t[e].name)]=n)})),r},C=e=>{const t=[];for(let r=0;r{const t=[];if(e.length){const r=e[0].getElementsByTagName(\"value\");for(let e=0;e{const t=(new window.DOMParser).parseFromString(e,\"text/xml\"),r=[];if(t){const e=t.getElementsByTagName(\"field\");for(let t=0;t{const t=document.createElement(\"textarea\");return t.innerHTML=e,t.textContent},T=e=>{const t={'\"':\""\",\"&\":\"&\",\"<\":\"<\",\">\":\">\"};return\"string\"==typeof e?e.replace(/[\"&<>]/g,(e=>t[e]||e)):e},L=function(e,t,r){for(let n=0;ne.filter(((e,t,r)=>r.indexOf(e)===t)),R=(e,t=\"\")=>{const r=jQuery;let n=[];return Array.isArray(e)||(e=[e]),N(e)||(n=jQuery.map(e,(e=>{const r={dataType:\"script\",cache:!0,url:(t||\"\")+e};return jQuery.ajax(r).done((()=>window.fbLoaded.js.push(e)))}))),n.push(jQuery.Deferred((e=>r(e.resolve)))),jQuery.when(...n)},N=(e,t=\"js\")=>{const r=window.fbLoaded[t];return Array.isArray(e)?e.every((e=>r.includes(e))):r.includes(e)},D=(t,r=\"\")=>{Array.isArray(t)||(t=[t]),t.forEach((t=>{let n=\"href\",o=t,i=\"\";if(\"object\"==typeof t&&(n=t.type||(t.style?\"inline\":\"href\"),i=t.id,o=i||t.href||t.style,t=\"inline\"===n?t.style:t.href),!N(o,\"css\")){if(\"href\"===n){const e=document.createElement(\"link\");e.type=\"text/css\",e.rel=\"stylesheet\",e.href=(r||\"\")+t,document.head.appendChild(e)}else e(``).attr(\"id\",i).appendTo(e(document.head));window.fbLoaded.css.push(o)}}))},q=(e,t)=>{const r=Object.assign({},e,t);for(const n in t)r.hasOwnProperty(n)&&(Array.isArray(t[n])?r[n]=Array.isArray(e[n])?P(e[n].concat(t[n])):t[n]:\"object\"==typeof t[n]?r[n]=q(e[n],t[n]):r[n]=t[n]);return r},M=/^col-(xs|sm|md|lg)-([^\\s]+)/,F={addEventListeners:(e,t,r)=>t.split(\" \").forEach((t=>e.addEventListener(t,r,!1))),attrString:e=>Object.entries(e).map((([e,t])=>b(e)&&Object.values(g(e,t)).join(\"\"))).filter(Boolean).join(\" \"),camelCase:w,capitalize:e=>e.replace(/\\b\\w/g,(function(e){return e.toUpperCase()})),closest:(e,t)=>{const r=t.replace(\".\",\"\");for(;(e=e.parentElement)&&!e.classList.contains(r););return e},getContentType:O,escapeAttr:T,escapeAttrs:e=>{for(const t in e)e.hasOwnProperty(t)&&(e[t]=T(e[t]));return e},escapeHtml:e=>{const t=document.createElement(\"textarea\");return t.textContent=e,t.innerHTML},forceNumber:e=>e.replace(/[^0-9]/g,\"\"),forEach:L,getScripts:R,getStyles:D,hyphenCase:v,isCached:N,markup:j,merge:q,mobileClass:()=>{let e=\"\";var t;return t=navigator.userAgent||navigator.vendor||window.opera,/(android|bb\\d+|meego).+mobile|avantgo|bada\\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(t)&&(e=\"formbuilder-mobile\"),e},nameAttr:x,parsedHtml:A,parseXML:E,removeFromArray:(e,t)=>{const r=t.indexOf(e);r>-1&&t.splice(r,1)},safeAttr:g,safeAttrName:y,safename:e=>e.replace(/\\s/g,\"-\").replace(/[^a-zA-Z0-9[\\]_-]/g,\"\"),subtract:(e,t)=>t.filter((function(e){return!~this.indexOf(e)}),e),trimObj:h,unique:P,validAttr:b,titleCase:function(e){const t=[\"a\",\"an\",\"and\",\"as\",\"at\",\"but\",\"by\",\"for\",\"for\",\"from\",\"in\",\"into\",\"near\",\"nor\",\"of\",\"on\",\"onto\",\"or\",\"the\",\"to\",\"with\"].map((e=>`\\\\s${e}\\\\s`)),r=new RegExp(`(?!${t.join(\"|\")})\\\\w\\\\S*`,\"g\");return`${e}`.replace(r,(e=>e.charAt(0).toUpperCase()+e.slice(1).replace(/[A-Z]/g,(e=>` ${e}`))))},firstNumberOrUndefined:function(...e){return e.find((e=>\"number\"==typeof e))},splitObject:(e,t)=>{const r=e=>(t,r)=>(t[r]=e[r],t);return[Object.keys(e).filter((e=>t.includes(e))).reduce(r(e),{}),Object.keys(e).filter((e=>!t.includes(e))).reduce(r(e),{})]}};e.fn.swapWith=function(t){const r=this,n=e(t),o=e(\"
\");return r.before(o),n.before(r),o.before(n).remove(),r};var z=F;const B=(e,t,r=!0)=>{const n=[];let o=[\"none\",\"block\"];r&&(o=o.reverse());for(let r=e.length-1;r>=0;r--)-1!==e[r].textContent.toLowerCase().indexOf(t.toLowerCase())?(e[r].style.display=o[0],n.push(e[r])):e[r].style.display=o[1];return n};new RegExp(`(${[\"select\",\"checkbox-group\",\"checkbox\",\"radio-group\",\"autocomplete\"].join(\"|\")})`);const H=[\"label\",\"type\"];class ${constructor(e,t){this.rawConfig=jQuery.extend({},e),e=jQuery.extend({},e),this.preview=t,delete e.isPreview,this.preview&&delete e.required;const r=[\"label\",\"description\",\"subtype\",\"required\",\"disabled\"];for(const t of r)this[t]=e[t],delete e[t];e.id||(e.name?e.id=e.name:e.id=\"control-\"+Math.floor(1e7*Math.random()+1)),this.id=e.id,this.type=e.type,this.description&&(e.title=this.description),$.controlConfig||($.controlConfig={});const n=this.subtype?this.type+\".\"+this.subtype:this.type;this.classConfig=jQuery.extend({},$.controlConfig[this.type]||{},$.controlConfig[n]||{}),this.subtype&&(e.type=this.subtype),this.required&&(e.required=\"required\",e[\"aria-required\"]=\"true\"),this.disabled&&(e.disabled=\"disabled\"),this.config=e,this.configure()}static get definition(){return{}}static register(e,t,r){const n=r?r+\".\":\"\";$.classRegister||($.classRegister={}),Array.isArray(e)||(e=[e]);for(const r of e)-1===r.indexOf(\".\")?$.classRegister[n+r]=t:$.error(`Ignoring type ${r}. Cannot use the character '.' in a type name.`)}static getRegistered(e=!1){const t=Object.keys($.classRegister);return t.length?t.filter((t=>e?t.indexOf(e+\".\")>-1:-1===t.indexOf(\".\"))):t}static getRegisteredSubtypes(){const e={};for(const t in $.classRegister)if($.classRegister.hasOwnProperty(t)){const[r,n]=t.split(\".\");if(!n)continue;e[r]||(e[r]=[]),e[r].push(n)}return e}static getClass(e,t){const r=t?e+\".\"+t:e;return $.classRegister[r]||$.classRegister[e]||$.error(\"Invalid control type. (Type: \"+e+\", Subtype: \"+t+\"). Please ensure you have registered it, and imported it correctly.\")}static loadCustom(e){let t=[];if(e&&(t=t.concat(e)),window.fbControls&&(t=t.concat(window.fbControls)),!this.fbControlsLoaded){for(const e of t)e($,$.classRegister);this.fbControlsLoaded=!0}}static mi18n(e,t){const n=this.definition;let o=n.i18n||{};o=o[r().locale]||o.default||o;const i=this.camelCase(e),s=\"object\"==typeof o?o[i]||o[e]:o;if(s)return s;let a=n.mi18n;return\"object\"==typeof a&&(a=a[i]||a[e]),a||(a=i),r().get(a,t)}static active(e){return!Array.isArray(this.definition.inactive)||-1===this.definition.inactive.indexOf(e)}static label(e){return this.mi18n(e)}static icon(e){const t=this.definition;return t&&\"object\"==typeof t.icon?t.icon[e]:t.icon}configure(){}build(){const e=this.config,{label:t,type:r}=e,n=function(e,t){if(null==e)return{};var r,n,o=function(e,t){if(null==e)return{};var r={};for(var n in e)if({}.hasOwnProperty.call(e,n)){if(t.indexOf(n)>=0)continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||{}.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}(e,H);return this.markup(r,A(t),n)}on(e){const t={prerender:e=>e,render:e=>{const t=()=>{this.onRender&&this.onRender(e)};this.css&&D(this.css),this.js&&!N(this.js)?R(this.js).done(t):t()}};return e?t[e]:t}static error(e){throw new Error(e)}markup(e,t=\"\",r={}){return this.element=j(e,t,r),this.element}parsedHtml(e){return A(e)}static camelCase(e){return w(e)}}const U=(e,t)=>{let r=e.id?`formbuilder-${e.type} form-group field-${e.id}`:\"\";if(e.className){const n=(e=>\"string\"==typeof e?e.split(\" \").filter((e=>M.test(e)||e.startsWith(\"row-\"))):[])(e.className);n&&n.length>0&&(r+=` ${n.join(\" \")}`,Array.isArray(t)||(t=[t]),t.forEach((e=>{e.classList&&e.classList.remove(...n),e.querySelectorAll(\"[class*=row-],[class*=col-]\").forEach((e=>{e.classList&&e.classList.remove(...n)}))})))}return r};class I{constructor(e,t=!1,r=!1,n={}){this.preview=null!=t&&t,this.disableHTMLLabels=null!=r&&r,this.controlConfig=null!=n?n:{},this.templates={label:null,help:null,default:(e,t,r,n)=>(r&&t.appendChild(r),this.markup(\"div\",[t,e],{className:U(n,e)})),noLabel:(e,t,r,n)=>this.markup(\"div\",e,{className:U(n,e)}),hidden:e=>e},e&&(this.templates=jQuery.extend(this.templates,e)),this.configure()}configure(){}build(e,t,r){this.preview&&(t.name?t.name=t.name+\"-preview\":t.name=z.nameAttr(t)+\"-preview\"),t.id=t.name,this.data=jQuery.extend({},t),$.controlConfig=this.controlConfig;const n=new e(t,this.preview);let o=n.build();if(\"object\"==typeof o&&o.field||(o={field:o}),\"string\"==typeof o.field){const e=this.markup(\"div\",o.field,{});1===e.childElementCount?o.field=e.children.item(0):o.field=Array.from(e.children)}const i=this.label(),s=this.help();let a;a=r&&this.isTemplate(r)?r:this.isTemplate(o.layout)?o.layout:\"default\";const l=this.processTemplate(a,o.field,i,s);return n.on(\"prerender\")(l),l.addEventListener(\"fieldRendered\",n.on(\"render\")),l}label(){const e=this.data.label||\"\",t=[this.disableHTMLLabels?document.createTextNode(e):z.parsedHtml(e)];return this.data.required&&t.push(this.markup(\"span\",\"*\",{className:\"formbuilder-required\"})),this.isTemplate(\"label\")?this.processTemplate(\"label\",t):this.markup(\"label\",t,{for:this.data.id,className:`formbuilder-${this.data.type}-label`})}help(){return this.data.description?this.isTemplate(\"help\")?this.processTemplate(\"help\",this.data.description):this.markup(\"span\",\"?\",{className:\"tooltip-element\",tooltip:this.data.description}):null}isTemplate(e){return\"function\"==typeof this.templates[e]}processTemplate(e,...t){let r=this.templates[e](...t,this.data);return r.jquery&&(r=r[0]),r}markup(e,t=\"\",r={}){return z.markup(e,t,r)}}const _=[\"values\",\"type\"];$.register(\"autocomplete\",class extends ${static get definition(){return{mi18n:{requireValidOption:\"requireValidOption\"}}}build(){const e=this.config,{values:t,type:r}=e,n=function(e,t){if(null==e)return{};var r,n,o=function(e,t){if(null==e)return{};var r={};for(var n in e)if({}.hasOwnProperty.call(e,n)){if(t.indexOf(n)>=0)continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||{}.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}(e,_),o=e=>{const t=e.target.nextSibling.nextSibling,r=e.target.nextSibling,n=this.getActiveOption(t);let o=new Map([[38,()=>{const e=this.getPreviousOption(n);e&&this.selectOption(t,e)}],[40,()=>{const e=this.getNextOption(n);e&&this.selectOption(t,e)}],[13,()=>{n?(e.target.value=n.innerHTML,r.value=n.getAttribute(\"value\"),\"none\"===t.style.display?this.showList(t,n):this.hideList(t)):this.config.requireValidOption&&(this.isOptionValid(t,e.target.value)||(e.target.value=\"\",e.target.nextSibling.value=\"\")),e.preventDefault()}],[27,()=>{this.hideList(t)}]]).get(e.keyCode);return o||(o=()=>!1),o()},i={focus:e=>{const t=e.target.nextSibling.nextSibling,r=B(t.querySelectorAll(\"li\"),e.target.value);if(e.target.addEventListener(\"keydown\",o),e.target.value.length>0){const e=r.length>0?r[r.length-1]:null;this.showList(t,e)}},blur:e=>{e.target.removeEventListener(\"keydown\",o);const t=setTimeout((()=>{e.target.nextSibling.nextSibling.style.display=\"none\",clearTimeout(t)}),200);if(this.config.requireValidOption){const t=e.target.nextSibling.nextSibling;this.isOptionValid(t,e.target.value)||(e.target.value=\"\",e.target.nextSibling.value=\"\")}},input:e=>{const t=e.target.nextSibling.nextSibling;e.target.nextSibling.value=e.target.value;const r=B(t.querySelectorAll(\"li\"),e.target.value);if(0==r.length)this.hideList(t);else{let e=this.getActiveOption(t);e||(e=r[r.length-1]),this.showList(t,e)}}},s=Object.assign({},n,{id:`${n.id}-input`,autocomplete:\"off\",events:i}),a=Object.assign({},n,{type:\"hidden\"});delete s.name;const l=[this.markup(\"input\",null,s),this.markup(\"input\",null,a)],d=t.map((e=>{const t=e.label,r={events:{click:t=>{const r=t.target.parentElement,n=r.previousSibling.previousSibling;n.value=e.label,n.nextSibling.value=e.value,this.hideList(r)}},value:e.value};return this.markup(\"li\",t,r)}));return l.push(this.markup(\"ul\",d,{id:`${n.id}-list`,className:`formbuilder-${r}-list`})),l}hideList(e){this.selectOption(e,null),e.style.display=\"none\"}showList(e,t){this.selectOption(e,t),e.style.display=\"block\",e.style.width=e.parentElement.offsetWidth+\"px\"}getActiveOption(e){const t=e.getElementsByClassName(\"active-option\")[0];return t&&\"none\"!==t.style.display?t:null}getPreviousOption(e){let t=e;do{t=t?t.previousSibling:null}while(null!=t&&\"none\"===t.style.display);return t}getNextOption(e){let t=e;do{t=t?t.nextSibling:null}while(null!=t&&\"none\"===t.style.display);return t}selectOption(e,t){const r=e.querySelectorAll(\"li\");for(let e=0;e=0)continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||{}.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}(e,X);let n=t;const o={paragraph:\"p\",header:this.subtype};return o[t]&&(n=o[t]),{field:this.markup(n,z.parsedHtml(this.label),r),layout:\"noLabel\"}}}$.register([\"paragraph\",\"header\"],W),$.register([\"p\",\"address\",\"blockquote\",\"canvas\",\"output\"],W,\"paragraph\"),$.register([\"h1\",\"h2\",\"h3\",\"h4\",\"h5\",\"h6\"],W,\"header\");const J=[\"values\",\"value\",\"placeholder\",\"type\",\"inline\",\"other\",\"toggle\"],G=[\"label\"];function Z(e,t){if(null==e)return{};var r,n,o=function(e,t){if(null==e)return{};var r={};for(var n in e)if({}.hasOwnProperty.call(e,n)){if(t.indexOf(n)>=0)continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||{}.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}$.register([\"select\",\"checkbox-group\",\"radio-group\",\"checkbox\"],class extends ${static get definition(){return{inactive:[\"checkbox\"],mi18n:{minSelectionRequired:\"minSelectionRequired\"}}}build(){const e=[],t=this.config,{values:r,value:n,placeholder:o,type:i,inline:s,other:a,toggle:l}=t,d=Z(t,J),c=i.replace(\"-group\",\"\"),u=\"select\"===i;if((d.multiple||\"checkbox-group\"===i)&&(d.name=d.name+\"[]\"),(\"checkbox-group\"===i||\"radio-group\"===i)&&d.required){const e=this,t=this.onRender.bind(this);this.onRender=function(){t(),e.groupRequired()}}if(delete d.title,r){o&&u&&e.push(this.markup(\"option\",o,{disabled:!0,selected:!0,value:\"\"}));for(let t=0;t{const t=e.target;t.parentElement.previousElementSibling.value=t.value}},id:`${t.id}-value`,className:\"other-val\"},i=this.markup(\"input\",null,n),a=[document.createTextNode($.mi18n(\"other\")),this.markup(\"input\",null,o)],l=this.markup(\"label\",a,{for:n.id}),u=this.markup(\"div\",[i,l],{className:r});e.push(u)}}return this.dom=\"select\"==i?this.markup(c,e,h(d,!0)):this.markup(\"div\",e,{className:i}),this.dom}groupRequired(){const e=this.element.getElementsByTagName(\"input\"),t=this.element.querySelectorAll(\"input:not([type=text])\"),r=this.element.querySelector(\".other-option\"),n=this.element.querySelector(\".other-val\"),o=()=>{const e=[].some.call(t,(e=>e.checked));((e,t,r,n)=>{[].forEach.call(e,(e=>{n?e.removeAttribute(\"required\"):e.setAttribute(\"required\",\"required\"),((e,t)=>{const r=$.mi18n(\"minSelectionRequired\",1);t?e.setCustomValidity(\"\"):e.setCustomValidity(r)})(e,n)})),t&&(t.checked?r.setAttribute(\"required\",\"required\"):r.removeAttribute(\"required\"))})(t,r,n,e)};for(let t=e.length-1;t>=0;t--)e[t].addEventListener(\"change\",o);o()}onRender(){if(this.config.userData){const t=this.config.userData.slice();\"select\"===this.config.type?e(this.dom).val(t).prop(\"selected\",!0):this.config.type.endsWith(\"-group\")&&(\"checkbox-group\"===this.config.type&&this.dom.querySelectorAll(\"input[type=checkbox]\").forEach((e=>{e.removeAttribute(\"checked\")})),this.dom.querySelectorAll(\"input\").forEach((e=>{if(!e.classList.contains(\"other-val\")){for(let r=0;r0){const r=this.dom.querySelector(`#${e.id}-value`);e.setAttribute(\"checked\",\"checked\"),r.value=e.value=t[0],r.style.display=\"inline-block\"}}})))}}});class K extends ${static get definition(){return{mi18n:{date:\"dateField\",file:\"fileUpload\"}}}build(){let{name:e}=this.config;e=this.config.multiple?`${e}[]`:e;const t=Object.assign({},this.config,{name:e});return this.dom=this.markup(\"input\",null,t),this.dom}onRender(){this.config.userData&&e(this.dom).val(this.config.userData[0])}}$.register([\"text\",\"file\",\"date\",\"number\"],K),$.register([\"text\",\"password\",\"email\",\"color\",\"tel\"],K,\"text\"),$.register([\"date\",\"time\",\"datetime-local\"],K,\"date\"),$.register([\"number\",\"range\"],K,\"number\");const Y=[\"value\"];class ee extends ${static get definition(){return{mi18n:{textarea:\"textArea\"}}}build(){const e=this.config,{value:t=\"\"}=e,r=function(e,t){if(null==e)return{};var r,n,o=function(e,t){if(null==e)return{};var r={};for(var n in e)if({}.hasOwnProperty.call(e,n)){if(t.indexOf(n)>=0)continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||{}.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}(e,Y);return delete r.type,this.field=this.markup(\"textarea\",this.parsedHtml(t),r),this.field}onRender(){this.config.userData&&e(this.field).val(this.config.userData[0])}on(t){return\"prerender\"==t&&this.preview?t=>{this.field&&(t=this.field),e(t).on(\"mousedown\",(e=>{e.stopPropagation()}))}:super.on(t)}}$.register(\"textarea\",ee),$.register(\"textarea\",ee,\"textarea\");const te=[\"value\"];ee.register(\"tinymce\",class extends ee{configure(){if(this.js=[],window.tinymce||this.js.push(\"https://cdnjs.cloudflare.com/ajax/libs/tinymce/4.9.11/tinymce.min.js\"),this.classConfig.js){let e=this.classConfig.js;Array.isArray(e)||(e=new Array(e)),this.js=this.js.concat(e),delete this.classConfig.js}this.classConfig.css&&(this.css=this.classConfig.css),this.editorOptions={height:250,paste_data_images:!0,plugins:[\"advlist\",\"autolink\",\"lists\",\"link\",\"image\",\"charmap\",\"print\",\"preview\",\"anchor\",\"searchreplace\",\"visualblocks\",\"code\",\"fullscreen\",\"insertdatetime\",\"media\",\"table\",\"contextmenu\",\"paste\",\"code\"],toolbar:\"undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | table\"}}build(){const e=this.config,{value:t=\"\"}=e,r=function(e,t){if(null==e)return{};var r,n,o=function(e,t){if(null==e)return{};var r={};for(var n in e)if({}.hasOwnProperty.call(e,n)){if(t.indexOf(n)>=0)continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||{}.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}(e,te);return delete r.type,this.field=this.markup(\"textarea\",this.parsedHtml(t),r),r.disabled&&(this.editorOptions.readonly=!0),this.field}onRender(){const e=window.tinymce.get(this.id);e&&window.tinymce.remove(e);const t=jQuery.extend(this.editorOptions,this.classConfig);t.target=this.field;const r=[];Number(window.tinymce.majorVersion)>=5&&r.push(\"contextmenu\"),Number(window.tinymce.majorVersion)>=6&&r.push(\"paste\",\"print\"),t.plugins=t.plugins.filter((e=>-1===r.indexOf(e)));const n=this.config.userData?this.parsedHtml(this.config.userData[0]):void 0,o=window.lastFormBuilderCopiedTinyMCE?this.parsedHtml(window.lastFormBuilderCopiedTinyMCE):void 0;window.lastFormBuilderCopiedTinyMCE=null;const i=function(e){o?e[0].setContent(o):n&&e[0].setContent(n)};setTimeout((()=>{window.tinymce.init(t).then(i)}),0)}},\"textarea\");const re=[\"value\"];function ne(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function oe(e){for(var t=1;t=0)continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||{}.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}(e,re);return delete r.type,this.field=this.markup(\"div\",null,r),this.field.classList.contains(\"form-control\")&&this.field.classList.remove(\"form-control\"),this.field}onRender(e){const t=this.config.value||\"\",r=window.Quill.import(\"delta\");window.fbEditors.quill[this.id]={};const n=window.fbEditors.quill[this.id];return n.instance=new window.Quill(this.field,this.editorConfig),n.data=new r,t&&n.instance.setContents(window.JSON.parse(this.parsedHtml(t))),n.instance.on(\"text-change\",(function(e){n.data=n.data.compose(e)})),e}},\"textarea\"),r().addLanguage(\"en-US\",{NATIVE_NAME:\"English (US)\",ENGLISH_NAME:\"English\",addOption:\"Add Option +\",allFieldsRemoved:\"All fields were removed.\",allowMultipleFiles:\"Allow users to upload multiple files\",allowSelect:\"Allow selection\",autocomplete:\"Autocomplete\",button:\"Button\",cannotBeEmpty:\"This field cannot be empty\",checkboxGroup:\"Checkbox Group\",checkbox:\"Checkbox\",checkboxes:\"Checkboxes\",className:\"Class\",clearAllMessage:\"Are you sure you want to clear all fields?\",clear:\"Clear\",close:\"Close\",content:\"Content\",copy:\"Copy To Clipboard\",copyButton:\"+\",copyButtonTooltip:\"Copy\",dateField:\"Date Field\",description:\"Help Text\",descriptionField:\"Description\",devMode:\"Developer Mode\",editNames:\"Edit Names\",editorTitle:\"Form Elements\",editXML:\"Edit XML\",enableOther:\"Enable "Other"\",enableOtherMsg:\"Let users enter an unlisted option\",fieldDeleteWarning:\"false\",fieldVars:\"Field Variables\",fieldNonEditable:\"This field cannot be edited.\",fieldRemoveWarning:\"Are you sure you want to remove this field?\",fileUpload:\"File Upload\",formUpdated:\"Form Updated\",getStarted:\"Drag a field from the right to this area\",header:\"Header\",hide:\"Edit\",hidden:\"Hidden Input\",inline:\"Inline\",inlineDesc:\"Display {type} inline\",label:\"Label\",labelEmpty:\"Field Label cannot be empty\",limitRole:\"Limit access to one or more of the following roles:\",mandatory:\"Mandatory\",maxlength:\"Max Length\",minOptionMessage:\"This field requires a minimum of 2 options\",minSelectionRequired:\"Minimum {min} selections required\",multipleFiles:\"Multiple Files\",name:\"Name\",no:\"No\",noFieldsToClear:\"There are no fields to clear\",number:\"Number\",off:\"Off\",on:\"On\",option:\"Option\",optionCount:\"Option {count}\",options:\"Options\",optional:\"optional\",optionLabelPlaceholder:\"Label\",optionValuePlaceholder:\"Value\",optionEmpty:\"Option value required\",other:\"Other\",paragraph:\"Paragraph\",placeholder:\"Placeholder\",\"placeholders.value\":\"Value\",\"placeholders.label\":\"Label\",\"placeholders.email\":\"Enter your email\",\"placeholders.className\":\"space separated classes\",\"placeholders.password\":\"Enter your password\",preview:\"Preview\",radioGroup:\"Radio Group\",radio:\"Radio\",removeMessage:\"Remove Element\",removeOption:\"Remove Option\",remove:\"×\",required:\"Required\",reset:\"Reset\",requireValidOption:\"Only accept a pre-defined Option\",richText:\"Rich Text Editor\",roles:\"Access\",rows:\"Rows\",save:\"Save\",selectOptions:\"Options\",select:\"Select\",selectColor:\"Select Color\",selectionsMessage:\"Allow Multiple Selections\",size:\"Size\",sizes:\"Sizes\",\"size.xs\":\"Extra Small\",\"size.sm\":\"Small\",\"size.m\":\"Default\",\"size.lg\":\"Large\",step:\"Step\",style:\"Style\",styles:\"Styles\",\"styles.btn\":\"Button Styles\",\"styles.btn.default\":\"Default\",\"styles.btn.danger\":\"Danger\",\"styles.btn.info\":\"Info\",\"styles.btn.primary\":\"Primary\",\"styles.btn.success\":\"Success\",\"styles.btn.warning\":\"Warning\",submit:\"Submit\",subtype:\"Type\",text:\"Text Field\",textArea:\"Text Area\",toggle:\"Toggle\",warning:\"Warning!\",value:\"Value\",viewJSON:\"[{…}]\",viewXML:\"</>\",yes:\"Yes\"});const se={location:\"assets/lang/\"};n(700);class ae{constructor(e={},t=[]){this.customRegister={},this.templateControlRegister={},this.def={icon:{},i18n:{}},this.register(e,t)}register(e={},t=[]){t.forEach((t=>{if(t.template){const r=t.type||t.attrs?.type;e[r]=t.template}}));const n=r().locale;this.def.i18n[n]||(this.def.i18n[n]={});const o=this;Object.keys(e).forEach((t=>{const r=function(r,n){this.customControl=new Q(r,n,e[t]),this.build=function(){return this.customControl.build()},this.on=function(e){return this.customControl.on(e)}};r.definition={},r.label=e=>o.label(e),r.icon=e=>o.icon(e),this.templateControlRegister[t]=r}));for(const o of t){let t=o.type;if(o.attrs=o.attrs||{},!t){if(!o.attrs.type){$.error(\"Ignoring invalid custom field definition. Please specify a type property.\");continue}t=o.attrs.type}let i=o.subtype||t;if(e[t]){const e=this.templateControlRegister[t];e.definition=o,this.customRegister[i]=jQuery.extend(o,{type:t,class:e})}else try{const e=$.getClass(t,o.subtype);i=o.datatype?o.datatype:`${t}-${Math.floor(9e3*Math.random()+1e3)}`,this.customRegister[i]=jQuery.extend(o,{type:t,class:e})}catch(e){$.error(\"Error while registering custom field: \"+t+(o.subtype?\":\"+o.subtype:\"\")+\". Unable to find any existing defined control or template for rendering.\")}this.def.i18n[n][i]=Array.isArray(o.label)?r().get(...o.label)||o.label[0]:o.label,this.def.icon[i]=o.icon}}label(e){const t=this.def;let n=t.i18n||{};n=n[r().locale]||n.default||n;const o=$.camelCase(e),i=\"object\"==typeof n?n[o]||n[e]:n;if(i)return i;{let n=t.mi18n;return\"object\"==typeof n&&(n=n[o]||n[e]),n||(n=o),r().get(n)}}get definition(){return{}}icon(e){const t=this.def;return t&&\"object\"==typeof t.icon?t.icon[e]:t.icon}getRegistered(e=!1){var t;return e?null!==(t=this.templateControlRegister[e])&&void 0!==t?t:void 0:Object.keys(this.customRegister)}getClass(e){var t;return null!==(t=this.templateControlRegister[e])&&void 0!==t?t:void 0}lookup(e){return this.customRegister[e]}}class le{constructor(e={}){const t={layout:I,layoutTemplates:{},controls:{},controlConfig:{},container:!1,dataType:\"json\",disableHTMLLabels:!1,formData:[],i18n:Object.assign({},se),messages:{formRendered:\"Form Rendered\",noFormData:\"No form data.\",other:\"Other\",selectColor:\"Select Color\",invalidControl:\"Invalid control\"},onRender:()=>{},render:!0,sanitizerOptions:{clobberingProtection:{document:!0,form:!1,namespaceAttributes:!0},backendOrder:[\"dompurify\",\"sanitizer\",\"fallback\"]},templates:{},notify:{error:e=>{console.log(e)},success:e=>{console.log(e)},warning:e=>{console.warn(e)}}};this.options=jQuery.extend(!0,t,e),this.instanceContainers=[],(e=>{if(\"object\"!=typeof e)throw\"Invalid value given to setSanitizerConfig, expected config object\";if(e.hasOwnProperty(\"clobberingProtection\")&&[\"document\",\"form\",\"namespaceAttributes\"].forEach((t=>{e.clobberingProtection.hasOwnProperty(t)&&\"boolean\"==typeof e.clobberingProtection[t]&&(o.clobberingProtection[t]=e.clobberingProtection[t])})),e.hasOwnProperty(\"backends\")){if(\"object\"!=typeof e.backends)throw\"backends config expected to be an Object\";Object.keys(e.backends).forEach((t=>o.backends[t]=e.backends[t]))}if(e.hasOwnProperty(\"backendOrder\")){if(o.backendOrder=[],!Array.isArray(e.backendOrder))throw\"backendOrder config expected to be an Array of backend keys as strings\";e.backendOrder.forEach((e=>{if(!o.backends.hasOwnProperty(e))throw\"unknown sanitizer backend \"+e;o.backendOrder.push(e)}))}})(this.options.sanitizerOptions),r().current||r().init(this.options.i18n),this.options.formData?this.options.formData=this.parseFormData(this.options.formData):this.options.formData=[],$.controlConfig=e.controlConfig||{},$.loadCustom(e.controls),this.templatedControls=new ae(this.options.templates),\"function\"!=typeof Element.prototype.appendFormFields&&(Element.prototype.appendFormFields=function(e){Array.isArray(e)||(e=[e]);const t=z.markup(\"div\",null,{className:\"rendered-form formbuilder-embedded-bootstrap\"});this.appendChild(t),e.forEach((e=>{const[r]=e.className.match(/row-([^\\s]+)/)||[];if(r){const n=this.id?`${this.id}-row-${r}`:`row-${r}`;let o=document.getElementById(n);o||(o=z.markup(\"div\",null,{id:n,className:\"row\"}),t.appendChild(o)),o.appendChild(e)}else t.appendChild(e);e.dispatchEvent(new Event(\"fieldRendered\",{bubbles:!0,cancelable:!1}))}))}),\"function\"!=typeof Element.prototype.emptyContainer&&(Element.prototype.emptyContainer=function(){const e=this;for(;e.lastChild;)e.removeChild(e.lastChild)})}sanitizeField(e,t){let r=Object.assign({},e);return t&&(r.id=e.id&&`${e.id}-${t}`,r.name=e.name&&`${e.name}-${t}`),r.className=Array.isArray(e.className)?z.unique(e.className.join(\" \").split(\" \")).join(\" \"):e.className||e.class||null,delete r.class,e.values&&(r.values=e.values.map((e=>z.trimObj(e)))),r=z.trimObj(r),Array.isArray(e.userData)&&0===e.userData.length&&(r.userData=[]),r}getElement(e){return(e=this.options.container||e)instanceof jQuery?e=e[0]:\"string\"==typeof e&&(e=document.querySelector(e)),e}render(e=null,t=0){const r=this,n=this.options;e=this.getElement(e);const o=[],i=new n.layout(n.layoutTemplates,!1,n.disableHTMLLabels,n.controlConfig);if(n.formData.length)for(let e=0;ee.map((e=>e.innerHTML)).join(\"\");r.markup=e(o)}if(!0===n.disableInjectedStyle){const e=document.getElementsByClassName(\"formBuilder-injected-style\");L(e,(t=>(e=>{e.parentNode&&e.parentNode.removeChild(e)})(e[t])))}else\"bootstrap\"===n.disableInjectedStyle&&n.render&&e&&e.getElementsByClassName(\"formbuilder-embedded-bootstrap\").item(0)?.classList.remove(\"formbuilder-embedded-bootstrap\");return r}renderControl(e=null){const t=this.options,r=t.formData;if(!r||Array.isArray(r))throw new Error(\"To render a single element, please specify a single object of formData for the field in question\");const n=this.sanitizeField(r),o=new t.layout,i=this.templatedControls.getClass(r.type)||$.getClass(r.type,r.subtype),s=t.forceTemplate||\"hidden\",a=o.build(i,n,s);return e.appendFormFields(a),t.notify.success(t.messages.formRendered),this}get userData(){const t=this.options.formData.slice();return t.filter((e=>\"tinymce\"===e.subtype)).forEach((e=>window.tinymce.get(e.name).save())),this.instanceContainers.forEach((r=>{const n=e(\"select, input, textarea\",r).serializeArray().reduce(((e,{name:t,value:r})=>(t=t.replace(/\\[\\w*]/,\"\"),e[t]??=[],e[t].push(r),e)),{}),o=t.length;for(let e=0;e{this.options.formData.slice().filter((e=>\"tinymce\"===e.subtype)).forEach((e=>window.tinymce.get(e.name).setContent(\"\"))),e.querySelectorAll(\"input, select, textarea\").forEach((e=>{[\"checkbox\",\"radio\"].includes(e.type)?e.checked=!1:e.value=\"\"}))}))}parseFormData(e){const t={xml:e=>E(e),json:e=>window.JSON.parse(e)};return\"object\"!=typeof e&&(e=t[this.options.dataType](e)||!1),e}}!function(e){let t;const r={init:(e,n={})=>(t=e,r.instance=new le(n),e.each((t=>r.instance.render(e[t],t))),r.instance),userData:()=>r.instance&&r.instance.userData,clear:()=>r.instance&&r.instance.clear(),setData:e=>{if(r.instance){const t=r.instance;t.options.formData=t.parseFormData(e)}},render:(e,n={})=>{if(r.instance){const o=r.instance;e||(e=o.options.formData),o.options=Object.assign({},o.options,n,{formData:o.parseFormData(e)}),t.each((e=>r.instance.render(t[e],e)))}},html:()=>t.map((e=>t[e])).html()};e.fn.formRender=function(e={},...t){if(r[e])return r[e].apply(this,t);{const t=r.init(this,e);return Object.assign(r,t),t}},e.fn.controlRender=function(e,t={}){t.formData=e,t.dataType=\"string\"==typeof e?\"json\":\"xml\";const r=new le(t),n=this;return n.each((e=>r.renderControl(n[e]))),n}}(jQuery)}()}()}(jQuery);"; +const formRenderSource: string = + '/*! For license information please see form-render.min.js.LICENSE.txt */\n!function(e){"use strict";!function(){var t={962:function(e,t,r){r.r(t);var n=r(645),o=r.n(n),i=r(278),s=r.n(i)()(o());s.push([e.id,\'.rendered-form *{box-sizing:border-box}.rendered-form.formbuilder-embedded-bootstrap button,.rendered-form.formbuilder-embedded-bootstrap input,.rendered-form.formbuilder-embedded-bootstrap select,.rendered-form.formbuilder-embedded-bootstrap textarea{font-family:inherit;font-size:inherit;line-height:inherit}.rendered-form.formbuilder-embedded-bootstrap input{line-height:normal}.rendered-form.formbuilder-embedded-bootstrap textarea{overflow:auto}.rendered-form.formbuilder-embedded-bootstrap button,.rendered-form.formbuilder-embedded-bootstrap input,.rendered-form.formbuilder-embedded-bootstrap select,.rendered-form.formbuilder-embedded-bootstrap textarea{font-family:inherit;font-size:inherit;line-height:inherit}.rendered-form.formbuilder-embedded-bootstrap .btn-group{position:relative;display:inline-block;vertical-align:middle}.rendered-form.formbuilder-embedded-bootstrap .btn-group>.btn{position:relative;float:left}.rendered-form.formbuilder-embedded-bootstrap .btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.rendered-form.formbuilder-embedded-bootstrap .btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.rendered-form.formbuilder-embedded-bootstrap .btn-group .btn+.btn,.rendered-form.formbuilder-embedded-bootstrap .btn-group .btn+.btn-group,.rendered-form.formbuilder-embedded-bootstrap .btn-group .btn-group+.btn,.rendered-form.formbuilder-embedded-bootstrap .btn-group .btn-group+.btn-group{margin-left:-1px}.rendered-form.formbuilder-embedded-bootstrap .btn-group>.btn:last-child:not(:first-child),.rendered-form.formbuilder-embedded-bootstrap .btn-group>.dropdown-toggle:not(:first-child),.rendered-form.formbuilder-embedded-bootstrap .btn-group .input-group .form-control:last-child,.rendered-form.formbuilder-embedded-bootstrap .btn-group .input-group-addon:last-child,.rendered-form.formbuilder-embedded-bootstrap .btn-group .input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.rendered-form.formbuilder-embedded-bootstrap .btn-group .input-group-btn:first-child>.btn:not(:first-child),.rendered-form.formbuilder-embedded-bootstrap .btn-group .input-group-btn:last-child>.btn,.rendered-form.formbuilder-embedded-bootstrap .btn-group .input-group-btn:last-child>.btn-group>.btn,.rendered-form.formbuilder-embedded-bootstrap .btn-group .input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.rendered-form.formbuilder-embedded-bootstrap .btn-group>.btn.active,.rendered-form.formbuilder-embedded-bootstrap .btn-group>.btn:active,.rendered-form.formbuilder-embedded-bootstrap .btn-group>.btn:focus,.rendered-form.formbuilder-embedded-bootstrap .btn-group>.btn:hover{z-index:2}.rendered-form.formbuilder-embedded-bootstrap .btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-image:none;border-radius:4px}.rendered-form.formbuilder-embedded-bootstrap .btn.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.rendered-form.formbuilder-embedded-bootstrap .btn.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.rendered-form.formbuilder-embedded-bootstrap .btn.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.rendered-form.formbuilder-embedded-bootstrap .btn.active,.rendered-form.formbuilder-embedded-bootstrap .btn.btn-active,.rendered-form.formbuilder-embedded-bootstrap .btn:active{background-image:none}.rendered-form.formbuilder-embedded-bootstrap .input-group .form-control:last-child,.rendered-form.formbuilder-embedded-bootstrap .input-group-addon:last-child,.rendered-form.formbuilder-embedded-bootstrap .input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.rendered-form.formbuilder-embedded-bootstrap .input-group-btn:first-child>.btn:not(:first-child),.rendered-form.formbuilder-embedded-bootstrap .input-group-btn:last-child>.btn,.rendered-form.formbuilder-embedded-bootstrap .input-group-btn:last-child>.btn-group>.btn,.rendered-form.formbuilder-embedded-bootstrap .input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.rendered-form.formbuilder-embedded-bootstrap .input-group .form-control,.rendered-form.formbuilder-embedded-bootstrap .input-group-addon,.rendered-form.formbuilder-embedded-bootstrap .input-group-btn{display:table-cell}.rendered-form.formbuilder-embedded-bootstrap .input-group-lg>.form-control,.rendered-form.formbuilder-embedded-bootstrap .input-group-lg>.input-group-addon,.rendered-form.formbuilder-embedded-bootstrap .input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333}.rendered-form.formbuilder-embedded-bootstrap .input-group{position:relative;display:table;border-collapse:separate}.rendered-form.formbuilder-embedded-bootstrap .input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.rendered-form.formbuilder-embedded-bootstrap .form-control,.rendered-form.formbuilder-embedded-bootstrap output{font-size:14px;line-height:1.42857143;display:block}.rendered-form.formbuilder-embedded-bootstrap textarea.form-control{height:auto}.rendered-form.formbuilder-embedded-bootstrap .form-control{height:34px;display:block;width:100%;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px}.rendered-form.formbuilder-embedded-bootstrap .form-control:focus{outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.rendered-form.formbuilder-embedded-bootstrap .form-group{margin-left:0px;margin-bottom:15px}.rendered-form.formbuilder-embedded-bootstrap .btn,.rendered-form.formbuilder-embedded-bootstrap .form-control{background-image:none}.rendered-form.formbuilder-embedded-bootstrap .pull-right{float:right}.rendered-form.formbuilder-embedded-bootstrap .pull-left{float:left}.rendered-form .formbuilder-required,.rendered-form .required-asterisk{color:#c10000}.rendered-form .formbuilder-checkbox-group input[type=checkbox],.rendered-form .formbuilder-checkbox-group input[type=radio],.rendered-form .formbuilder-radio-group input[type=checkbox],.rendered-form .formbuilder-radio-group input[type=radio]{margin:0 4px 0 0}.rendered-form .formbuilder-checkbox-inline,.rendered-form .formbuilder-radio-inline{margin-right:8px;display:inline-block;vertical-align:middle;padding-left:0}.rendered-form .formbuilder-checkbox-inline label input[type=text],.rendered-form .formbuilder-radio-inline label input[type=text]{margin-top:0}.rendered-form .formbuilder-checkbox-inline:first-child,.rendered-form .formbuilder-radio-inline:first-child{padding-left:0}.rendered-form .formbuilder-autocomplete-list{background-color:#fff;display:none;list-style:none;padding:0;border:1px solid #ccc;border-width:0 1px 1px;position:absolute;z-index:20;max-height:200px;overflow-y:auto}.rendered-form .formbuilder-autocomplete-list li{display:none;cursor:default;padding:5px;margin:0;transition:background-color 200ms ease-in-out}.rendered-form .formbuilder-autocomplete-list li:hover,.rendered-form .formbuilder-autocomplete-list li.active-option{background-color:rgba(0,0,0,.075)}.rendered-form *[tooltip]{position:relative}.rendered-form *[tooltip]:hover::after{background:rgba(0,0,0,.9);border-radius:5px 5px 5px 0;bottom:23px;color:#fff;content:attr(tooltip);padding:10px 5px;position:absolute;z-index:98;left:2px;width:230px;text-shadow:none;font-size:12px;line-height:1.5em;cursor:default}.rendered-form *[tooltip]:hover::before{border:solid;border-color:#222 rgba(0,0,0,0);border-width:6px 6px 0;bottom:17px;content:"";left:2px;position:absolute;z-index:99;cursor:default}.rendered-form .tooltip-element{visibility:visible;color:#fff;background:#000;width:16px;height:16px;border-radius:8px;display:inline-block;text-align:center;line-height:16px;margin:0 5px;font-size:12px;cursor:default}.rendered-form .kc-toggle{padding-left:0 !important}.rendered-form .kc-toggle span{position:relative;width:48px;height:24px;background:#e6e6e6;display:inline-block;border-radius:4px;border:1px solid #ccc;padding:2px;overflow:hidden;float:left;margin-right:5px;will-change:transform}.rendered-form .kc-toggle span::after,.rendered-form .kc-toggle span::before{position:absolute;display:inline-block;top:0}.rendered-form .kc-toggle span::after{position:relative;content:"";width:50%;height:100%;left:0;border-radius:3px;background:linear-gradient(to bottom, white 0%, #ccc 100%);border:1px solid #999;transition:transform 100ms;transform:translateX(0)}.rendered-form .kc-toggle span::before{border-radius:4px;top:2px;left:2px;content:"";width:calc(100% - 4px);height:18px;box-shadow:0 0 1px 1px #b3b3b3 inset;background-color:rgba(0,0,0,0)}.rendered-form .kc-toggle input{height:0;overflow:hidden;width:0;opacity:0;pointer-events:none;margin:0}.rendered-form .kc-toggle input:checked+span::after{transform:translateX(100%)}.rendered-form .kc-toggle input:checked+span::before{background-color:#6fc665}.rendered-form label{font-weight:normal}.rendered-form .form-group .formbuilder-required{color:#c10000}.rendered-form .other-option:checked+label input{display:inline-block}.rendered-form .other-val{margin-left:5px;display:none}.rendered-form .form-control.number{width:auto}.rendered-form .form-control[type=color]{width:60px;padding:2px;display:inline-block}.rendered-form .form-control[multiple]{height:auto}\',""]),t.default=s},278:function(e){e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var r="",n=void 0!==t[5];return t[4]&&(r+="@supports (".concat(t[4],") {")),t[2]&&(r+="@media ".concat(t[2]," {")),n&&(r+="@layer".concat(t[5].length>0?" ".concat(t[5]):""," {")),r+=e(t),n&&(r+="}"),t[2]&&(r+="}"),t[4]&&(r+="}"),r})).join("")},t.i=function(e,r,n,o,i){"string"==typeof e&&(e=[[null,e,void 0]]);var s={};if(n)for(var a=0;a0?" ".concat(c[5]):""," {").concat(c[1],"}")),c[5]=i),r&&(c[2]?(c[1]="@media ".concat(c[2]," {").concat(c[1],"}"),c[2]=r):c[2]=r),o&&(c[4]?(c[1]="@supports (".concat(c[4],") {").concat(c[1],"}"),c[4]=o):c[4]="".concat(o)),t.push(c))}},t}},645:function(e){e.exports=function(e){return e[1]}},252:function(e){e.exports=function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=7)}([function(e,t,r){var n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},o=r(2),i=r(10),s=Object.prototype.toString;function a(e){return"[object Array]"===s.call(e)}function l(e){return null!==e&&"object"===(void 0===e?"undefined":n(e))}function d(e){return"[object Function]"===s.call(e)}function c(e,t){if(null!=e)if("object"!==(void 0===e?"undefined":n(e))&&(e=[e]),a(e))for(var r=0,o=e.length;r=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*"}}};n.forEach(["delete","get","head"],(function(e){a.headers[e]={}})),n.forEach(["post","put","patch"],(function(e){a.headers[e]=n.merge(i)})),e.exports=a}).call(this,r(12))},function(e,t,r){e.exports=function(e,t){return function(){for(var r=new Array(arguments.length),n=0;n0&&void 0!==arguments[0]?arguments[0]:s;!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.langs=Object.create(null),this.loaded=[],this.processConfig(t)}return e.prototype.processConfig=function(e){var t=this,r=Object.assign({},s,e),n=r.location,o=function(e,t){var r={};for(var n in e)t.indexOf(n)>=0||Object.prototype.hasOwnProperty.call(e,n)&&(r[n]=e[n]);return r}(r,["location"]),i=n.replace(/\\/?$/,"/");this.config=Object.assign({},{location:i},o);var a=this.config,l=a.override,d=a.preloaded,c=void 0===d?{}:d,u=Object.entries(this.langs).concat(Object.entries(l||c));this.langs=u.reduce((function(e,r){var n=r[0],o=r[1];return e[n]=t.applyLanguage.call(t,n,o),e}),{}),this.locale=this.config.locale||this.config.langs[0]},e.prototype.init=function(e){return this.processConfig.call(this,Object.assign({},this.config,e)),this.setCurrent(this.locale)},e.prototype.addLanguage=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};t="string"==typeof t?this.processFile.call(this,t):t,this.applyLanguage.call(this,e,t),this.config.langs.push("locale")},e.prototype.getValue=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.locale;return this.langs[t]&&this.langs[t][e]||this.getFallbackValue(e)},e.prototype.getFallbackValue=function(e){var t=Object.values(this.langs).find((function(t){return t[e]}));return t&&t[e]},e.prototype.makeSafe=function(e){var t={"{":"\\\\{","}":"\\\\}","|":"\\\\|"};return e=e.replace(/\\{|\\}|\\|/g,(function(e){return t[e]})),new RegExp(e,"g")},e.prototype.put=function(e,t){return this.current[e]=t},e.prototype.get=function(e,t){var r=this.getValue(e);if(r){var o=r.match(/\\{[^}]+?\\}/g),i=void 0;if(t&&o)if("object"===(void 0===t?"undefined":n(t)))for(var s=0;s1&&void 0!==arguments[1])||arguments[1],r=this;return new Promise((function(n,o){if(-1!==r.loaded.indexOf(e)&&t)return r.applyLanguage.call(r,r.langs[e]),n(r.langs[e]);var s=[r.config.location,e,r.config.extension].join("");return(0,i.get)(s).then((function(t){var o=t.data,i=r.processFile(o);return r.applyLanguage.call(r,e,i),r.loaded.push(e),n(r.langs[e])})).catch((function(){var t=r.applyLanguage.call(r,e);n(t)}))}))},e.prototype.applyLanguage=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=this.config.override[e]||{},n=this.langs[e]||{};return this.langs[e]=Object.assign({},n,t,r),this.langs[e]},e.prototype.setCurrent=function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"en-US";return this.loadLang(t).then((function(){return e.locale=t,e.current=e.langs[t],e.current}))},o(e,[{key:"getLangs",get:function(){return this.config.langs}}]),e}();t.default=new a},function(e,t,r){e.exports=r(9)},function(e,t,r){var n=r(0),o=r(2),i=r(11),s=r(1);function a(e){var t=new i(e),r=o(i.prototype.request,t);return n.extend(r,i.prototype,t),n.extend(r,t),r}var l=a(s);l.Axios=i,l.create=function(e){return a(n.merge(s,e))},l.Cancel=r(6),l.CancelToken=r(26),l.isCancel=r(5),l.all=function(e){return Promise.all(e)},l.spread=r(27),e.exports=l,e.exports.default=l},function(e,t,r){function n(e){return!!e.constructor&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}e.exports=function(e){return null!=e&&(n(e)||function(e){return"function"==typeof e.readFloatLE&&"function"==typeof e.slice&&n(e.slice(0,0))}(e)||!!e._isBuffer)}},function(e,t,r){var n=r(1),o=r(0),i=r(21),s=r(22);function a(e){this.defaults=e,this.interceptors={request:new i,response:new i}}a.prototype.request=function(e){"string"==typeof e&&(e=o.merge({url:arguments[0]},arguments[1])),(e=o.merge(n,{method:"get"},this.defaults,e)).method=e.method.toLowerCase();var t=[s,void 0],r=Promise.resolve(e);for(this.interceptors.request.forEach((function(e){t.unshift(e.fulfilled,e.rejected)})),this.interceptors.response.forEach((function(e){t.push(e.fulfilled,e.rejected)}));t.length;)r=r.then(t.shift(),t.shift());return r},o.forEach(["delete","get","head","options"],(function(e){a.prototype[e]=function(t,r){return this.request(o.merge(r||{},{method:e,url:t}))}})),o.forEach(["post","put","patch"],(function(e){a.prototype[e]=function(t,r,n){return this.request(o.merge(n||{},{method:e,url:t,data:r}))}})),e.exports=a},function(e,t,r){var n,o,i=e.exports={};function s(){throw new Error("setTimeout has not been defined")}function a(){throw new Error("clearTimeout has not been defined")}function l(e){if(n===setTimeout)return setTimeout(e,0);if((n===s||!n)&&setTimeout)return n=setTimeout,setTimeout(e,0);try{return n(e,0)}catch(t){try{return n.call(null,e,0)}catch(t){return n.call(this,e,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:s}catch(e){n=s}try{o="function"==typeof clearTimeout?clearTimeout:a}catch(e){o=a}}();var d,c=[],u=!1,f=-1;function p(){u&&d&&(u=!1,d.length?c=d.concat(c):f=-1,c.length&&m())}function m(){if(!u){var e=l(p);u=!0;for(var t=c.length;t;){for(d=c,c=[];++f1)for(var r=1;r=0)return;s[t]="set-cookie"===t?(s[t]?s[t]:[]).concat([r]):s[t]?s[t]+", "+r:r}})),s):s}},function(e,t,r){var n=r(0);e.exports=n.isStandardBrowserEnv()?function(){var e,t=/(msie|trident)/i.test(navigator.userAgent),r=document.createElement("a");function o(e){var n=e;return t&&(r.setAttribute("href",n),n=r.href),r.setAttribute("href",n),{href:r.href,protocol:r.protocol?r.protocol.replace(/:$/,""):"",host:r.host,search:r.search?r.search.replace(/^\\?/,""):"",hash:r.hash?r.hash.replace(/^#/,""):"",hostname:r.hostname,port:r.port,pathname:"/"===r.pathname.charAt(0)?r.pathname:"/"+r.pathname}}return e=o(window.location.href),function(t){var r=n.isString(t)?o(t):t;return r.protocol===e.protocol&&r.host===e.host}}():function(){return!0}},function(e,t,r){function n(){this.message="String contains an invalid character"}n.prototype=new Error,n.prototype.code=5,n.prototype.name="InvalidCharacterError",e.exports=function(e){for(var t,r,o=String(e),i="",s=0,a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";o.charAt(0|s)||(a="=",s%1);i+=a.charAt(63&t>>8-s%1*8)){if((r=o.charCodeAt(s+=.75))>255)throw new n;t=t<<8|r}return i}},function(e,t,r){var n=r(0);e.exports=n.isStandardBrowserEnv()?{write:function(e,t,r,o,i,s){var a=[];a.push(e+"="+encodeURIComponent(t)),n.isNumber(r)&&a.push("expires="+new Date(r).toGMTString()),n.isString(o)&&a.push("path="+o),n.isString(i)&&a.push("domain="+i),!0===s&&a.push("secure"),document.cookie=a.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}:{write:function(){},read:function(){return null},remove:function(){}}},function(e,t,r){var n=r(0);function o(){this.handlers=[]}o.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},o.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},o.prototype.forEach=function(e){n.forEach(this.handlers,(function(t){null!==t&&e(t)}))},e.exports=o},function(e,t,r){var n=r(0),o=r(23),i=r(5),s=r(1),a=r(24),l=r(25);function d(e){e.cancelToken&&e.cancelToken.throwIfRequested()}e.exports=function(e){return d(e),e.baseURL&&!a(e.url)&&(e.url=l(e.baseURL,e.url)),e.headers=e.headers||{},e.data=o(e.data,e.headers,e.transformRequest),e.headers=n.merge(e.headers.common||{},e.headers[e.method]||{},e.headers||{}),n.forEach(["delete","get","head","post","put","patch","common"],(function(t){delete e.headers[t]})),(e.adapter||s.adapter)(e).then((function(t){return d(e),t.data=o(t.data,t.headers,e.transformResponse),t}),(function(t){return i(t)||(d(e),t&&t.response&&(t.response.data=o(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)}))}},function(e,t,r){var n=r(0);e.exports=function(e,t,r){return n.forEach(r,(function(r){e=r(e,t)})),e}},function(e,t,r){e.exports=function(e){return/^([a-z][a-z\\d\\+\\-\\.]*:)?\\/\\//i.test(e)}},function(e,t,r){e.exports=function(e,t){return t?e.replace(/\\/+$/,"")+"/"+t.replace(/^\\/+/,""):e}},function(e,t,r){var n=r(6);function o(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise((function(e){t=e}));var r=this;e((function(e){r.reason||(r.reason=new n(e),t(r.reason))}))}o.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},o.source=function(){var e;return{token:new o((function(t){e=t})),cancel:e}},e.exports=o},function(e,t,r){e.exports=function(e){return function(t){return e.apply(null,t)}}}])},700:function(e,t,r){var n=r(292),o=r(962);"string"==typeof(o=o.__esModule?o.default:o)&&(o=[[e.id,o,""]]);n(o,{attributes:{class:"formBuilder-injected-style"},insert:"head",singleton:!1}),e.exports=o.locals||{}},292:function(e,t,r){var n,o=function(){var e={};return function(t){if(void 0===e[t]){var r=document.querySelector(t);if(window.HTMLIFrameElement&&r instanceof window.HTMLIFrameElement)try{r=r.contentDocument.head}catch(e){r=null}e[t]=r}return e[t]}}(),i=[];function s(e){for(var t=-1,r=0;re}};var i;const s=(e,t)=>{if(0===o.backendOrder.length)return!1;const r=e.toLowerCase();return t=t?t+"":"",r.startsWith("on")||["form","formaction"].includes(r)||t.trim().toLowerCase().startsWith("javascript:")};o.backends.fallback=function(t){const r=document.implementation.createHTMLDocument(""),n=r.createElement("base");n.href=document.location.href,r.head.appendChild(n);const o=["applet","comment","embed","iframe","link","listing","meta","noscript","object","plaintext","script","style","xmp"],i=e.parseHTML(t,r,!1);e(i).find("*").addBack().each(((t,r)=>{"#text"!==r.nodeName&&(r.tagName&&o.includes(r.tagName.toLowerCase())?r.parentElement?r.parentElement.removeChild(r):i.includes(r)&&i.splice(i.indexOf(r),1):r.attributes&&Array.from(r.attributes).forEach((t=>{s(t.name,t.value)&&e(r).removeAttr(t.name)})))}));const a=r.createElement("div");return e(a).html(i),a.innerHTML};const a=e=>{const t=!!o.clobberingProtection.document&&document,r=!!o.clobberingProtection.form&&document.createElement("form");return t&&e in t||r&&e in r?o.clobberingProtection.namespaceAttributes?"user-content-"+e:void 0:e},l={fallback:(e,t)=>{const r=o.backends.fallback,n="function"==typeof r;return n&&(t=r(t)),e.innerHTML=t,n},dompurify:(e,t)=>{const r=o.backends.dompurify;return!(!1===r||!r.isSupported||(e.innerHTML=r.sanitize(t),0))},sanitizer:(e,t)=>{const r=o.backends.sanitizer;return!!r&&(e.setHTML(t,{sanitizer:r}),!0)}},d=["events"],c=["tag","content"];function u(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function f(e){for(var t=1;t=0)continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||{}.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}window.fbLoaded={js:[],css:[]},window.fbEditors={quill:{},tinymce:{}};const h=function(e,t=!1){if(null==e||"object"!=typeof e)return e;const r="function"==typeof window.structuredClone?window.structuredClone(e):Object.assign({},e),n=[null,void 0,""];t&&n.push(!1);for(const e in r)n.includes(r[e])?delete r[e]:Array.isArray(r[e])&&(r[e].length||delete r[e]);return r},b=function(e){return!["values","enableOther","other","label","subtype"].includes(e)},g=(e,t)=>{let r;return e=y(e),t&&(Array.isArray(t)?r=T(t.join(" ")):("boolean"==typeof t&&(t=t.toString()),r=T(t.trim()))),{name:e,value:t=t?`="${r}"`:""}},y=e=>({className:"class"}[e]||v(e)),v=e=>(e=(e=(e=e.replace(/[^\\w\\s\\-\\[\\]]/gi,"")).replace(/\\[([^\\]]+)\\]/g,"-$1")).replace(/([A-Z])/g,(function(e){return"-"+e.toLowerCase()}))).replace(/\\s/g,"-").replace(/^-+/g,""),w=e=>e.replace(/-([a-z])/g,((e,t)=>t.toUpperCase())),x=function(){let e,t=0;return function(r){const n=Date.now();return n===e?++t:(t=0,e=n),(r.type||v(r.label))+"-"+n+"-"+t}}(),O=e=>void 0===e?e:[["array",e=>Array.isArray(e)],["node",e=>e instanceof window.Node||e instanceof window.HTMLElement],["component",()=>e?.dom],[typeof e,()=>!0]].find((t=>t[1](e)))[0],j=function(t,r="",n={}){let i=O(r);const{events:u}=n,f=m(n,d),p=document.createElement(t),h={string:t=>{((t,r,n=!1)=>{if(!n){const n=document.createElement(t.tagName);return void 0!==o.backendOrder.find((e=>l[e](n,r)))?((t=>{e(t).find("*").each(((e,t)=>{["embed","form","iframe","image","img","object"].includes(t.tagName.toLowerCase())&&t.removeAttribute("name"),["id","name"].forEach((e=>{if(t.hasAttribute(e)){const r=a(t.getAttribute(e));void 0===r?t.removeAttribute(e):t.setAttribute(e,r)}}))}))})(n),t.innerHTML=n.innerHTML,t):(t.innerHTML=r,t)}t.textContent=r})(p,p.innerHTML+t)},object:e=>{const{tag:t,content:r}=e,n=m(e,c);return p.appendChild(j(t,r,n))},node:e=>p.appendChild(e),array:e=>{for(let t=0;t{e=e(),i=O(e),h[i](e)},undefined:()=>{}};for(const e in f)if(f.hasOwnProperty(e)){const t=y(e);let r=Array.isArray(f[e])?P(f[e].join(" ").split(" ")).join(" "):f[e];if(s(t,r))continue;if("boolean"==typeof r){if(!0===r){const e="contenteditable"===t||t;p.setAttribute(t,e)}}else"id"!==t&&"name"!==t||(r=a(r)),void 0!==r&&p.setAttribute(t,r)}return r&&h[i](r),((e,t)=>{if(t)for(const r in t)t.hasOwnProperty(r)&&e.addEventListener(r,(e=>t[r](e)))})(p,u),p},k=e=>{const t=e.attributes,r={};return L(t,(e=>{let n=t[e].value||"";n.match(/false|true/g)?n="true"===n:n.match(/undefined/g)&&(n=void 0),n&&(r[w(t[e].name)]=n)})),r},C=e=>{const t=[];for(let r=0;r{const t=[];if(e.length){const r=e[0].getElementsByTagName("value");for(let e=0;e{const t=(new window.DOMParser).parseFromString(e,"text/xml"),r=[];if(t){const e=t.getElementsByTagName("field");for(let t=0;t{const t=document.createElement("textarea");return t.innerHTML=e,t.textContent},T=e=>{const t={\'"\':""","&":"&","<":"<",">":">"};return"string"==typeof e?e.replace(/["&<>]/g,(e=>t[e]||e)):e},L=function(e,t,r){for(let n=0;ne.filter(((e,t,r)=>r.indexOf(e)===t)),R=(e,t="")=>{const r=jQuery;let n=[];return Array.isArray(e)||(e=[e]),N(e)||(n=jQuery.map(e,(e=>{const r={dataType:"script",cache:!0,url:(t||"")+e};return jQuery.ajax(r).done((()=>window.fbLoaded.js.push(e)))}))),n.push(jQuery.Deferred((e=>r(e.resolve)))),jQuery.when(...n)},N=(e,t="js")=>{const r=window.fbLoaded[t];return Array.isArray(e)?e.every((e=>r.includes(e))):r.includes(e)},D=(t,r="")=>{Array.isArray(t)||(t=[t]),t.forEach((t=>{let n="href",o=t,i="";if("object"==typeof t&&(n=t.type||(t.style?"inline":"href"),i=t.id,o=i||t.href||t.style,t="inline"===n?t.style:t.href),!N(o,"css")){if("href"===n){const e=document.createElement("link");e.type="text/css",e.rel="stylesheet",e.href=(r||"")+t,document.head.appendChild(e)}else e(``).attr("id",i).appendTo(e(document.head));window.fbLoaded.css.push(o)}}))},q=(e,t)=>{const r=Object.assign({},e,t);for(const n in t)r.hasOwnProperty(n)&&(Array.isArray(t[n])?r[n]=Array.isArray(e[n])?P(e[n].concat(t[n])):t[n]:"object"==typeof t[n]?r[n]=q(e[n],t[n]):r[n]=t[n]);return r},M=/^col-(xs|sm|md|lg)-([^\\s]+)/,F={addEventListeners:(e,t,r)=>t.split(" ").forEach((t=>e.addEventListener(t,r,!1))),attrString:e=>Object.entries(e).map((([e,t])=>b(e)&&Object.values(g(e,t)).join(""))).filter(Boolean).join(" "),camelCase:w,capitalize:e=>e.replace(/\\b\\w/g,(function(e){return e.toUpperCase()})),closest:(e,t)=>{const r=t.replace(".","");for(;(e=e.parentElement)&&!e.classList.contains(r););return e},getContentType:O,escapeAttr:T,escapeAttrs:e=>{for(const t in e)e.hasOwnProperty(t)&&(e[t]=T(e[t]));return e},escapeHtml:e=>{const t=document.createElement("textarea");return t.textContent=e,t.innerHTML},forceNumber:e=>e.replace(/[^0-9]/g,""),forEach:L,getScripts:R,getStyles:D,hyphenCase:v,isCached:N,markup:j,merge:q,mobileClass:()=>{let e="";var t;return t=navigator.userAgent||navigator.vendor||window.opera,/(android|bb\\d+|meego).+mobile|avantgo|bada\\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(t)&&(e="formbuilder-mobile"),e},nameAttr:x,parsedHtml:A,parseXML:E,removeFromArray:(e,t)=>{const r=t.indexOf(e);r>-1&&t.splice(r,1)},safeAttr:g,safeAttrName:y,safename:e=>e.replace(/\\s/g,"-").replace(/[^a-zA-Z0-9[\\]_-]/g,""),subtract:(e,t)=>t.filter((function(e){return!~this.indexOf(e)}),e),trimObj:h,unique:P,validAttr:b,titleCase:function(e){const t=["a","an","and","as","at","but","by","for","for","from","in","into","near","nor","of","on","onto","or","the","to","with"].map((e=>`\\\\s${e}\\\\s`)),r=new RegExp(`(?!${t.join("|")})\\\\w\\\\S*`,"g");return`${e}`.replace(r,(e=>e.charAt(0).toUpperCase()+e.slice(1).replace(/[A-Z]/g,(e=>` ${e}`))))},firstNumberOrUndefined:function(...e){return e.find((e=>"number"==typeof e))},splitObject:(e,t)=>{const r=e=>(t,r)=>(t[r]=e[r],t);return[Object.keys(e).filter((e=>t.includes(e))).reduce(r(e),{}),Object.keys(e).filter((e=>!t.includes(e))).reduce(r(e),{})]}};e.fn.swapWith=function(t){const r=this,n=e(t),o=e("
");return r.before(o),n.before(r),o.before(n).remove(),r};var z=F;const B=(e,t,r=!0)=>{const n=[];let o=["none","block"];r&&(o=o.reverse());for(let r=e.length-1;r>=0;r--)-1!==e[r].textContent.toLowerCase().indexOf(t.toLowerCase())?(e[r].style.display=o[0],n.push(e[r])):e[r].style.display=o[1];return n};new RegExp(`(${["select","checkbox-group","checkbox","radio-group","autocomplete"].join("|")})`);const H=["label","type"];class ${constructor(e,t){this.rawConfig=jQuery.extend({},e),e=jQuery.extend({},e),this.preview=t,delete e.isPreview,this.preview&&delete e.required;const r=["label","description","subtype","required","disabled"];for(const t of r)this[t]=e[t],delete e[t];e.id||(e.name?e.id=e.name:e.id="control-"+Math.floor(1e7*Math.random()+1)),this.id=e.id,this.type=e.type,this.description&&(e.title=this.description),$.controlConfig||($.controlConfig={});const n=this.subtype?this.type+"."+this.subtype:this.type;this.classConfig=jQuery.extend({},$.controlConfig[this.type]||{},$.controlConfig[n]||{}),this.subtype&&(e.type=this.subtype),this.required&&(e.required="required",e["aria-required"]="true"),this.disabled&&(e.disabled="disabled"),this.config=e,this.configure()}static get definition(){return{}}static register(e,t,r){const n=r?r+".":"";$.classRegister||($.classRegister={}),Array.isArray(e)||(e=[e]);for(const r of e)-1===r.indexOf(".")?$.classRegister[n+r]=t:$.error(`Ignoring type ${r}. Cannot use the character \'.\' in a type name.`)}static getRegistered(e=!1){const t=Object.keys($.classRegister);return t.length?t.filter((t=>e?t.indexOf(e+".")>-1:-1===t.indexOf("."))):t}static getRegisteredSubtypes(){const e={};for(const t in $.classRegister)if($.classRegister.hasOwnProperty(t)){const[r,n]=t.split(".");if(!n)continue;e[r]||(e[r]=[]),e[r].push(n)}return e}static getClass(e,t){const r=t?e+"."+t:e;return $.classRegister[r]||$.classRegister[e]||$.error("Invalid control type. (Type: "+e+", Subtype: "+t+"). Please ensure you have registered it, and imported it correctly.")}static loadCustom(e){let t=[];if(e&&(t=t.concat(e)),window.fbControls&&(t=t.concat(window.fbControls)),!this.fbControlsLoaded){for(const e of t)e($,$.classRegister);this.fbControlsLoaded=!0}}static mi18n(e,t){const n=this.definition;let o=n.i18n||{};o=o[r().locale]||o.default||o;const i=this.camelCase(e),s="object"==typeof o?o[i]||o[e]:o;if(s)return s;let a=n.mi18n;return"object"==typeof a&&(a=a[i]||a[e]),a||(a=i),r().get(a,t)}static active(e){return!Array.isArray(this.definition.inactive)||-1===this.definition.inactive.indexOf(e)}static label(e){return this.mi18n(e)}static icon(e){const t=this.definition;return t&&"object"==typeof t.icon?t.icon[e]:t.icon}configure(){}build(){const e=this.config,{label:t,type:r}=e,n=function(e,t){if(null==e)return{};var r,n,o=function(e,t){if(null==e)return{};var r={};for(var n in e)if({}.hasOwnProperty.call(e,n)){if(t.indexOf(n)>=0)continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||{}.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}(e,H);return this.markup(r,A(t),n)}on(e){const t={prerender:e=>e,render:e=>{const t=()=>{this.onRender&&this.onRender(e)};this.css&&D(this.css),this.js&&!N(this.js)?R(this.js).done(t):t()}};return e?t[e]:t}static error(e){throw new Error(e)}markup(e,t="",r={}){return this.element=j(e,t,r),this.element}parsedHtml(e){return A(e)}static camelCase(e){return w(e)}}const U=(e,t)=>{let r=e.id?`formbuilder-${e.type} form-group field-${e.id}`:"";if(e.className){const n=(e=>"string"==typeof e?e.split(" ").filter((e=>M.test(e)||e.startsWith("row-"))):[])(e.className);n&&n.length>0&&(r+=` ${n.join(" ")}`,Array.isArray(t)||(t=[t]),t.forEach((e=>{e.classList&&e.classList.remove(...n),e.querySelectorAll("[class*=row-],[class*=col-]").forEach((e=>{e.classList&&e.classList.remove(...n)}))})))}return r};class I{constructor(e,t=!1,r=!1,n={}){this.preview=null!=t&&t,this.disableHTMLLabels=null!=r&&r,this.controlConfig=null!=n?n:{},this.templates={label:null,help:null,default:(e,t,r,n)=>(r&&t.appendChild(r),this.markup("div",[t,e],{className:U(n,e)})),noLabel:(e,t,r,n)=>this.markup("div",e,{className:U(n,e)}),hidden:e=>e},e&&(this.templates=jQuery.extend(this.templates,e)),this.configure()}configure(){}build(e,t,r){this.preview&&(t.name?t.name=t.name+"-preview":t.name=z.nameAttr(t)+"-preview"),t.id=t.name,this.data=jQuery.extend({},t),$.controlConfig=this.controlConfig;const n=new e(t,this.preview);let o=n.build();if("object"==typeof o&&o.field||(o={field:o}),"string"==typeof o.field){const e=this.markup("div",o.field,{});1===e.childElementCount?o.field=e.children.item(0):o.field=Array.from(e.children)}const i=this.label(),s=this.help();let a;a=r&&this.isTemplate(r)?r:this.isTemplate(o.layout)?o.layout:"default";const l=this.processTemplate(a,o.field,i,s);return n.on("prerender")(l),l.addEventListener("fieldRendered",n.on("render")),l}label(){const e=this.data.label||"",t=[this.disableHTMLLabels?document.createTextNode(e):z.parsedHtml(e)];return this.data.required&&t.push(this.markup("span","*",{className:"formbuilder-required"})),this.isTemplate("label")?this.processTemplate("label",t):this.markup("label",t,{for:this.data.id,className:`formbuilder-${this.data.type}-label`})}help(){return this.data.description?this.isTemplate("help")?this.processTemplate("help",this.data.description):this.markup("span","?",{className:"tooltip-element",tooltip:this.data.description}):null}isTemplate(e){return"function"==typeof this.templates[e]}processTemplate(e,...t){let r=this.templates[e](...t,this.data);return r.jquery&&(r=r[0]),r}markup(e,t="",r={}){return z.markup(e,t,r)}}const _=["values","type"];$.register("autocomplete",class extends ${static get definition(){return{mi18n:{requireValidOption:"requireValidOption"}}}build(){const e=this.config,{values:t,type:r}=e,n=function(e,t){if(null==e)return{};var r,n,o=function(e,t){if(null==e)return{};var r={};for(var n in e)if({}.hasOwnProperty.call(e,n)){if(t.indexOf(n)>=0)continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||{}.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}(e,_),o=e=>{const t=e.target.nextSibling.nextSibling,r=e.target.nextSibling,n=this.getActiveOption(t);let o=new Map([[38,()=>{const e=this.getPreviousOption(n);e&&this.selectOption(t,e)}],[40,()=>{const e=this.getNextOption(n);e&&this.selectOption(t,e)}],[13,()=>{n?(e.target.value=n.innerHTML,r.value=n.getAttribute("value"),"none"===t.style.display?this.showList(t,n):this.hideList(t)):this.config.requireValidOption&&(this.isOptionValid(t,e.target.value)||(e.target.value="",e.target.nextSibling.value="")),e.preventDefault()}],[27,()=>{this.hideList(t)}]]).get(e.keyCode);return o||(o=()=>!1),o()},i={focus:e=>{const t=e.target.nextSibling.nextSibling,r=B(t.querySelectorAll("li"),e.target.value);if(e.target.addEventListener("keydown",o),e.target.value.length>0){const e=r.length>0?r[r.length-1]:null;this.showList(t,e)}},blur:e=>{e.target.removeEventListener("keydown",o);const t=setTimeout((()=>{e.target.nextSibling.nextSibling.style.display="none",clearTimeout(t)}),200);if(this.config.requireValidOption){const t=e.target.nextSibling.nextSibling;this.isOptionValid(t,e.target.value)||(e.target.value="",e.target.nextSibling.value="")}},input:e=>{const t=e.target.nextSibling.nextSibling;e.target.nextSibling.value=e.target.value;const r=B(t.querySelectorAll("li"),e.target.value);if(0==r.length)this.hideList(t);else{let e=this.getActiveOption(t);e||(e=r[r.length-1]),this.showList(t,e)}}},s=Object.assign({},n,{id:`${n.id}-input`,autocomplete:"off",events:i}),a=Object.assign({},n,{type:"hidden"});delete s.name;const l=[this.markup("input",null,s),this.markup("input",null,a)],d=t.map((e=>{const t=e.label,r={events:{click:t=>{const r=t.target.parentElement,n=r.previousSibling.previousSibling;n.value=e.label,n.nextSibling.value=e.value,this.hideList(r)}},value:e.value};return this.markup("li",t,r)}));return l.push(this.markup("ul",d,{id:`${n.id}-list`,className:`formbuilder-${r}-list`})),l}hideList(e){this.selectOption(e,null),e.style.display="none"}showList(e,t){this.selectOption(e,t),e.style.display="block",e.style.width=e.parentElement.offsetWidth+"px"}getActiveOption(e){const t=e.getElementsByClassName("active-option")[0];return t&&"none"!==t.style.display?t:null}getPreviousOption(e){let t=e;do{t=t?t.previousSibling:null}while(null!=t&&"none"===t.style.display);return t}getNextOption(e){let t=e;do{t=t?t.nextSibling:null}while(null!=t&&"none"===t.style.display);return t}selectOption(e,t){const r=e.querySelectorAll("li");for(let e=0;e=0)continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||{}.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}(e,X);let n=t;const o={paragraph:"p",header:this.subtype};return o[t]&&(n=o[t]),{field:this.markup(n,z.parsedHtml(this.label),r),layout:"noLabel"}}}$.register(["paragraph","header"],W),$.register(["p","address","blockquote","canvas","output"],W,"paragraph"),$.register(["h1","h2","h3","h4","h5","h6"],W,"header");const J=["values","value","placeholder","type","inline","other","toggle"],G=["label"];function Z(e,t){if(null==e)return{};var r,n,o=function(e,t){if(null==e)return{};var r={};for(var n in e)if({}.hasOwnProperty.call(e,n)){if(t.indexOf(n)>=0)continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||{}.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}$.register(["select","checkbox-group","radio-group","checkbox"],class extends ${static get definition(){return{inactive:["checkbox"],mi18n:{minSelectionRequired:"minSelectionRequired"}}}build(){const e=[],t=this.config,{values:r,value:n,placeholder:o,type:i,inline:s,other:a,toggle:l}=t,d=Z(t,J),c=i.replace("-group",""),u="select"===i;if((d.multiple||"checkbox-group"===i)&&(d.name=d.name+"[]"),("checkbox-group"===i||"radio-group"===i)&&d.required){const e=this,t=this.onRender.bind(this);this.onRender=function(){t(),e.groupRequired()}}if(delete d.title,r){o&&u&&e.push(this.markup("option",o,{disabled:!0,selected:!0,value:""}));for(let t=0;t{const t=e.target;t.parentElement.previousElementSibling.value=t.value}},id:`${t.id}-value`,className:"other-val"},i=this.markup("input",null,n),a=[document.createTextNode($.mi18n("other")),this.markup("input",null,o)],l=this.markup("label",a,{for:n.id}),u=this.markup("div",[i,l],{className:r});e.push(u)}}return this.dom="select"==i?this.markup(c,e,h(d,!0)):this.markup("div",e,{className:i}),this.dom}groupRequired(){const e=this.element.getElementsByTagName("input"),t=this.element.querySelectorAll("input:not([type=text])"),r=this.element.querySelector(".other-option"),n=this.element.querySelector(".other-val"),o=()=>{const e=[].some.call(t,(e=>e.checked));((e,t,r,n)=>{[].forEach.call(e,(e=>{n?e.removeAttribute("required"):e.setAttribute("required","required"),((e,t)=>{const r=$.mi18n("minSelectionRequired",1);t?e.setCustomValidity(""):e.setCustomValidity(r)})(e,n)})),t&&(t.checked?r.setAttribute("required","required"):r.removeAttribute("required"))})(t,r,n,e)};for(let t=e.length-1;t>=0;t--)e[t].addEventListener("change",o);o()}onRender(){if(this.config.userData){const t=this.config.userData.slice();"select"===this.config.type?e(this.dom).val(t).prop("selected",!0):this.config.type.endsWith("-group")&&("checkbox-group"===this.config.type&&this.dom.querySelectorAll("input[type=checkbox]").forEach((e=>{e.removeAttribute("checked")})),this.dom.querySelectorAll("input").forEach((e=>{if(!e.classList.contains("other-val")){for(let r=0;r0){const r=this.dom.querySelector(`#${e.id}-value`);e.setAttribute("checked","checked"),r.value=e.value=t[0],r.style.display="inline-block"}}})))}}});class K extends ${static get definition(){return{mi18n:{date:"dateField",file:"fileUpload"}}}build(){let{name:e}=this.config;e=this.config.multiple?`${e}[]`:e;const t=Object.assign({},this.config,{name:e});return this.dom=this.markup("input",null,t),this.dom}onRender(){this.config.userData&&e(this.dom).val(this.config.userData[0])}}$.register(["text","file","date","number"],K),$.register(["text","password","email","color","tel"],K,"text"),$.register(["date","time","datetime-local"],K,"date"),$.register(["number","range"],K,"number");const Y=["value"];class ee extends ${static get definition(){return{mi18n:{textarea:"textArea"}}}build(){const e=this.config,{value:t=""}=e,r=function(e,t){if(null==e)return{};var r,n,o=function(e,t){if(null==e)return{};var r={};for(var n in e)if({}.hasOwnProperty.call(e,n)){if(t.indexOf(n)>=0)continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||{}.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}(e,Y);return delete r.type,this.field=this.markup("textarea",this.parsedHtml(t),r),this.field}onRender(){this.config.userData&&e(this.field).val(this.config.userData[0])}on(t){return"prerender"==t&&this.preview?t=>{this.field&&(t=this.field),e(t).on("mousedown",(e=>{e.stopPropagation()}))}:super.on(t)}}$.register("textarea",ee),$.register("textarea",ee,"textarea");const te=["value"];ee.register("tinymce",class extends ee{configure(){if(this.js=[],window.tinymce||this.js.push("https://cdnjs.cloudflare.com/ajax/libs/tinymce/4.9.11/tinymce.min.js"),this.classConfig.js){let e=this.classConfig.js;Array.isArray(e)||(e=new Array(e)),this.js=this.js.concat(e),delete this.classConfig.js}this.classConfig.css&&(this.css=this.classConfig.css),this.editorOptions={height:250,paste_data_images:!0,plugins:["advlist","autolink","lists","link","image","charmap","print","preview","anchor","searchreplace","visualblocks","code","fullscreen","insertdatetime","media","table","contextmenu","paste","code"],toolbar:"undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image | table"}}build(){const e=this.config,{value:t=""}=e,r=function(e,t){if(null==e)return{};var r,n,o=function(e,t){if(null==e)return{};var r={};for(var n in e)if({}.hasOwnProperty.call(e,n)){if(t.indexOf(n)>=0)continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||{}.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}(e,te);return delete r.type,this.field=this.markup("textarea",this.parsedHtml(t),r),r.disabled&&(this.editorOptions.readonly=!0),this.field}onRender(){const e=window.tinymce.get(this.id);e&&window.tinymce.remove(e);const t=jQuery.extend(this.editorOptions,this.classConfig);t.target=this.field;const r=[];Number(window.tinymce.majorVersion)>=5&&r.push("contextmenu"),Number(window.tinymce.majorVersion)>=6&&r.push("paste","print"),t.plugins=t.plugins.filter((e=>-1===r.indexOf(e)));const n=this.config.userData?this.parsedHtml(this.config.userData[0]):void 0,o=window.lastFormBuilderCopiedTinyMCE?this.parsedHtml(window.lastFormBuilderCopiedTinyMCE):void 0;window.lastFormBuilderCopiedTinyMCE=null;const i=function(e){o?e[0].setContent(o):n&&e[0].setContent(n)};setTimeout((()=>{window.tinymce.init(t).then(i)}),0)}},"textarea");const re=["value"];function ne(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function oe(e){for(var t=1;t=0)continue;r[n]=e[n]}return r}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||{}.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}(e,re);return delete r.type,this.field=this.markup("div",null,r),this.field.classList.contains("form-control")&&this.field.classList.remove("form-control"),this.field}onRender(e){const t=this.config.value||"",r=window.Quill.import("delta");window.fbEditors.quill[this.id]={};const n=window.fbEditors.quill[this.id];return n.instance=new window.Quill(this.field,this.editorConfig),n.data=new r,t&&n.instance.setContents(window.JSON.parse(this.parsedHtml(t))),n.instance.on("text-change",(function(e){n.data=n.data.compose(e)})),e}},"textarea"),r().addLanguage("en-US",{NATIVE_NAME:"English (US)",ENGLISH_NAME:"English",addOption:"Add Option +",allFieldsRemoved:"All fields were removed.",allowMultipleFiles:"Allow users to upload multiple files",allowSelect:"Allow selection",autocomplete:"Autocomplete",button:"Button",cannotBeEmpty:"This field cannot be empty",checkboxGroup:"Checkbox Group",checkbox:"Checkbox",checkboxes:"Checkboxes",className:"Class",clearAllMessage:"Are you sure you want to clear all fields?",clear:"Clear",close:"Close",content:"Content",copy:"Copy To Clipboard",copyButton:"+",copyButtonTooltip:"Copy",dateField:"Date Field",description:"Help Text",descriptionField:"Description",devMode:"Developer Mode",editNames:"Edit Names",editorTitle:"Form Elements",editXML:"Edit XML",enableOther:"Enable "Other"",enableOtherMsg:"Let users enter an unlisted option",fieldDeleteWarning:"false",fieldVars:"Field Variables",fieldNonEditable:"This field cannot be edited.",fieldRemoveWarning:"Are you sure you want to remove this field?",fileUpload:"File Upload",formUpdated:"Form Updated",getStarted:"Drag a field from the right to this area",header:"Header",hide:"Edit",hidden:"Hidden Input",inline:"Inline",inlineDesc:"Display {type} inline",label:"Label",labelEmpty:"Field Label cannot be empty",limitRole:"Limit access to one or more of the following roles:",mandatory:"Mandatory",maxlength:"Max Length",minOptionMessage:"This field requires a minimum of 2 options",minSelectionRequired:"Minimum {min} selections required",multipleFiles:"Multiple Files",name:"Name",no:"No",noFieldsToClear:"There are no fields to clear",number:"Number",off:"Off",on:"On",option:"Option",optionCount:"Option {count}",options:"Options",optional:"optional",optionLabelPlaceholder:"Label",optionValuePlaceholder:"Value",optionEmpty:"Option value required",other:"Other",paragraph:"Paragraph",placeholder:"Placeholder","placeholders.value":"Value","placeholders.label":"Label","placeholders.email":"Enter your email","placeholders.className":"space separated classes","placeholders.password":"Enter your password",preview:"Preview",radioGroup:"Radio Group",radio:"Radio",removeMessage:"Remove Element",removeOption:"Remove Option",remove:"×",required:"Required",reset:"Reset",requireValidOption:"Only accept a pre-defined Option",richText:"Rich Text Editor",roles:"Access",rows:"Rows",save:"Save",selectOptions:"Options",select:"Select",selectColor:"Select Color",selectionsMessage:"Allow Multiple Selections",size:"Size",sizes:"Sizes","size.xs":"Extra Small","size.sm":"Small","size.m":"Default","size.lg":"Large",step:"Step",style:"Style",styles:"Styles","styles.btn":"Button Styles","styles.btn.default":"Default","styles.btn.danger":"Danger","styles.btn.info":"Info","styles.btn.primary":"Primary","styles.btn.success":"Success","styles.btn.warning":"Warning",submit:"Submit",subtype:"Type",text:"Text Field",textArea:"Text Area",toggle:"Toggle",warning:"Warning!",value:"Value",viewJSON:"[{…}]",viewXML:"</>",yes:"Yes"});const se={location:"assets/lang/"};n(700);class ae{constructor(e={},t=[]){this.customRegister={},this.templateControlRegister={},this.def={icon:{},i18n:{}},this.register(e,t)}register(e={},t=[]){t.forEach((t=>{if(t.template){const r=t.type||t.attrs?.type;e[r]=t.template}}));const n=r().locale;this.def.i18n[n]||(this.def.i18n[n]={});const o=this;Object.keys(e).forEach((t=>{const r=function(r,n){this.customControl=new Q(r,n,e[t]),this.build=function(){return this.customControl.build()},this.on=function(e){return this.customControl.on(e)}};r.definition={},r.label=e=>o.label(e),r.icon=e=>o.icon(e),this.templateControlRegister[t]=r}));for(const o of t){let t=o.type;if(o.attrs=o.attrs||{},!t){if(!o.attrs.type){$.error("Ignoring invalid custom field definition. Please specify a type property.");continue}t=o.attrs.type}let i=o.subtype||t;if(e[t]){const e=this.templateControlRegister[t];e.definition=o,this.customRegister[i]=jQuery.extend(o,{type:t,class:e})}else try{const e=$.getClass(t,o.subtype);i=o.datatype?o.datatype:`${t}-${Math.floor(9e3*Math.random()+1e3)}`,this.customRegister[i]=jQuery.extend(o,{type:t,class:e})}catch(e){$.error("Error while registering custom field: "+t+(o.subtype?":"+o.subtype:"")+". Unable to find any existing defined control or template for rendering.")}this.def.i18n[n][i]=Array.isArray(o.label)?r().get(...o.label)||o.label[0]:o.label,this.def.icon[i]=o.icon}}label(e){const t=this.def;let n=t.i18n||{};n=n[r().locale]||n.default||n;const o=$.camelCase(e),i="object"==typeof n?n[o]||n[e]:n;if(i)return i;{let n=t.mi18n;return"object"==typeof n&&(n=n[o]||n[e]),n||(n=o),r().get(n)}}get definition(){return{}}icon(e){const t=this.def;return t&&"object"==typeof t.icon?t.icon[e]:t.icon}getRegistered(e=!1){var t;return e?null!==(t=this.templateControlRegister[e])&&void 0!==t?t:void 0:Object.keys(this.customRegister)}getClass(e){var t;return null!==(t=this.templateControlRegister[e])&&void 0!==t?t:void 0}lookup(e){return this.customRegister[e]}}class le{constructor(e={}){const t={layout:I,layoutTemplates:{},controls:{},controlConfig:{},container:!1,dataType:"json",disableHTMLLabels:!1,formData:[],i18n:Object.assign({},se),messages:{formRendered:"Form Rendered",noFormData:"No form data.",other:"Other",selectColor:"Select Color",invalidControl:"Invalid control"},onRender:()=>{},render:!0,sanitizerOptions:{clobberingProtection:{document:!0,form:!1,namespaceAttributes:!0},backendOrder:["dompurify","sanitizer","fallback"]},templates:{},notify:{error:e=>{console.log(e)},success:e=>{console.log(e)},warning:e=>{console.warn(e)}}};this.options=jQuery.extend(!0,t,e),this.instanceContainers=[],(e=>{if("object"!=typeof e)throw"Invalid value given to setSanitizerConfig, expected config object";if(e.hasOwnProperty("clobberingProtection")&&["document","form","namespaceAttributes"].forEach((t=>{e.clobberingProtection.hasOwnProperty(t)&&"boolean"==typeof e.clobberingProtection[t]&&(o.clobberingProtection[t]=e.clobberingProtection[t])})),e.hasOwnProperty("backends")){if("object"!=typeof e.backends)throw"backends config expected to be an Object";Object.keys(e.backends).forEach((t=>o.backends[t]=e.backends[t]))}if(e.hasOwnProperty("backendOrder")){if(o.backendOrder=[],!Array.isArray(e.backendOrder))throw"backendOrder config expected to be an Array of backend keys as strings";e.backendOrder.forEach((e=>{if(!o.backends.hasOwnProperty(e))throw"unknown sanitizer backend "+e;o.backendOrder.push(e)}))}})(this.options.sanitizerOptions),r().current||r().init(this.options.i18n),this.options.formData?this.options.formData=this.parseFormData(this.options.formData):this.options.formData=[],$.controlConfig=e.controlConfig||{},$.loadCustom(e.controls),this.templatedControls=new ae(this.options.templates),"function"!=typeof Element.prototype.appendFormFields&&(Element.prototype.appendFormFields=function(e){Array.isArray(e)||(e=[e]);const t=z.markup("div",null,{className:"rendered-form formbuilder-embedded-bootstrap"});this.appendChild(t),e.forEach((e=>{const[r]=e.className.match(/row-([^\\s]+)/)||[];if(r){const n=this.id?`${this.id}-row-${r}`:`row-${r}`;let o=document.getElementById(n);o||(o=z.markup("div",null,{id:n,className:"row"}),t.appendChild(o)),o.appendChild(e)}else t.appendChild(e);e.dispatchEvent(new Event("fieldRendered",{bubbles:!0,cancelable:!1}))}))}),"function"!=typeof Element.prototype.emptyContainer&&(Element.prototype.emptyContainer=function(){const e=this;for(;e.lastChild;)e.removeChild(e.lastChild)})}sanitizeField(e,t){let r=Object.assign({},e);return t&&(r.id=e.id&&`${e.id}-${t}`,r.name=e.name&&`${e.name}-${t}`),r.className=Array.isArray(e.className)?z.unique(e.className.join(" ").split(" ")).join(" "):e.className||e.class||null,delete r.class,e.values&&(r.values=e.values.map((e=>z.trimObj(e)))),r=z.trimObj(r),Array.isArray(e.userData)&&0===e.userData.length&&(r.userData=[]),r}getElement(e){return(e=this.options.container||e)instanceof jQuery?e=e[0]:"string"==typeof e&&(e=document.querySelector(e)),e}render(e=null,t=0){const r=this,n=this.options;e=this.getElement(e);const o=[],i=new n.layout(n.layoutTemplates,!1,n.disableHTMLLabels,n.controlConfig);if(n.formData.length)for(let e=0;ee.map((e=>e.innerHTML)).join("");r.markup=e(o)}if(!0===n.disableInjectedStyle){const e=document.getElementsByClassName("formBuilder-injected-style");L(e,(t=>(e=>{e.parentNode&&e.parentNode.removeChild(e)})(e[t])))}else"bootstrap"===n.disableInjectedStyle&&n.render&&e&&e.getElementsByClassName("formbuilder-embedded-bootstrap").item(0)?.classList.remove("formbuilder-embedded-bootstrap");return r}renderControl(e=null){const t=this.options,r=t.formData;if(!r||Array.isArray(r))throw new Error("To render a single element, please specify a single object of formData for the field in question");const n=this.sanitizeField(r),o=new t.layout,i=this.templatedControls.getClass(r.type)||$.getClass(r.type,r.subtype),s=t.forceTemplate||"hidden",a=o.build(i,n,s);return e.appendFormFields(a),t.notify.success(t.messages.formRendered),this}get userData(){const t=this.options.formData.slice();return t.filter((e=>"tinymce"===e.subtype)).forEach((e=>window.tinymce.get(e.name).save())),this.instanceContainers.forEach((r=>{const n=e("select, input, textarea",r).serializeArray().reduce(((e,{name:t,value:r})=>(t=t.replace(/\\[\\w*]/,""),e[t]??=[],e[t].push(r),e)),{}),o=t.length;for(let e=0;e{this.options.formData.slice().filter((e=>"tinymce"===e.subtype)).forEach((e=>window.tinymce.get(e.name).setContent(""))),e.querySelectorAll("input, select, textarea").forEach((e=>{["checkbox","radio"].includes(e.type)?e.checked=!1:e.value=""}))}))}parseFormData(e){const t={xml:e=>E(e),json:e=>window.JSON.parse(e)};return"object"!=typeof e&&(e=t[this.options.dataType](e)||!1),e}}!function(e){let t;const r={init:(e,n={})=>(t=e,r.instance=new le(n),e.each((t=>r.instance.render(e[t],t))),r.instance),userData:()=>r.instance&&r.instance.userData,clear:()=>r.instance&&r.instance.clear(),setData:e=>{if(r.instance){const t=r.instance;t.options.formData=t.parseFormData(e)}},render:(e,n={})=>{if(r.instance){const o=r.instance;e||(e=o.options.formData),o.options=Object.assign({},o.options,n,{formData:o.parseFormData(e)}),t.each((e=>r.instance.render(t[e],e)))}},html:()=>t.map((e=>t[e])).html()};e.fn.formRender=function(e={},...t){if(r[e])return r[e].apply(this,t);{const t=r.init(this,e);return Object.assign(r,t),t}},e.fn.controlRender=function(e,t={}){t.formData=e,t.dataType="string"==typeof e?"json":"xml";const r=new le(t),n=this;return n.each((e=>r.renderControl(n[e]))),n}}(jQuery)}()}()}(jQuery);'; export default formRenderSource; diff --git a/src/lib/form-render/jquery-source.ts b/src/lib/form-render/jquery-source.ts index bf319fa..f465cb7 100644 --- a/src/lib/form-render/jquery-source.ts +++ b/src/lib/form-render/jquery-source.ts @@ -1,4 +1,5 @@ // Auto-generated – do not edit manually. // Regenerate via: node scripts/generate-vendor-sources.js -const jquerySource: string = "/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */\n!function(e,t){\"use strict\";\"object\"==typeof module&&\"object\"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error(\"jQuery requires a window with a document\");return t(e)}:t(e)}(\"undefined\"!=typeof window?window:this,function(C,e){\"use strict\";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return\"function\"==typeof e&&\"number\"!=typeof e.nodeType&&\"function\"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement(\"script\");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+\"\":\"object\"==typeof e||\"function\"==typeof e?n[o.call(e)]||\"object\":typeof e}var f=\"3.6.0\",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&\"length\"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&(\"array\"===n||0===t||\"number\"==typeof t&&0+~]|\"+M+\")\"+M+\"*\"),U=new RegExp(M+\"|>\"),X=new RegExp(F),V=new RegExp(\"^\"+I+\"$\"),G={ID:new RegExp(\"^#(\"+I+\")\"),CLASS:new RegExp(\"^\\\\.(\"+I+\")\"),TAG:new RegExp(\"^(\"+I+\"|[*])\"),ATTR:new RegExp(\"^\"+W),PSEUDO:new RegExp(\"^\"+F),CHILD:new RegExp(\"^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\\\(\"+M+\"*(even|odd|(([+-]|)(\\\\d*)n|)\"+M+\"*(?:([+-]|)\"+M+\"*(\\\\d+)|))\"+M+\"*\\\\)|)\",\"i\"),bool:new RegExp(\"^(?:\"+R+\")$\",\"i\"),needsContext:new RegExp(\"^\"+M+\"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\\\(\"+M+\"*((?:-\\\\d)?\\\\d*)\"+M+\"*\\\\)|)(?=[^-]|$)\",\"i\")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\\d$/i,K=/^[^{]+\\{\\s*\\[native \\w/,Z=/^(?:#([\\w-]+)|(\\w+)|\\.([\\w-]+))$/,ee=/[+~]/,te=new RegExp(\"\\\\\\\\[\\\\da-fA-F]{1,6}\"+M+\"?|\\\\\\\\([^\\\\r\\\\n\\\\f])\",\"g\"),ne=function(e,t){var n=\"0x\"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\\0-\\x1f\\x7f]|^-?\\d)|^-$|[^\\0-\\x1f\\x7f-\\uFFFF\\w-]/g,ie=function(e,t){return t?\"\\0\"===e?\"\\ufffd\":e.slice(0,-1)+\"\\\\\"+e.charCodeAt(e.length-1).toString(16)+\" \":\"\\\\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&\"fieldset\"===e.nodeName.toLowerCase()},{dir:\"parentNode\",next:\"legend\"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],\"string\"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+\" \"]&&(!v||!v.test(t))&&(1!==p||\"object\"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute(\"id\"))?s=s.replace(re,ie):e.setAttribute(\"id\",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?\"#\"+s:\":scope\")+\" \"+xe(l[o]);c=l.join(\",\")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute(\"id\")}}}return g(t.replace($,\"$1\"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+\" \")>b.cacheLength&&delete e[r.shift()],e[t+\" \"]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement(\"fieldset\");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split(\"|\"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return\"input\"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return(\"input\"===t||\"button\"===t)&&e.type===n}}function ge(t){return function(e){return\"form\"in e?e.parentNode&&!1===e.disabled?\"label\"in e?\"label\"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:\"label\"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&\"undefined\"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||\"HTML\")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener(\"unload\",oe,!1):n.attachEvent&&n.attachEvent(\"onunload\",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement(\"div\")),\"undefined\"!=typeof e.querySelectorAll&&!e.querySelectorAll(\":scope fieldset div\").length}),d.attributes=ce(function(e){return e.className=\"i\",!e.getAttribute(\"className\")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment(\"\")),!e.getElementsByTagName(\"*\").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute(\"id\")===t}},b.find.ID=function(e,t){if(\"undefined\"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t=\"undefined\"!=typeof e.getAttributeNode&&e.getAttributeNode(\"id\");return t&&t.value===n}},b.find.ID=function(e,t){if(\"undefined\"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode(\"id\"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode(\"id\"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return\"undefined\"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if(\"*\"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if(\"undefined\"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML=\"\",e.querySelectorAll(\"[msallowcapture^='']\").length&&v.push(\"[*^$]=\"+M+\"*(?:''|\\\"\\\")\"),e.querySelectorAll(\"[selected]\").length||v.push(\"\\\\[\"+M+\"*(?:value|\"+R+\")\"),e.querySelectorAll(\"[id~=\"+S+\"-]\").length||v.push(\"~=\"),(t=C.createElement(\"input\")).setAttribute(\"name\",\"\"),e.appendChild(t),e.querySelectorAll(\"[name='']\").length||v.push(\"\\\\[\"+M+\"*name\"+M+\"*=\"+M+\"*(?:''|\\\"\\\")\"),e.querySelectorAll(\":checked\").length||v.push(\":checked\"),e.querySelectorAll(\"a#\"+S+\"+*\").length||v.push(\".#.+[+~]\"),e.querySelectorAll(\"\\\\\\f\"),v.push(\"[\\\\r\\\\n\\\\f]\")}),ce(function(e){e.innerHTML=\"\";var t=C.createElement(\"input\");t.setAttribute(\"type\",\"hidden\"),e.appendChild(t).setAttribute(\"name\",\"D\"),e.querySelectorAll(\"[name=d]\").length&&v.push(\"name\"+M+\"*[*^$|!~]?=\"),2!==e.querySelectorAll(\":enabled\").length&&v.push(\":enabled\",\":disabled\"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(\":disabled\").length&&v.push(\":enabled\",\":disabled\"),e.querySelectorAll(\"*,:x\"),v.push(\",.*:\")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,\"*\"),c.call(e,\"[s!='']:x\"),s.push(\"!=\",F)}),v=v.length&&new RegExp(v.join(\"|\")),s=s.length&&new RegExp(s.join(\"|\")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+\" \"]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0\":{dir:\"parentNode\",first:!0},\" \":{dir:\"parentNode\"},\"+\":{dir:\"previousSibling\",first:!0},\"~\":{dir:\"previousSibling\"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||\"\").replace(te,ne),\"~=\"===e[2]&&(e[3]=\" \"+e[3]+\" \"),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),\"nth\"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*(\"even\"===e[3]||\"odd\"===e[3])),e[5]=+(e[7]+e[8]||\"odd\"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||\"\":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(\")\",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return\"*\"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+\" \"];return t||(t=new RegExp(\"(^|\"+M+\")\"+e+\"(\"+M+\"|$)\"))&&m(e,function(e){return t.test(\"string\"==typeof e.className&&e.className||\"undefined\"!=typeof e.getAttribute&&e.getAttribute(\"class\")||\"\")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?\"!=\"===r:!r||(t+=\"\",\"=\"===r?t===i:\"!=\"===r?t!==i:\"^=\"===r?i&&0===t.indexOf(i):\"*=\"===r?i&&-1\",\"#\"===e.firstChild.getAttribute(\"href\")})||fe(\"type|href|height|width\",function(e,t,n){if(!n)return e.getAttribute(t,\"type\"===t.toLowerCase()?1:2)}),d.attributes&&ce(function(e){return e.innerHTML=\"\",e.firstChild.setAttribute(\"value\",\"\"),\"\"===e.firstChild.getAttribute(\"value\")})||fe(\"value\",function(e,t,n){if(!n&&\"input\"===e.nodeName.toLowerCase())return e.defaultValue}),ce(function(e){return null==e.getAttribute(\"disabled\")})||fe(R,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),se}(C);S.find=d,S.expr=d.selectors,S.expr[\":\"]=S.expr.pseudos,S.uniqueSort=S.unique=d.uniqueSort,S.text=d.getText,S.isXMLDoc=d.isXML,S.contains=d.contains,S.escapeSelector=d.escape;var h=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&S(e).is(n))break;r.push(e)}return r},T=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},k=S.expr.match.needsContext;function A(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var N=/^<([a-z][^\\/\\0>:\\x20\\t\\r\\n\\f]*)[\\x20\\t\\r\\n\\f]*\\/?>(?:<\\/\\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):\"string\"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,\"string\"==typeof e){if(!(r=\"<\"===e[0]&&\">\"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\\x20\\t\\r\\n\\f]*)/i,he=/^$|^module$|\\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement(\"div\")),(fe=E.createElement(\"input\")).setAttribute(\"type\",\"radio\"),fe.setAttribute(\"checked\",\"checked\"),fe.setAttribute(\"name\",\"t\"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML=\"\",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML=\"\",y.option=!!ce.lastChild;var ge={thead:[1,\"\",\"
\"],col:[2,\"\",\"
\"],tr:[2,\"\",\"
\"],td:[3,\"\",\"
\"],_default:[0,\"\",\"\"]};function ve(e,t){var n;return n=\"undefined\"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||\"*\"):\"undefined\"!=typeof e.querySelectorAll?e.querySelectorAll(t||\"*\"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n\",\"\"]);var me=/<|&#?\\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\\s*$/g;function je(e,t){return A(e,\"table\")&&A(11!==t.nodeType?t:t.firstChild,\"tr\")&&S(e).children(\"tbody\")[0]||e}function De(e){return e.type=(null!==e.getAttribute(\"type\"))+\"/\"+e.type,e}function qe(e){return\"true/\"===(e.type||\"\").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute(\"type\"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,\"handle events\"),s)for(n=0,r=s[i].length;n\").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on(\"load error\",i=function(e){r.remove(),i=null,e&&t(\"error\"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\\?(?=&|$)|\\?\\?/;S.ajaxSetup({jsonp:\"callback\",jsonpCallback:function(){var e=zt.pop()||S.expando+\"_\"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter(\"json jsonp\",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?\"url\":\"string\"==typeof e.data&&0===(e.contentType||\"\").indexOf(\"application/x-www-form-urlencoded\")&&Ut.test(e.data)&&\"data\");if(a||\"jsonp\"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,\"$1\"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?\"&\":\"?\")+e.jsonp+\"=\"+r),e.converters[\"script json\"]=function(){return o||S.error(r+\" was not called\"),o[0]},e.dataTypes[0]=\"json\",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),\"script\"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument(\"\").body).innerHTML=\"
\",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return\"string\"!=typeof e?[]:(\"boolean\"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument(\"\")).createElement(\"base\")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(\" \");return-1\").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,\"position\"),c=S(e),f={};\"static\"===l&&(e.style.position=\"relative\"),s=c.offset(),o=S.css(e,\"top\"),u=S.css(e,\"left\"),(\"absolute\"===l||\"fixed\"===l)&&-1<(o+u).indexOf(\"auto\")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),\"using\"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if(\"fixed\"===S.css(r,\"position\"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&\"static\"===S.css(e,\"position\"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,\"borderTopWidth\",!0),i.left+=S.css(e,\"borderLeftWidth\",!0))}return{top:t.top-i.top-S.css(r,\"marginTop\",!0),left:t.left-i.left-S.css(r,\"marginLeft\",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&\"static\"===S.css(e,\"position\"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:\"pageXOffset\",scrollTop:\"pageYOffset\"},function(t,i){var o=\"pageYOffset\"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each([\"top\",\"left\"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+\"px\":t})}),S.each({Height:\"height\",Width:\"width\"},function(a,s){S.each({padding:\"inner\"+a,content:s,\"\":\"outer\"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||\"boolean\"!=typeof e),i=r||(!0===e||!0===t?\"margin\":\"border\");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf(\"outer\")?e[\"inner\"+a]:e.document.documentElement[\"client\"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body[\"scroll\"+a],r[\"scroll\"+a],e.body[\"offset\"+a],r[\"offset\"+a],r[\"client\"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each([\"ajaxStart\",\"ajaxStop\",\"ajaxComplete\",\"ajaxError\",\"ajaxSuccess\",\"ajaxSend\"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,\"**\"):this.off(t,e||\"**\",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each(\"blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu\".split(\" \"),function(e,n){S.fn[n]=function(e,t){return 0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\\\("+M+"*(even|odd|(([+-]|)(\\\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\\\d+)|))"+M+"*\\\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\\\("+M+"*((?:-\\\\d)?\\\\d*)"+M+"*\\\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\\d$/i,K=/^[^{]+\\{\\s*\\[native \\w/,Z=/^(?:#([\\w-]+)|(\\w+)|\\.([\\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\\\\\[\\\\da-fA-F]{1,6}"+M+"?|\\\\\\\\([^\\\\r\\\\n\\\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\\0-\\x1f\\x7f]|^-?\\d)|^-$|[^\\0-\\x1f\\x7f-\\uFFFF\\w-]/g,ie=function(e,t){return t?"\\0"===e?"\\ufffd":e.slice(0,-1)+"\\\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^=\'\']").length&&v.push("[*^$]="+M+"*(?:\'\'|\\"\\")"),e.querySelectorAll("[selected]").length||v.push("\\\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name=\'\']").length||v.push("\\\\["+M+"*name"+M+"*="+M+"*(?:\'\'|\\"\\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\\\\f"),v.push("[\\\\r\\\\n\\\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!=\'\']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\\x20\\t\\r\\n\\f]*)[\\x20\\t\\r\\n\\f]*\\/?>(?:<\\/\\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\\x20\\t\\r\\n\\f]*)/i,he=/^$|^module$|\\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\\?(?=&|$)|\\?\\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0 { + if (!isNative) return; + + try { + if (Platform.OS === 'ios') { + const mod = NativeModules.CheckInLiveActivityModule; + if (mod?.startActivity) { + await mod.startActivity(params); + } + } else if (Platform.OS === 'android') { + const mod = NativeModules.CheckInNotificationModule; + if (mod?.startNotification) { + await mod.startNotification(params); + } + } + } catch (error) { + logger.error({ + message: 'Failed to start check-in live activity', + context: { error, platform: Platform.OS }, + }); + } +} + +/** + * Update the running Live Activity / foreground notification with latest timer data. + * No-op on Web and Electron. + */ +export async function updateCheckInActivity(update: CheckInActivityUpdate): Promise { + if (!isNative) return; + + try { + if (Platform.OS === 'ios') { + const mod = NativeModules.CheckInLiveActivityModule; + if (mod?.updateActivity) { + await mod.updateActivity(update); + } + } else if (Platform.OS === 'android') { + const mod = NativeModules.CheckInNotificationModule; + if (mod?.updateNotification) { + await mod.updateNotification(update); + } + } + } catch (error) { + logger.error({ + message: 'Failed to update check-in live activity', + context: { error, platform: Platform.OS }, + }); + } +} + +/** + * End the Live Activity / foreground notification. + * No-op on Web and Electron. + */ +export async function endCheckInActivity(): Promise { + if (!isNative) return; + + try { + if (Platform.OS === 'ios') { + const mod = NativeModules.CheckInLiveActivityModule; + if (mod?.endActivity) { + await mod.endActivity(); + } + } else if (Platform.OS === 'android') { + const mod = NativeModules.CheckInNotificationModule; + if (mod?.stopNotification) { + await mod.stopNotification(); + } + } + } catch (error) { + logger.error({ + message: 'Failed to end check-in live activity', + context: { error, platform: Platform.OS }, + }); + } +} diff --git a/src/lib/offline/checkInQueue.ts b/src/lib/offline/checkInQueue.ts new file mode 100644 index 0000000..fd89b9c --- /dev/null +++ b/src/lib/offline/checkInQueue.ts @@ -0,0 +1,39 @@ +import { type PerformCheckInInput } from '@/api/checkIn/checkInTimers'; +import { storage } from '@/lib/storage'; + +const QUEUE_KEY = 'checkIn:pendingQueue'; + +export interface QueuedCheckIn { + input: PerformCheckInInput; + queuedAt: number; +} + +export function getQueuedCheckIns(): QueuedCheckIn[] { + const raw = storage.getString(QUEUE_KEY); + if (!raw) return []; + try { + return JSON.parse(raw) as QueuedCheckIn[]; + } catch { + return []; + } +} + +export function enqueueCheckIn(input: PerformCheckInInput): void { + const queue = getQueuedCheckIns(); + queue.push({ input, queuedAt: Date.now() }); + storage.set(QUEUE_KEY, JSON.stringify(queue)); +} + +export function dequeueCheckIn(queuedAt: number): void { + const queue = getQueuedCheckIns(); + const filtered = queue.filter((item) => item.queuedAt !== queuedAt); + storage.set(QUEUE_KEY, JSON.stringify(filtered)); +} + +export function clearCheckInQueue(): void { + storage.delete(QUEUE_KEY); +} + +export function getQueueLength(): number { + return getQueuedCheckIns().length; +} diff --git a/src/lib/offline/checkInQueueProcessor.ts b/src/lib/offline/checkInQueueProcessor.ts new file mode 100644 index 0000000..4e494b3 --- /dev/null +++ b/src/lib/offline/checkInQueueProcessor.ts @@ -0,0 +1,105 @@ +import { Platform } from 'react-native'; + +import { performCheckIn } from '@/api/checkIn/checkInTimers'; +import { logger } from '@/lib/logging'; + +import { dequeueCheckIn, getQueuedCheckIns } from './checkInQueue'; + +// Conditionally require NetInfo only on native platforms +// eslint-disable-next-line @typescript-eslint/no-var-requires +const NetInfo = Platform.OS !== 'web' ? require('@react-native-community/netinfo').default : null; + +let isProcessing = false; + +async function isConnected(): Promise { + if (Platform.OS === 'web') { + return typeof navigator !== 'undefined' ? navigator.onLine : true; + } + if (NetInfo) { + try { + const state = await NetInfo.fetch(); + return !!state.isConnected; + } catch (error) { + logger.error({ message: 'NetInfo.fetch() failed, assuming offline', context: { error } }); + return false; + } + } + return true; +} + +export async function processCheckInQueue(): Promise { + if (isProcessing) return 0; + + const connected = await isConnected(); + if (!connected) return 0; + + isProcessing = true; + let processed = 0; + + try { + const queue = getQueuedCheckIns(); + for (const item of queue) { + try { + await performCheckIn(item.input); + dequeueCheckIn(item.queuedAt); + processed++; + } catch (error) { + logger.error({ + message: 'Failed to process queued check-in', + context: { error, input: item.input }, + }); + break; + } + } + } finally { + isProcessing = false; + } + + return processed; +} + +let cleanup: (() => void) | null = null; + +export function startQueueListener(): void { + if (cleanup) return; + + if (Platform.OS === 'web') { + // Web/Electron: use browser online/offline events + const handler = () => { + if (navigator.onLine) { + processCheckInQueue().catch(() => {}); + } + }; + window.addEventListener('online', handler); + cleanup = () => window.removeEventListener('online', handler); + + // Drain existing backlog if already online + if (typeof navigator !== 'undefined' && navigator.onLine) { + processCheckInQueue().catch(() => {}); + } + } else if (NetInfo) { + // Native: use NetInfo listener + const unsubscribe = NetInfo.addEventListener((state: { isConnected: boolean }) => { + if (state.isConnected) { + processCheckInQueue().catch(() => {}); + } + }); + cleanup = unsubscribe; + + // Drain existing backlog if already connected + NetInfo.fetch() + .then((state: { isConnected: boolean }) => { + if (state.isConnected) { + processCheckInQueue().catch(() => {}); + } + }) + .catch(() => {}); + } +} + +export function stopQueueListener(): void { + if (cleanup) { + cleanup(); + cleanup = null; + } +} diff --git a/src/lib/storage/index.web.tsx b/src/lib/storage/index.web.tsx index 479c2a6..4a79709 100644 --- a/src/lib/storage/index.web.tsx +++ b/src/lib/storage/index.web.tsx @@ -32,6 +32,28 @@ export const storage: any = { console.warn('localStorage.removeItem failed', e); } }, + getAllKeys: (): string[] => { + if (!isLocalStorageAvailable) return []; + try { + const keys: string[] = []; + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + if (key) keys.push(key); + } + return keys; + } catch (e) { + console.warn('localStorage.getAllKeys failed', e); + return []; + } + }, + contains: (key: string): boolean => { + if (!isLocalStorageAvailable) return false; + try { + return localStorage.getItem(key) !== null; + } catch (e) { + return false; + } + }, }; export function getItem(key: string): T | null { diff --git a/src/models/v4/callVideoFeeds/callVideoFeedEnums.ts b/src/models/v4/callVideoFeeds/callVideoFeedEnums.ts new file mode 100644 index 0000000..97b2e55 --- /dev/null +++ b/src/models/v4/callVideoFeeds/callVideoFeedEnums.ts @@ -0,0 +1,27 @@ +export enum CallVideoFeedFormat { + RTSP = 0, + HLS = 1, + MJPEG = 2, + YouTubeLive = 3, + WebRTC = 4, + DASH = 5, + Embed = 6, + Other = 99, +} + +export enum CallVideoFeedType { + Drone = 0, + FixedCamera = 1, + BodyCam = 2, + TrafficCam = 3, + WeatherCam = 4, + SatelliteFeed = 5, + WebCam = 6, + Other = 99, +} + +export enum CallVideoFeedStatus { + Active = 0, + Inactive = 1, + Error = 2, +} diff --git a/src/models/v4/callVideoFeeds/callVideoFeedResultData.ts b/src/models/v4/callVideoFeeds/callVideoFeedResultData.ts new file mode 100644 index 0000000..dd11738 --- /dev/null +++ b/src/models/v4/callVideoFeeds/callVideoFeedResultData.ts @@ -0,0 +1,17 @@ +export class CallVideoFeedResultData { + public CallVideoFeedId: string = ''; + public CallId: string = ''; + public Name: string = ''; + public Url: string = ''; + public FeedType: number | null = null; + public FeedFormat: number | null = null; + public Description: string = ''; + public Status: number = 0; + public Latitude: number | null = null; + public Longitude: number | null = null; + public AddedByUserId: string = ''; + public AddedOnFormatted: string = ''; + public AddedOnUtc: string = ''; + public SortOrder: number = 0; + public FullName: string = ''; +} diff --git a/src/models/v4/callVideoFeeds/callVideoFeedsResult.ts b/src/models/v4/callVideoFeeds/callVideoFeedsResult.ts new file mode 100644 index 0000000..b8da4b2 --- /dev/null +++ b/src/models/v4/callVideoFeeds/callVideoFeedsResult.ts @@ -0,0 +1,6 @@ +import { BaseV4Request } from '../baseV4Request'; +import { type CallVideoFeedResultData } from './callVideoFeedResultData'; + +export class CallVideoFeedsResult extends BaseV4Request { + public Data: CallVideoFeedResultData[] = []; +} diff --git a/src/models/v4/callVideoFeeds/deleteCallVideoFeedResult.ts b/src/models/v4/callVideoFeeds/deleteCallVideoFeedResult.ts new file mode 100644 index 0000000..576cb2c --- /dev/null +++ b/src/models/v4/callVideoFeeds/deleteCallVideoFeedResult.ts @@ -0,0 +1,3 @@ +import { BaseV4Request } from '../baseV4Request'; + +export class DeleteCallVideoFeedResult extends BaseV4Request {} diff --git a/src/models/v4/callVideoFeeds/saveCallVideoFeedResult.ts b/src/models/v4/callVideoFeeds/saveCallVideoFeedResult.ts new file mode 100644 index 0000000..ad1acbd --- /dev/null +++ b/src/models/v4/callVideoFeeds/saveCallVideoFeedResult.ts @@ -0,0 +1,5 @@ +import { BaseV4Request } from '../baseV4Request'; + +export class SaveCallVideoFeedResult extends BaseV4Request { + public Id: string = ''; +} diff --git a/src/models/v4/calls/callResultData.ts b/src/models/v4/calls/callResultData.ts index f49065e..29adc31 100644 --- a/src/models/v4/calls/callResultData.ts +++ b/src/models/v4/calls/callResultData.ts @@ -27,4 +27,5 @@ export class CallResultData { public DispatchedOnUtc: string = ''; public Latitude: string = ''; public Longitude: string = ''; + public CheckInTimersEnabled: boolean = false; } diff --git a/src/models/v4/checkIn/checkInRecordResult.ts b/src/models/v4/checkIn/checkInRecordResult.ts new file mode 100644 index 0000000..2bd9d9f --- /dev/null +++ b/src/models/v4/checkIn/checkInRecordResult.ts @@ -0,0 +1,6 @@ +import { BaseV4Request } from '../baseV4Request'; +import { type CheckInRecordResultData } from './checkInRecordResultData'; + +export class CheckInRecordResult extends BaseV4Request { + public Data: CheckInRecordResultData[] = []; +} diff --git a/src/models/v4/checkIn/checkInRecordResultData.ts b/src/models/v4/checkIn/checkInRecordResultData.ts new file mode 100644 index 0000000..b54fa12 --- /dev/null +++ b/src/models/v4/checkIn/checkInRecordResultData.ts @@ -0,0 +1,12 @@ +export class CheckInRecordResultData { + public CheckInRecordId: string = ''; + public CallId: number = 0; + public CheckInType: number = 0; + public CheckInTypeName: string = ''; + public UserId: string = ''; + public UnitId: number = 0; + public Latitude: string = ''; + public Longitude: string = ''; + public Timestamp: string = ''; + public Note: string = ''; +} diff --git a/src/models/v4/checkIn/checkInTimerStatusResult.ts b/src/models/v4/checkIn/checkInTimerStatusResult.ts new file mode 100644 index 0000000..677c89c --- /dev/null +++ b/src/models/v4/checkIn/checkInTimerStatusResult.ts @@ -0,0 +1,6 @@ +import { BaseV4Request } from '../baseV4Request'; +import { type CheckInTimerStatusResultData } from './checkInTimerStatusResultData'; + +export class CheckInTimerStatusResult extends BaseV4Request { + public Data: CheckInTimerStatusResultData[] = []; +} diff --git a/src/models/v4/checkIn/checkInTimerStatusResultData.ts b/src/models/v4/checkIn/checkInTimerStatusResultData.ts new file mode 100644 index 0000000..b9ab368 --- /dev/null +++ b/src/models/v4/checkIn/checkInTimerStatusResultData.ts @@ -0,0 +1,14 @@ +export class CheckInTimerStatusResultData { + public TargetType: number = 0; + public TargetTypeName: string = ''; + public TargetEntityId: string = ''; + public TargetName: string = ''; + public UnitId: number = 0; + public LastCheckIn: string = ''; + public DurationMinutes: number = 0; + public WarningThresholdMinutes: number = 0; + public ElapsedMinutes: number = 0; + public Status: string = ''; + /** Injected client-side when aggregating across calls */ + public CallId: number = 0; +} diff --git a/src/models/v4/checkIn/performCheckInResult.ts b/src/models/v4/checkIn/performCheckInResult.ts new file mode 100644 index 0000000..9669971 --- /dev/null +++ b/src/models/v4/checkIn/performCheckInResult.ts @@ -0,0 +1,5 @@ +import { BaseV4Request } from '../baseV4Request'; + +export class PerformCheckInResult extends BaseV4Request { + public Data: boolean = false; +} diff --git a/src/models/v4/checkIn/resolvedCheckInTimerResult.ts b/src/models/v4/checkIn/resolvedCheckInTimerResult.ts new file mode 100644 index 0000000..40e4fd8 --- /dev/null +++ b/src/models/v4/checkIn/resolvedCheckInTimerResult.ts @@ -0,0 +1,6 @@ +import { BaseV4Request } from '../baseV4Request'; +import { type ResolvedCheckInTimerResultData } from './resolvedCheckInTimerResultData'; + +export class ResolvedCheckInTimerResult extends BaseV4Request { + public Data: ResolvedCheckInTimerResultData[] = []; +} diff --git a/src/models/v4/checkIn/resolvedCheckInTimerResultData.ts b/src/models/v4/checkIn/resolvedCheckInTimerResultData.ts new file mode 100644 index 0000000..18906a1 --- /dev/null +++ b/src/models/v4/checkIn/resolvedCheckInTimerResultData.ts @@ -0,0 +1,11 @@ +export class ResolvedCheckInTimerResultData { + public TargetType: number = 0; + public TargetTypeName: string = ''; + public UnitTypeId: number = 0; + public TargetEntityId: string = ''; + public TargetName: string = ''; + public DurationMinutes: number = 0; + public WarningThresholdMinutes: number = 0; + public IsFromOverride: boolean = false; + public ActiveForStates: string = ''; +} diff --git a/src/models/v4/weatherAlerts/activeWeatherAlertsResult.ts b/src/models/v4/weatherAlerts/activeWeatherAlertsResult.ts new file mode 100644 index 0000000..d4361cc --- /dev/null +++ b/src/models/v4/weatherAlerts/activeWeatherAlertsResult.ts @@ -0,0 +1,6 @@ +import { BaseV4Request } from '../baseV4Request'; +import { type WeatherAlertResultData } from './weatherAlertResultData'; + +export class ActiveWeatherAlertsResult extends BaseV4Request { + public Data: WeatherAlertResultData[] = []; +} diff --git a/src/models/v4/weatherAlerts/weatherAlertEnums.ts b/src/models/v4/weatherAlerts/weatherAlertEnums.ts new file mode 100644 index 0000000..f433eac --- /dev/null +++ b/src/models/v4/weatherAlerts/weatherAlertEnums.ts @@ -0,0 +1,52 @@ +export enum WeatherAlertSeverity { + Extreme = 0, + Severe = 1, + Moderate = 2, + Minor = 3, + Unknown = 4, +} + +export enum WeatherAlertCategory { + Met = 0, + Fire = 1, + Health = 2, + Env = 3, + Other = 4, +} + +export enum WeatherAlertUrgency { + Immediate = 0, + Expected = 1, + Future = 2, + Past = 3, + Unknown = 4, +} + +export enum WeatherAlertCertainty { + Observed = 0, + Likely = 1, + Possible = 2, + Unlikely = 3, + Unknown = 4, +} + +export enum WeatherAlertStatus { + Active = 0, + Updated = 1, + Expired = 2, + Cancelled = 3, +} + +export enum WeatherAlertSourceType { + NationalWeatherService = 0, + EnvironmentCanada = 1, + MeteoAlarm = 2, +} + +export const SEVERITY_COLORS: Record = { + [WeatherAlertSeverity.Extreme]: '#7B1FA2', + [WeatherAlertSeverity.Severe]: '#D32F2F', + [WeatherAlertSeverity.Moderate]: '#F57C00', + [WeatherAlertSeverity.Minor]: '#FBC02D', + [WeatherAlertSeverity.Unknown]: '#9E9E9E', +}; diff --git a/src/models/v4/weatherAlerts/weatherAlertResult.ts b/src/models/v4/weatherAlerts/weatherAlertResult.ts new file mode 100644 index 0000000..8920f49 --- /dev/null +++ b/src/models/v4/weatherAlerts/weatherAlertResult.ts @@ -0,0 +1,6 @@ +import { BaseV4Request } from '../baseV4Request'; +import { WeatherAlertResultData } from './weatherAlertResultData'; + +export class WeatherAlertResult extends BaseV4Request { + public Data: WeatherAlertResultData = new WeatherAlertResultData(); +} diff --git a/src/models/v4/weatherAlerts/weatherAlertResultData.ts b/src/models/v4/weatherAlerts/weatherAlertResultData.ts new file mode 100644 index 0000000..370f524 --- /dev/null +++ b/src/models/v4/weatherAlerts/weatherAlertResultData.ts @@ -0,0 +1,29 @@ +export class WeatherAlertResultData { + public WeatherAlertId: string = ''; + public DepartmentId: number = 0; + public WeatherAlertSourceId: string = ''; + public ExternalId: string = ''; + public Sender: string = ''; + public Event: string = ''; + public AlertCategory: number = 0; + public Severity: number = 0; + public Urgency: number = 0; + public Certainty: number = 0; + public Status: number = 0; + public Headline: string = ''; + public Description: string = ''; + public Instruction: string = ''; + public AreaDescription: string = ''; + public Polygon: string = ''; + public Geocodes: string = ''; + public CenterGeoLocation: string = ''; + public OnsetUtc: string | null = null; + public ExpiresUtc: string | null = null; + public EffectiveUtc: string = ''; + public SentUtc: string | null = null; + public FirstSeenUtc: string = ''; + public LastUpdatedUtc: string = ''; + public ReferencesExternalId: string = ''; + public NotificationSent: boolean = false; + public SystemMessageId: number | null = null; +} diff --git a/src/models/v4/weatherAlerts/weatherAlertSettingsData.ts b/src/models/v4/weatherAlerts/weatherAlertSettingsData.ts new file mode 100644 index 0000000..ad6e481 --- /dev/null +++ b/src/models/v4/weatherAlerts/weatherAlertSettingsData.ts @@ -0,0 +1,15 @@ +export interface WeatherAlertSeverityScheduleData { + Severity: number; + Enabled: boolean; + StartHour: number; + EndHour: number; +} + +export interface WeatherAlertSettingsData { + WeatherAlertsEnabled: boolean; + MinimumSeverity: number; + AutoMessageSeverity: number; + CallIntegrationEnabled: boolean; + AutoMessageSchedule: WeatherAlertSeverityScheduleData[]; + ExcludedEvents: string; +} diff --git a/src/models/v4/weatherAlerts/weatherAlertSettingsResult.ts b/src/models/v4/weatherAlerts/weatherAlertSettingsResult.ts new file mode 100644 index 0000000..67b71c4 --- /dev/null +++ b/src/models/v4/weatherAlerts/weatherAlertSettingsResult.ts @@ -0,0 +1,6 @@ +import { BaseV4Request } from '../baseV4Request'; +import { type WeatherAlertSettingsData } from './weatherAlertSettingsData'; + +export class WeatherAlertSettingsResult extends BaseV4Request { + public Data: WeatherAlertSettingsData | null = null; +} diff --git a/src/models/v4/weatherAlerts/weatherAlertZoneResultData.ts b/src/models/v4/weatherAlerts/weatherAlertZoneResultData.ts new file mode 100644 index 0000000..2aa5666 --- /dev/null +++ b/src/models/v4/weatherAlerts/weatherAlertZoneResultData.ts @@ -0,0 +1,10 @@ +export class WeatherAlertZoneResultData { + public WeatherAlertZoneId: string = ''; + public DepartmentId: number = 0; + public Name: string = ''; + public ZoneCode: string = ''; + public CenterGeoLocation: string = ''; + public RadiusMiles: number = 0; + public IsActive: boolean = false; + public IsPrimary: boolean = false; +} diff --git a/src/models/v4/weatherAlerts/weatherAlertZonesResult.ts b/src/models/v4/weatherAlerts/weatherAlertZonesResult.ts new file mode 100644 index 0000000..5dec4da --- /dev/null +++ b/src/models/v4/weatherAlerts/weatherAlertZonesResult.ts @@ -0,0 +1,6 @@ +import { BaseV4Request } from '../baseV4Request'; +import { type WeatherAlertZoneResultData } from './weatherAlertZoneResultData'; + +export class WeatherAlertZonesResult extends BaseV4Request { + public Data: WeatherAlertZoneResultData[] = []; +} diff --git a/src/stores/callVideoFeeds/store.ts b/src/stores/callVideoFeeds/store.ts new file mode 100644 index 0000000..fdb1be1 --- /dev/null +++ b/src/stores/callVideoFeeds/store.ts @@ -0,0 +1,103 @@ +import { create } from 'zustand'; + +import { deleteCallVideoFeed, editCallVideoFeed, type EditCallVideoFeedInput, getCallVideoFeeds, saveCallVideoFeed, type SaveCallVideoFeedInput } from '@/api/callVideoFeeds/callVideoFeeds'; +import { logger } from '@/lib/logging'; +import { type CallVideoFeedResultData } from '@/models/v4/callVideoFeeds/callVideoFeedResultData'; + +interface CallVideoFeedsState { + feeds: CallVideoFeedResultData[]; + isLoading: boolean; + error: string | null; + isSaving: boolean; + _currentFetchId: number; + + fetchFeeds: (callId: string) => Promise; + addFeed: (input: SaveCallVideoFeedInput) => Promise; + updateFeed: (input: EditCallVideoFeedInput) => Promise; + removeFeed: (feedId: string, callId: string) => Promise; + reset: () => void; +} + +export const useCallVideoFeedsStore = create((set, get) => ({ + feeds: [], + isLoading: false, + error: null, + isSaving: false, + _currentFetchId: 0, + + fetchFeeds: async (callId: string) => { + const fetchId = get()._currentFetchId + 1; + set({ isLoading: true, error: null, _currentFetchId: fetchId }); + try { + const result = await getCallVideoFeeds(callId); + if (get()._currentFetchId !== fetchId) return; // stale response + const feeds = (result.Data || []).sort((a, b) => a.SortOrder - b.SortOrder); + set({ feeds, isLoading: false }); + } catch (error) { + if (get()._currentFetchId !== fetchId) return; // stale error + logger.error({ message: 'Failed to fetch video feeds', context: { error, callId } }); + set({ + error: error instanceof Error ? error.message : 'Failed to fetch video feeds', + isLoading: false, + }); + } + }, + + addFeed: async (input: SaveCallVideoFeedInput) => { + set({ isSaving: true }); + try { + const result = await saveCallVideoFeed(input); + // Re-fetch to get the full feed data + await get().fetchFeeds(input.CallId); + set({ isSaving: false }); + return result.Id || null; + } catch (error) { + logger.error({ message: 'Failed to add video feed', context: { error } }); + set({ error: error instanceof Error ? error.message : 'Failed to add video feed', isSaving: false }); + return null; + } + }, + + updateFeed: async (input: EditCallVideoFeedInput) => { + set({ isSaving: true }); + try { + await editCallVideoFeed(input); + await get().fetchFeeds(input.CallId); + set({ isSaving: false }); + return true; + } catch (error) { + logger.error({ message: 'Failed to update video feed', context: { error } }); + set({ error: error instanceof Error ? error.message : 'Failed to update video feed', isSaving: false }); + return false; + } + }, + + removeFeed: async (feedId: string, callId: string) => { + // Optimistic removal + set({ feeds: get().feeds.filter((f) => f.CallVideoFeedId !== feedId), error: null }); + try { + await deleteCallVideoFeed(feedId); + return true; + } catch (error) { + logger.error({ message: 'Failed to delete video feed', context: { error, feedId } }); + set({ error: error instanceof Error ? error.message : 'Failed to delete video feed' }); + // Re-sync from server instead of reverting a potentially stale snapshot + try { + await get().fetchFeeds(callId); + } catch { + // fetchFeeds handles its own error state; nothing else to do here + } + return false; + } + }, + + reset: () => { + set({ + feeds: [], + isLoading: false, + error: null, + isSaving: false, + _currentFetchId: 0, + }); + }, +})); diff --git a/src/stores/checkIn/store.ts b/src/stores/checkIn/store.ts new file mode 100644 index 0000000..a208a39 --- /dev/null +++ b/src/stores/checkIn/store.ts @@ -0,0 +1,298 @@ +import { create } from 'zustand'; + +import { getCheckInHistory, getTimersForCall, getTimerStatuses, getTimerStatusesForCalls, performCheckIn, type PerformCheckInInput, toggleCallTimers } from '@/api/checkIn/checkInTimers'; +import { type CheckInRecordResultData } from '@/models/v4/checkIn/checkInRecordResultData'; +import { type CheckInTimerStatusResultData } from '@/models/v4/checkIn/checkInTimerStatusResultData'; +import { type ResolvedCheckInTimerResultData } from '@/models/v4/checkIn/resolvedCheckInTimerResultData'; +import { usePersonnelStore } from '@/stores/personnel/store'; +import { useUnitsStore } from '@/stores/units/store'; + +/** + * Enrich timer statuses with TargetName from: + * 1. Resolved timers (same endpoint family) + * 2. Units store (by every possible ID match) + * 3. Personnel store (by every possible ID match) + */ +function enrichTimerNames( + statuses: CheckInTimerStatusResultData[], + resolved: ResolvedCheckInTimerResultData[] +): CheckInTimerStatusResultData[] { + // Build name lookup from resolved timers + const resolvedNameMap = new Map(); + for (const r of resolved) { + if (r.TargetName) { + if (r.TargetEntityId) resolvedNameMap.set(`${r.TargetType}-${r.TargetEntityId}`, r.TargetName); + // Also key by just TargetType for type-level timers + if (!resolvedNameMap.has(`type-${r.TargetType}`)) resolvedNameMap.set(`type-${r.TargetType}`, r.TargetName); + } + } + + // Get units and personnel from their stores for name lookup + const units = useUnitsStore.getState().units; + const personnel = usePersonnelStore.getState().personnel; + + // Build fast lookup maps for units + const unitByIdStr = new Map(); // UnitId string → Name + const unitByIdNum = new Map(); // parsed numeric UnitId → Name + for (const u of units) { + if (u.Name) { + unitByIdStr.set(u.UnitId, u.Name); + const num = parseInt(u.UnitId, 10); + if (!isNaN(num)) unitByIdNum.set(num, u.Name); + } + } + + // Build fast lookup maps for personnel + const personnelByUserId = new Map(); // UserId → full name + const personnelByIdNum = new Map(); // parsed numeric UserId → full name + for (const p of personnel) { + const fullName = `${p.FirstName || ''} ${p.LastName || ''}`.trim(); + if (fullName) { + personnelByUserId.set(p.UserId, fullName); + if (p.IdentificationNumber) personnelByUserId.set(p.IdentificationNumber, fullName); + const num = parseInt(p.UserId, 10); + if (!isNaN(num)) personnelByIdNum.set(num, fullName); + } + } + + return statuses.map((s) => { + // Skip if TargetName is a real entity name (not a type label like "UnitType") + const nameIsTypeLabel = !s.TargetName + || /type$/i.test(s.TargetName) + || s.TargetName === s.TargetTypeName; + if (s.TargetName && !nameIsTypeLabel) return s; + + let name: string | undefined; + + // 1. Try resolved timers + if (s.TargetEntityId) { + name = resolvedNameMap.get(`${s.TargetType}-${s.TargetEntityId}`); + } + if (!name) { + name = resolvedNameMap.get(`type-${s.TargetType}`); + } + + // 2. Try units store (by TargetEntityId string, UnitId number, and cross-parsed) + if (!name && s.TargetEntityId) { + name = unitByIdStr.get(s.TargetEntityId); + } + if (!name && s.UnitId > 0) { + name = unitByIdNum.get(s.UnitId) || unitByIdStr.get(String(s.UnitId)); + } + if (!name && s.TargetEntityId) { + const parsed = parseInt(s.TargetEntityId, 10); + if (!isNaN(parsed)) name = unitByIdNum.get(parsed); + } + + // 3. Try personnel store (by TargetEntityId string and cross-parsed) + if (!name && s.TargetEntityId) { + name = personnelByUserId.get(s.TargetEntityId); + } + if (!name && s.TargetEntityId) { + const parsed = parseInt(s.TargetEntityId, 10); + if (!isNaN(parsed)) name = personnelByIdNum.get(parsed); + } + + return name ? { ...s, TargetName: name } : s; + }); +} + +const STATUS_SEVERITY: Record = { + Critical: 0, + Overdue: 1, + Red: 1, + Warning: 2, + Yellow: 2, + Ok: 3, + Green: 3, +}; + +function sortByStatusSeverity(a: CheckInTimerStatusResultData, b: CheckInTimerStatusResultData): number { + return (STATUS_SEVERITY[a.Status] ?? 3) - (STATUS_SEVERITY[b.Status] ?? 3); +} + +interface CheckInState { + timerStatuses: CheckInTimerStatusResultData[]; + resolvedTimers: ResolvedCheckInTimerResultData[]; + checkInHistory: CheckInRecordResultData[]; + + isLoadingStatuses: boolean; + statusError: string | null; + isLoadingHistory: boolean; + isCheckingIn: boolean; + checkInError: string | null; + + _pollingInterval: ReturnType | null; + _pollingCallIds: number[]; + + fetchTimerStatuses: (callId: number) => Promise; + fetchTimerStatusesForCalls: (callIds: number[]) => Promise; + fetchResolvedTimers: (callId: number) => Promise; + fetchCheckInHistory: (callId: number) => Promise; + performCheckIn: (input: PerformCheckInInput) => Promise; + toggleTimers: (callId: number, enabled: boolean) => Promise; + startPolling: (callId: number, intervalMs?: number) => void; + startPollingForCalls: (callIds: number[], intervalMs?: number) => void; + stopPolling: () => void; + reset: () => void; +} + +export const useCheckInStore = create((set, get) => ({ + timerStatuses: [], + resolvedTimers: [], + checkInHistory: [], + isLoadingStatuses: false, + statusError: null, + isLoadingHistory: false, + isCheckingIn: false, + checkInError: null, + _pollingInterval: null, + _pollingCallIds: [], + + fetchTimerStatuses: async (callId: number) => { + set({ isLoadingStatuses: true, statusError: null }); + try { + const [statusResult, resolvedResult] = await Promise.all([ + getTimerStatuses(callId), + getTimersForCall(callId).catch(() => ({ Data: [] as ResolvedCheckInTimerResultData[] })), + ]); + const enriched = enrichTimerNames(statusResult.Data || [], resolvedResult.Data || []); + const sorted = enriched.sort(sortByStatusSeverity); + set({ timerStatuses: sorted, resolvedTimers: resolvedResult.Data || [], isLoadingStatuses: false }); + } catch (error) { + set({ + statusError: error instanceof Error ? error.message : 'Failed to fetch timer statuses', + isLoadingStatuses: false, + }); + } + }, + + fetchTimerStatusesForCalls: async (callIds: number[]) => { + if (callIds.length === 0) { + set({ timerStatuses: [], resolvedTimers: [], isLoadingStatuses: false, statusError: null }); + return; + } + set({ isLoadingStatuses: true, statusError: null }); + try { + const [statusResult, ...resolvedResults] = await Promise.all([ + getTimerStatusesForCalls(callIds), + ...callIds.map((id) => getTimersForCall(id).catch(() => ({ Data: [] as ResolvedCheckInTimerResultData[] }))), + ]); + const allResolved = resolvedResults.flatMap((r) => r.Data || []); + const enriched = enrichTimerNames(statusResult.Data || [], allResolved); + const sorted = enriched.sort(sortByStatusSeverity); + set({ timerStatuses: sorted, resolvedTimers: allResolved, isLoadingStatuses: false }); + } catch (error) { + set({ + statusError: error instanceof Error ? error.message : 'Failed to fetch timer statuses', + isLoadingStatuses: false, + }); + } + }, + + fetchResolvedTimers: async (callId: number) => { + try { + const result = await getTimersForCall(callId); + set({ resolvedTimers: result.Data || [] }); + } catch { + // silent fail — resolved timers are supplementary + } + }, + + fetchCheckInHistory: async (callId: number) => { + set({ isLoadingHistory: true }); + try { + const result = await getCheckInHistory(callId); + set({ checkInHistory: result.Data || [], isLoadingHistory: false }); + } catch { + set({ checkInHistory: [], isLoadingHistory: false }); + } + }, + + performCheckIn: async (input: PerformCheckInInput) => { + set({ isCheckingIn: true, checkInError: null }); + try { + await performCheckIn(input); + // Refresh statuses after successful check-in + const pollingCallIds = get()._pollingCallIds; + if (pollingCallIds.length > 0) { + await get().fetchTimerStatusesForCalls(pollingCallIds); + } else { + await get().fetchTimerStatuses(input.CallId); + } + set({ isCheckingIn: false }); + return true; + } catch (error) { + set({ + checkInError: error instanceof Error ? error.message : 'Failed to perform check-in', + isCheckingIn: false, + }); + return false; + } + }, + + toggleTimers: async (callId: number, enabled: boolean) => { + try { + await toggleCallTimers(callId, enabled); + await get().fetchTimerStatuses(callId); + return true; + } catch { + return false; + } + }, + + startPolling: (callId: number, intervalMs: number = 30000) => { + const existing = get()._pollingInterval; + if (existing) { + clearInterval(existing); + } + set({ _pollingCallIds: [callId] }); + const interval = setInterval(() => { + get().fetchTimerStatuses(callId); + }, intervalMs); + set({ _pollingInterval: interval }); + }, + + startPollingForCalls: (callIds: number[], intervalMs: number = 30000) => { + const existing = get()._pollingInterval; + if (existing) { + clearInterval(existing); + } + if (callIds.length === 0) { + set({ _pollingInterval: null, _pollingCallIds: [] }); + return; + } + set({ _pollingCallIds: callIds }); + const interval = setInterval(() => { + get().fetchTimerStatusesForCalls(callIds); + }, intervalMs); + set({ _pollingInterval: interval }); + }, + + stopPolling: () => { + const interval = get()._pollingInterval; + if (interval) { + clearInterval(interval); + set({ _pollingInterval: null, _pollingCallIds: [] }); + } + }, + + reset: () => { + const interval = get()._pollingInterval; + if (interval) { + clearInterval(interval); + } + set({ + timerStatuses: [], + resolvedTimers: [], + checkInHistory: [], + isLoadingStatuses: false, + statusError: null, + isLoadingHistory: false, + isCheckingIn: false, + checkInError: null, + _pollingInterval: null, + _pollingCallIds: [], + }); + }, +})); diff --git a/src/stores/dispatch/store.ts b/src/stores/dispatch/store.ts index 3de4005..8863685 100644 --- a/src/stores/dispatch/store.ts +++ b/src/stores/dispatch/store.ts @@ -1,15 +1,8 @@ import { create } from 'zustand'; import { getAllGroups } from '@/api/groups/groups'; -import { getRecipients } from '@/api/messaging/messages'; import { getAllPersonnelInfos } from '@/api/personnel/personnel'; -import { getAllUnitRolesAndAssignmentsForDepartment } from '@/api/units/unitRoles'; import { getUnits } from '@/api/units/units'; -import { type GroupResultData } from '@/models/v4/groups/groupsResultData'; -import { type RecipientsResultData } from '@/models/v4/messages/recipientsResultData'; -import { type PersonnelInfoResultData } from '@/models/v4/personnel/personnelInfoResultData'; -import { type UnitRoleResultData } from '@/models/v4/unitRoles/unitRoleResultData'; -import { type UnitResultData } from '@/models/v4/units/unitResultData'; export interface DispatchSelection { everyone: boolean; @@ -19,11 +12,16 @@ export interface DispatchSelection { units: string[]; } +export interface DispatchItem { + Id: string; + Name: string; +} + export interface DispatchData { - users: RecipientsResultData[]; - groups: RecipientsResultData[]; - roles: RecipientsResultData[]; - units: RecipientsResultData[]; + users: DispatchItem[]; + groups: DispatchItem[]; + roles: DispatchItem[]; + units: DispatchItem[]; } interface DispatchState { @@ -67,37 +65,45 @@ export const useDispatchStore = create((set, get) => ({ fetchDispatchData: async () => { set({ isLoading: true, error: null }); try { - const recipients = await getRecipients(false, true); - - // Initialize arrays for categorized recipients - const categorizedUsers: RecipientsResultData[] = []; - const categorizedGroups: RecipientsResultData[] = []; - const categorizedRoles: RecipientsResultData[] = []; - const categorizedUnits: RecipientsResultData[] = []; - - // Categorize recipients based on Type field - recipients.Data.forEach((recipient) => { - if (recipient.Type === 'Personnel') { - categorizedUsers.push(recipient); - } else if (recipient.Type === 'Groups') { - categorizedGroups.push(recipient); - } else if (recipient.Type === 'Roles') { - categorizedRoles.push(recipient); - } else if (recipient.Type === 'Unit') { - categorizedUnits.push(recipient); + const [personnelResult, groupsResult, unitsResult] = await Promise.all([getAllPersonnelInfos(''), getAllGroups(), getUnits()]); + + const users: DispatchItem[] = (personnelResult?.Data ?? []).map((p) => ({ + Id: p.UserId, + Name: `${p.FirstName} ${p.LastName}`.trim(), + })); + + const groups: DispatchItem[] = (groupsResult?.Data ?? []).map((g) => ({ + Id: g.GroupId, + Name: g.Name, + })); + + const units: DispatchItem[] = (unitsResult?.Data ?? []).map((u) => ({ + Id: u.UnitId, + Name: u.Name, + })); + + // Extract unique roles from personnel data + const roleSet = new Map(); + (personnelResult?.Data ?? []).forEach((p) => { + if (p.Roles) { + p.Roles.forEach((role) => { + if (role && !roleSet.has(role)) { + roleSet.set(role, role); + } + }); } }); + const roles: DispatchItem[] = Array.from(roleSet.entries()).map(([name]) => ({ + Id: name, + Name: name, + })); set({ - data: { - users: categorizedUsers, - groups: categorizedGroups, - roles: categorizedRoles, - units: categorizedUnits, - }, + data: { users, groups, roles, units }, isLoading: false, }); } catch (error) { + console.error('fetchDispatchData failed:', error); set({ error: 'Failed to fetch dispatch data', isLoading: false, @@ -112,7 +118,6 @@ export const useDispatchStore = create((set, get) => ({ toggleEveryone: () => { const { selection } = get(); if (selection.everyone) { - // If everyone was selected, deselect it set({ selection: { ...selection, @@ -120,7 +125,6 @@ export const useDispatchStore = create((set, get) => ({ }, }); } else { - // If everyone wasn't selected, select it and clear all others set({ selection: { everyone: true, @@ -140,7 +144,7 @@ export const useDispatchStore = create((set, get) => ({ set({ selection: { ...selection, - everyone: false, // Deselect everyone when selecting specific items + everyone: false, users: isSelected ? selection.users.filter((id) => id !== userId) : [...selection.users, userId], }, }); @@ -153,7 +157,7 @@ export const useDispatchStore = create((set, get) => ({ set({ selection: { ...selection, - everyone: false, // Deselect everyone when selecting specific items + everyone: false, groups: isSelected ? selection.groups.filter((id) => id !== groupId) : [...selection.groups, groupId], }, }); @@ -166,7 +170,7 @@ export const useDispatchStore = create((set, get) => ({ set({ selection: { ...selection, - everyone: false, // Deselect everyone when selecting specific items + everyone: false, roles: isSelected ? selection.roles.filter((id) => id !== roleId) : [...selection.roles, roleId], }, }); @@ -179,7 +183,7 @@ export const useDispatchStore = create((set, get) => ({ set({ selection: { ...selection, - everyone: false, // Deselect everyone when selecting specific items + everyone: false, units: isSelected ? selection.units.filter((id) => id !== unitId) : [...selection.units, unitId], }, }); diff --git a/src/stores/signalr/signalr-store.ts b/src/stores/signalr/signalr-store.ts index 158cd8a..427b9bf 100644 --- a/src/stores/signalr/signalr-store.ts +++ b/src/stores/signalr/signalr-store.ts @@ -8,7 +8,18 @@ import { signalRService } from '@/services/signalr.service'; import { useCoreStore } from '../app/core-store'; import { securityStore, useSecurityStore } from '../security/store'; -export type SignalREventType = 'personnelStatusUpdated' | 'personnelStaffingUpdated' | 'unitStatusUpdated' | 'callsUpdated' | 'callAdded' | 'callClosed' | null; +export type SignalREventType = + | 'personnelStatusUpdated' + | 'personnelStaffingUpdated' + | 'unitStatusUpdated' + | 'callsUpdated' + | 'callAdded' + | 'callClosed' + | 'checkInUpdated' + | 'weatherAlertReceived' + | 'weatherAlertUpdated' + | 'weatherAlertExpired' + | null; interface SignalRState { isUpdateHubConnected: boolean; @@ -18,6 +29,8 @@ interface SignalRState { lastPersonnelUpdateTimestamp: number; lastUnitsUpdateTimestamp: number; lastCallsUpdateTimestamp: number; + lastCheckInUpdateTimestamp: number; + lastWeatherAlertTimestamp: number; isGeolocationHubConnected: boolean; lastGeolocationMessage: unknown; lastGeolocationTimestamp: number; @@ -42,6 +55,10 @@ interface EventHandlers { callsUpdated: ((data: unknown) => void) | null; callAdded: ((data: unknown) => void) | null; callClosed: ((data: unknown) => void) | null; + checkInUpdated: ((data: unknown) => void) | null; + weatherAlertReceived: ((data: unknown) => void) | null; + weatherAlertUpdated: ((data: unknown) => void) | null; + weatherAlertExpired: ((data: unknown) => void) | null; onConnected: ((data: unknown) => void) | null; } @@ -53,6 +70,10 @@ let updateHubHandlers: EventHandlers = { callsUpdated: null, callAdded: null, callClosed: null, + checkInUpdated: null, + weatherAlertReceived: null, + weatherAlertUpdated: null, + weatherAlertExpired: null, onConnected: null, }; @@ -60,7 +81,19 @@ let updateHubHandlers: EventHandlers = { * Helper function to unregister all update hub event handlers */ function unregisterUpdateHubHandlers(): void { - const events: (keyof EventHandlers)[] = ['personnelStatusUpdated', 'personnelStaffingUpdated', 'unitStatusUpdated', 'callsUpdated', 'callAdded', 'callClosed', 'onConnected']; + const events: (keyof EventHandlers)[] = [ + 'personnelStatusUpdated', + 'personnelStaffingUpdated', + 'unitStatusUpdated', + 'callsUpdated', + 'callAdded', + 'callClosed', + 'checkInUpdated', + 'weatherAlertReceived', + 'weatherAlertUpdated', + 'weatherAlertExpired', + 'onConnected', + ]; events.forEach((event) => { const handler = updateHubHandlers[event]; @@ -82,6 +115,8 @@ export const useSignalRStore = create((set, get) => ({ lastPersonnelUpdateTimestamp: 0, lastUnitsUpdateTimestamp: 0, lastCallsUpdateTimestamp: 0, + lastCheckInUpdateTimestamp: 0, + lastWeatherAlertTimestamp: 0, isGeolocationHubConnected: false, lastGeolocationMessage: null, lastGeolocationTimestamp: 0, @@ -166,7 +201,19 @@ export const useSignalRStore = create((set, get) => ({ name: Env.CHANNEL_HUB_NAME, eventingUrl: eventingUrl, hubName: Env.CHANNEL_HUB_NAME, - methods: ['personnelStatusUpdated', 'personnelStaffingUpdated', 'unitStatusUpdated', 'callsUpdated', 'callAdded', 'callClosed', 'onConnected'], + methods: [ + 'personnelStatusUpdated', + 'personnelStaffingUpdated', + 'unitStatusUpdated', + 'callsUpdated', + 'callAdded', + 'callClosed', + 'checkInUpdated', + 'weatherAlertReceived', + 'weatherAlertUpdated', + 'weatherAlertExpired', + 'onConnected', + ], }); await signalRService.invoke(Env.CHANNEL_HUB_NAME, 'connect', parseInt(securityStore.getState().rights?.DepartmentId ?? '0')); @@ -227,6 +274,78 @@ export const useSignalRStore = create((set, get) => ({ }; signalRService.on('callClosed', updateHubHandlers.callClosed); + updateHubHandlers.checkInUpdated = (message: unknown) => { + logger.info({ + message: 'checkInUpdated', + context: { message }, + }); + set({ lastUpdateMessage: JSON.stringify(message), lastUpdateTimestamp: Date.now(), lastEventType: 'checkInUpdated', lastCheckInUpdateTimestamp: Date.now() }); + }; + signalRService.on('checkInUpdated', updateHubHandlers.checkInUpdated); + + // Extract alertId from SignalR weather alert payloads which may be a plain + // string, a number, or an object with an alertId / WeatherAlertId field. + const extractAlertId = (message: unknown): string | null => { + if (typeof message === 'string') return message; + if (typeof message === 'number') return String(message); + if (message && typeof message === 'object') { + const obj = message as Record; + const id = obj.alertId ?? obj.AlertId ?? obj.WeatherAlertId ?? obj.id ?? obj.Id; + if (typeof id === 'string') return id; + if (typeof id === 'number') return String(id); + } + return null; + }; + + updateHubHandlers.weatherAlertReceived = (message: unknown) => { + logger.info({ + message: 'weatherAlertReceived', + context: { message }, + }); + const alertId = extractAlertId(message); + if (!alertId) { + logger.warn({ message: 'weatherAlertReceived: could not extract alertId', context: { message } }); + return; + } + // Lazy import to avoid circular dependency + const { useWeatherAlertsStore } = require('../weatherAlerts/store'); + useWeatherAlertsStore.getState().handleAlertReceived(alertId); + set({ lastUpdateMessage: JSON.stringify(message), lastUpdateTimestamp: Date.now(), lastEventType: 'weatherAlertReceived', lastWeatherAlertTimestamp: Date.now() }); + }; + signalRService.on('weatherAlertReceived', updateHubHandlers.weatherAlertReceived); + + updateHubHandlers.weatherAlertUpdated = (message: unknown) => { + logger.info({ + message: 'weatherAlertUpdated', + context: { message }, + }); + const alertId = extractAlertId(message); + if (!alertId) { + logger.warn({ message: 'weatherAlertUpdated: could not extract alertId', context: { message } }); + return; + } + const { useWeatherAlertsStore } = require('../weatherAlerts/store'); + useWeatherAlertsStore.getState().handleAlertUpdated(alertId); + set({ lastUpdateMessage: JSON.stringify(message), lastUpdateTimestamp: Date.now(), lastEventType: 'weatherAlertUpdated', lastWeatherAlertTimestamp: Date.now() }); + }; + signalRService.on('weatherAlertUpdated', updateHubHandlers.weatherAlertUpdated); + + updateHubHandlers.weatherAlertExpired = (message: unknown) => { + logger.info({ + message: 'weatherAlertExpired', + context: { message }, + }); + const alertId = extractAlertId(message); + if (!alertId) { + logger.warn({ message: 'weatherAlertExpired: could not extract alertId', context: { message } }); + return; + } + const { useWeatherAlertsStore } = require('../weatherAlerts/store'); + useWeatherAlertsStore.getState().handleAlertExpired(alertId); + set({ lastUpdateMessage: JSON.stringify(message), lastUpdateTimestamp: Date.now(), lastEventType: 'weatherAlertExpired', lastWeatherAlertTimestamp: Date.now() }); + }; + signalRService.on('weatherAlertExpired', updateHubHandlers.weatherAlertExpired); + updateHubHandlers.onConnected = () => { logger.info({ message: 'Connected to update SignalR hub', diff --git a/src/stores/weatherAlerts/store.ts b/src/stores/weatherAlerts/store.ts new file mode 100644 index 0000000..1a1eb56 --- /dev/null +++ b/src/stores/weatherAlerts/store.ts @@ -0,0 +1,158 @@ +import { create } from 'zustand'; + +import { getActiveAlerts, getAlertsNearLocation, getWeatherAlert, getWeatherAlertSettings } from '@/api/weatherAlerts/weatherAlerts'; +import { logger } from '@/lib/logging'; +import { WeatherAlertSeverity } from '@/models/v4/weatherAlerts/weatherAlertEnums'; +import { type WeatherAlertResultData } from '@/models/v4/weatherAlerts/weatherAlertResultData'; +import { type WeatherAlertSettingsData } from '@/models/v4/weatherAlerts/weatherAlertSettingsData'; + +function sortAlerts(a: WeatherAlertResultData, b: WeatherAlertResultData): number { + if (a.Severity !== b.Severity) return a.Severity - b.Severity; + return new Date(b.EffectiveUtc).getTime() - new Date(a.EffectiveUtc).getTime(); +} + +interface WeatherAlertsState { + alerts: WeatherAlertResultData[]; + isLoading: boolean; + error: string | null; + + selectedAlert: WeatherAlertResultData | null; + isLoadingDetail: boolean; + + settings: WeatherAlertSettingsData | null; + + nearbyAlerts: WeatherAlertResultData[]; + isLoadingNearby: boolean; + + lastWeatherAlertTimestamp: number; + + fetchActiveAlerts: () => Promise; + fetchAlertDetail: (alertId: string) => Promise; + fetchNearbyAlerts: (lat: number, lng: number, radiusMiles?: number) => Promise; + fetchSettings: () => Promise; + handleAlertReceived: (alertId: string) => Promise; + handleAlertExpired: (alertId: string) => void; + handleAlertUpdated: (alertId: string) => Promise; + reset: () => void; +} + +export const useWeatherAlertsStore = create((set, get) => ({ + alerts: [], + isLoading: false, + error: null, + selectedAlert: null, + isLoadingDetail: false, + settings: null, + nearbyAlerts: [], + isLoadingNearby: false, + lastWeatherAlertTimestamp: 0, + + fetchActiveAlerts: async () => { + set({ isLoading: true, error: null }); + try { + const result = await getActiveAlerts(); + const sorted = (result.Data || []).sort(sortAlerts); + set({ alerts: sorted, isLoading: false }); + } catch (error) { + logger.error({ message: 'Failed to fetch active weather alerts', context: { error } }); + set({ + error: error instanceof Error ? error.message : 'Failed to fetch weather alerts', + isLoading: false, + }); + } + }, + + fetchAlertDetail: async (alertId: string) => { + set({ isLoadingDetail: true }); + try { + const result = await getWeatherAlert(alertId); + set({ selectedAlert: result.Data, isLoadingDetail: false }); + } catch (error) { + logger.error({ message: 'Failed to fetch weather alert detail', context: { error, alertId } }); + set({ selectedAlert: null, isLoadingDetail: false }); + } + }, + + fetchNearbyAlerts: async (lat: number, lng: number, radiusMiles: number = 25) => { + set({ isLoadingNearby: true }); + try { + const result = await getAlertsNearLocation(lat, lng, radiusMiles); + const sorted = (result.Data || []).sort(sortAlerts); + set({ nearbyAlerts: sorted, isLoadingNearby: false }); + } catch (error) { + logger.error({ message: 'Failed to fetch nearby weather alerts', context: { error } }); + set({ isLoadingNearby: false }); + } + }, + + fetchSettings: async () => { + try { + const result = await getWeatherAlertSettings(); + set({ settings: result.Data }); + } catch (error) { + logger.error({ message: 'Failed to fetch weather alert settings', context: { error } }); + } + }, + + handleAlertReceived: async (alertId: string) => { + try { + const result = await getWeatherAlert(alertId); + if (result.Data) { + const current = get().alerts; + const updated = [result.Data, ...current].sort(sortAlerts); + set({ alerts: updated, lastWeatherAlertTimestamp: Date.now() }); + } + } catch (error) { + logger.error({ message: 'Failed to handle received weather alert', context: { error, alertId } }); + // Fall back to full refresh + await get().fetchActiveAlerts(); + } + }, + + handleAlertExpired: (alertId: string) => { + const current = get().alerts; + set({ + alerts: current.filter((a) => a.WeatherAlertId !== alertId), + lastWeatherAlertTimestamp: Date.now(), + }); + }, + + handleAlertUpdated: async (alertId: string) => { + try { + const result = await getWeatherAlert(alertId); + if (result.Data) { + const current = get().alerts; + const idx = current.findIndex((a) => a.WeatherAlertId === alertId); + let updated: WeatherAlertResultData[]; + if (idx >= 0) { + updated = [...current]; + updated[idx] = result.Data; + } else { + updated = [result.Data, ...current]; + } + set({ alerts: updated.sort(sortAlerts), lastWeatherAlertTimestamp: Date.now() }); + + // Update selected alert if it's the one being viewed + if (get().selectedAlert?.WeatherAlertId === alertId) { + set({ selectedAlert: result.Data }); + } + } + } catch (error) { + logger.error({ message: 'Failed to handle updated weather alert', context: { error, alertId } }); + await get().fetchActiveAlerts(); + } + }, + + reset: () => { + set({ + alerts: [], + isLoading: false, + error: null, + selectedAlert: null, + isLoadingDetail: false, + nearbyAlerts: [], + isLoadingNearby: false, + lastWeatherAlertTimestamp: 0, + }); + }, +})); diff --git a/src/translations/ar.json b/src/translations/ar.json index 384f0d8..eed32b9 100644 --- a/src/translations/ar.json +++ b/src/translations/ar.json @@ -177,7 +177,8 @@ "dispatched": "المرسلة", "info": "المعلومات", "protocols": "البروتوكولات", - "timeline": "النشاط" + "timeline": "النشاط", + "video": "فيديو" }, "timestamp": "الطابع الزمني", "title": "تفاصيل المكالمة", @@ -578,7 +579,8 @@ "personnel": "الموظفون", "protocols": "البروتوكولات", "settings": "الإعدادات", - "units": "الوحدات" + "units": "الوحدات", + "weatherAlerts": "تنبيهات الطقس" }, "map": { "call_set_as_current": "تم تعيين المكالمة كمكالمة حالية", @@ -589,7 +591,11 @@ "recenter_map": "إعادة توسيط الخريطة", "set_as_current_call": "تعيين كمكالمة حالية", "view_call_details": "عرض تفاصيل المكالمة", - "dispatched_resources": "المرسلون" + "dispatched_resources": "المرسلون", + "layers": "طبقات الخريطة", + "no_layers": "لا توجد طبقات متاحة", + "show_all": "إظهار الكل", + "hide_all": "إخفاء الكل" }, "notes": { "actions": { @@ -610,6 +616,38 @@ "search": "البحث في الملاحظات...", "title": "الملاحظات" }, + "personnel": { + "title": "الأفراد", + "search": "البحث عن الأفراد...", + "loading": "جاري تحميل الأفراد...", + "empty": "لم يتم العثور على أفراد", + "empty_description": "لا يوجد أفراد متاحون حالياً في قسمك.", + "no_results": "لا يوجد أفراد يطابقون بحثك", + "no_results_description": "حاول تعديل مصطلحات البحث.", + "status": "الحالة", + "staffing": "التوظيف", + "group": "المجموعة", + "roles": "الأدوار", + "email": "البريد الإلكتروني", + "phone": "الهاتف", + "id_number": "رقم الهوية", + "responding_to": "يستجيب لـ", + "status_updated": "تم تحديث الحالة", + "staffing_updated": "تم تحديث التوظيف", + "details": "تفاصيل الأفراد", + "contact_info": "معلومات الاتصال", + "status_info": "الحالة والتوظيف", + "no_email": "لا يوجد بريد إلكتروني متاح", + "no_phone": "لا يوجد هاتف متاح", + "no_group": "غير معيّن", + "no_roles": "لا توجد أدوار معيّنة", + "unknown_status": "غير معروف", + "on_duty": "في الخدمة", + "off_duty": "خارج الخدمة", + "call_phone": "اتصال", + "send_email": "بريد إلكتروني", + "custom_fields": "معلومات إضافية" + }, "onboarding": { "screen1": { "title": "Resgrid Dispatch", @@ -690,7 +728,11 @@ "background_location": "الموقع في الخلفية", "contact_us": "اتصل بنا", "current_unit": "الوحدة الحالية", + "arabic": "عربي", "english": "إنجليزي", + "french": "فرنسي", + "german": "ألماني", + "italian": "إيطالي", "enter_password": "أدخل كلمة المرور الخاصة بك", "enter_server_url": "أدخل عنوان URL لواجهة برمجة تطبيقات Resgrid (مثال: https://api.resgrid.com)", "enter_username": "أدخل اسم المستخدم الخاص بك", @@ -723,7 +765,10 @@ "server_url_note": "ملاحظة: هذا هو عنوان URL لواجهة برمجة تطبيقات Resgrid. يتم استخدامه للاتصال بخادم Resgrid. لا تقم بتضمين /api/v4 في عنوان URL أو شرطة مائلة في النهاية.", "set_active_unit": "تعيين الوحدة النشطة", "share": "مشاركة", + "polish": "بولندي", "spanish": "إسباني", + "swedish": "سويدي", + "ukrainian": "أوكراني", "status_page": "حالة النظام", "support": "الدعم", "support_us": "ادعمنا", @@ -777,7 +822,8 @@ "notes": "الملاحظات", "protocols": "البروتوكولات", "settings": "الإعدادات", - "shifts": "المناوبات" + "shifts": "المناوبات", + "personnel": "الموظفون" }, "dispatch": { "active_calls": "المكالمات النشطة", @@ -870,6 +916,8 @@ "calls_updated": "تم تحديث المكالمات", "call_added": "تمت إضافة مكالمة جديدة", "call_closed": "تم إغلاق المكالمة", + "check_ins": "تسجيلات الحضور", + "no_check_ins": "لا توجد مكالمات بمؤقتات تسجيل الحضور", "radio_log": "سجل الراديو", "radio": "الراديو", "activity": "النشاط", @@ -920,7 +968,186 @@ "set_status": "تعيين الحالة", "set_staffing": "التعيين", "dispatch": "إرسال", - "select_items_for_actions": "حدد مكالمة أو وحدة أو موظف لتفعيل الإجراءات السياقية" + "select_items_for_actions": "حدد مكالمة أو وحدة أو موظف لتفعيل الإجراءات السياقية", + "weather": { + "clear": "صافٍ", + "mainly_clear": "صافٍ غالبًا", + "partly_cloudy": "غائم جزئيًا", + "overcast": "غائم", + "fog": "ضباب", + "drizzle": "رذاذ", + "freezing_drizzle": "رذاذ متجمد", + "rain": "مطر", + "freezing_rain": "مطر متجمد", + "snow": "ثلج", + "rain_showers": "زخات مطر", + "snow_showers": "زخات ثلج", + "thunderstorm": "عاصفة رعدية", + "thunderstorm_hail": "عاصفة رعدية مع بَرَد", + "unknown": "غير معروف" + } + }, + "check_in": { + "tab_title": "تسجيل الحضور", + "timer_status": "حالة المؤقت", + "perform_check_in": "تسجيل", + "check_in_success": "تم تسجيل الحضور بنجاح", + "check_in_error": "فشل في تسجيل الحضور", + "checked_in_by": "بواسطة {{name}}", + "last_check_in": "آخر تسجيل", + "elapsed": "المنقضي", + "duration": "المدة", + "status_ok": "جيد", + "status_green": "جيد", + "status_warning": "تحذير", + "status_yellow": "تحذير", + "status_overdue": "متأخر", + "status_red": "متأخر", + "status_critical": "حرج", + "history": "سجل التسجيلات", + "no_timers": "لم يتم تكوين مؤقتات تسجيل الحضور", + "timers_disabled": "مؤقتات تسجيل الحضور معطلة لهذه المكالمة", + "type_personnel": "الأفراد", + "type_unit": "الوحدة", + "type_ic": "قائد الحادث", + "type_par": "PAR", + "type_hazmat": "التعرض للمواد الخطرة", + "type_sector_rotation": "تدوير القطاع", + "type_rehab": "إعادة التأهيل", + "add_note": "إضافة ملاحظة (اختياري)", + "confirm": "تأكيد التسجيل", + "minutes_ago": "منذ {{count}} دقيقة", + "select_target": "حدد الكيان للتسجيل", + "overdue_count": "{{count}} متأخر", + "warning_count": "{{count}} تحذير", + "enable_timers": "تفعيل المؤقتات", + "disable_timers": "تعطيل المؤقتات", + "summary": "{{overdue}} متأخر، {{warning}} تحذير، {{ok}} جيد" + }, + "units": { + "title": "الوحدات" + }, + "weatherAlerts": { + "title": "تنبيهات الطقس", + "activeAlerts": "التنبيهات النشطة", + "noActiveAlerts": "لا توجد تنبيهات طقس نشطة", + "moreAlerts": "+{{count}} أخرى", + "severity": { + "extreme": "شديدة للغاية", + "severe": "شديدة", + "moderate": "معتدلة", + "minor": "طفيفة", + "unknown": "غير معروفة" + }, + "category": { + "met": "أرصاد جوية", + "fire": "حريق", + "health": "صحة", + "env": "بيئية", + "other": "أخرى" + }, + "urgency": { + "immediate": "فورية", + "expected": "متوقعة", + "future": "مستقبلية", + "past": "سابقة", + "unknown": "غير معروفة" + }, + "certainty": { + "observed": "مرصودة", + "likely": "محتملة", + "possible": "ممكنة", + "unlikely": "غير محتملة", + "unknown": "غير معروفة" + }, + "status": { + "active": "نشطة", + "updated": "محدّثة", + "expired": "منتهية", + "cancelled": "ملغاة" + }, + "detail": { + "headline": "العنوان", + "description": "الوصف", + "instruction": "التعليمات", + "area": "المنطقة المتأثرة", + "effective": "سارية المفعول", + "onset": "البداية", + "expires": "تنتهي", + "sent": "أُرسلت", + "sender": "المصدر", + "urgency": "الاستعجال", + "certainty": "اليقين" + }, + "filter": { + "all": "الكل", + "nearby": "قريبة" + }, + "sort": { + "severity": "الشدة", + "expires": "قريبة الانتهاء", + "newest": "الأحدث" + }, + "banner": { + "viewAll": "عرض الكل" + } + }, + "videoFeeds": { + "title": "بث الفيديو", + "noFeeds": "لا توجد بثوث فيديو لهذه المكالمة", + "addFeed": "إضافة بث فيديو", + "editFeed": "تعديل بث الفيديو", + "deleteFeed": "حذف بث الفيديو", + "deleteConfirm": "هل أنت متأكد أنك تريد إزالة بث الفيديو هذا؟", + "watch": "مشاهدة", + "goLive": "بث مباشر", + "stopLive": "إيقاف البث", + "flipCamera": "عكس الكاميرا", + "feedAdded": "تمت إضافة بث الفيديو", + "feedUpdated": "تم تحديث بث الفيديو", + "feedDeleted": "تم حذف بث الفيديو", + "feedError": "فشل في تحميل بث الفيديو", + "unsupportedFormat": "تنسيق البث هذا غير مدعوم على الأجهزة المحمولة", + "copyUrl": "نسخ الرابط", + "form": { + "name": "اسم البث", + "namePlaceholder": "مثال: طائرة محرك 1", + "url": "رابط البث", + "urlPlaceholder": "مثال: https://stream.example.com/live.m3u8", + "feedType": "نوع الكاميرا", + "feedFormat": "تنسيق البث", + "description": "الوصف", + "descriptionPlaceholder": "وصف اختياري", + "status": "الحالة", + "sortOrder": "الترتيب", + "cameraLocation": "موقع الكاميرا", + "useCurrentLocation": "استخدام الموقع الحالي" + }, + "type": { + "drone": "طائرة بدون طيار", + "fixedCamera": "كاميرا ثابتة", + "bodyCam": "كاميرا جسدية", + "trafficCam": "كاميرا مرور", + "weatherCam": "كاميرا طقس", + "satelliteFeed": "بث فضائي", + "webCam": "كاميرا ويب", + "other": "أخرى" + }, + "format": { + "rtsp": "RTSP", + "hls": "HLS", + "mjpeg": "MJPEG", + "youtubeLive": "YouTube Live", + "webrtc": "WebRTC", + "dash": "DASH", + "embed": "مضمن", + "other": "أخرى" + }, + "status": { + "active": "نشط", + "inactive": "غير نشط", + "error": "خطأ" + } }, "welcome": "مرحبًا بك في موقع تطبيق obytes" } diff --git a/src/translations/de.json b/src/translations/de.json new file mode 100644 index 0000000..8df2d49 --- /dev/null +++ b/src/translations/de.json @@ -0,0 +1,1152 @@ +{ + "app": { + "title": "Resgrid Dispatch" + }, + "audio_streams": { + "buffering": "Puffern", + "buffering_stream": "Audiostream wird gepuffert...", + "close": "Schließen", + "currently_playing": "Wird abgespielt: {{streamName}}", + "loading": "Laden", + "loading_stream": "Audiostream wird geladen...", + "loading_streams": "Audiostreams werden geladen...", + "name": "Name", + "no_stream_playing": "Es wird derzeit kein Audiostream abgespielt", + "none": "Keiner", + "playing": "Wiedergabe", + "refresh_streams": "Streams aktualisieren", + "select_placeholder": "Audiostream auswählen", + "select_stream": "Audiostream auswählen", + "status": "Status", + "stopped": "Gestoppt", + "stream_info": "Stream-Informationen", + "stream_selected": "Ausgewählt: {{streamName}}", + "title": "Audiostreams", + "type": "Typ" + }, + "bluetooth": { + "applied": "Angewendet", + "audio": "Audio", + "audioActive": "Audio aktiv", + "audio_device": "BT-Headset", + "availableDevices": "Verfügbare Geräte", + "available_devices": "Verfügbare Geräte", + "bluetooth_not_ready": "Bluetooth ist {{state}}. Bitte aktivieren Sie Bluetooth.", + "buttonControlAvailable": "Tastensteuerung verfügbar", + "checking": "Bluetooth-Status wird überprüft...", + "clear": "Löschen", + "connect": "Verbinden", + "connected": "Verbunden", + "connectionError": "Verbindungsfehler", + "current_selection": "Aktuelle Auswahl", + "disconnect": "Trennen", + "doublePress": "Doppeldruck ", + "liveKitActive": "LiveKit aktiv", + "longPress": "Langer Druck ", + "micControl": "Mikrofonsteuerung", + "mute": "Stummschalten", + "noDevicesFound": "Keine Audiogeräte gefunden", + "noDevicesFoundRetry": "Keine Audiogeräte gefunden. Versuchen Sie erneut zu suchen.", + "no_device_selected": "Kein Gerät ausgewählt", + "no_devices_found": "Keine Bluetooth-Audiogeräte gefunden", + "not_connected": "Nicht verbunden", + "poweredOff": "Bluetooth ist ausgeschaltet. Bitte aktivieren Sie Bluetooth, um Audiogeräte zu verbinden.", + "pttStart": "PTT Start", + "pttStop": "PTT Stopp", + "recentButtonEvents": "Letzte Tastenereignisse", + "scan": "Suchen", + "scanAgain": "Erneut suchen", + "scan_again": "Erneut suchen", + "scan_error_message": "Bluetooth-Geräte konnten nicht gesucht werden", + "scan_error_title": "Suchfehler", + "scanning": "Suche läuft...", + "select_device": "Bluetooth-Gerät auswählen", + "selected": "Ausgewählt", + "selection_error_message": "Bevorzugtes Gerät konnte nicht gespeichert werden", + "selection_error_title": "Auswahlfehler", + "startScanning": "Suche starten", + "stopScan": "Suche beenden", + "supports_mic_control": "Mikrofonsteuerung", + "tap_scan_to_find_devices": "Tippen Sie auf 'Suchen', um Bluetooth-Audiogeräte zu finden", + "title": "Bluetooth-Audio", + "unauthorized": "Bluetooth-Berechtigung verweigert. Bitte erteilen Sie die Bluetooth-Berechtigung in den Einstellungen.", + "unknown": "Unbekannt", + "unknownDevice": "Unbekanntes Gerät", + "unknown_device": "Unbekanntes Gerät", + "unmute": "Stummschaltung aufheben", + "volumeDown": "Lautstärke -", + "volumeUp": "Lautstärke +" + }, + "callImages": { + "add": "Bild hinzufügen", + "add_new": "Neues Bild hinzufügen", + "default_name": "Unbenanntes Bild", + "error": "Fehler beim Laden der Bilder", + "failed_to_load": "Bild konnte nicht geladen werden", + "image_alt": "Einsatzbild", + "image_name": "Bildname", + "image_note": "Bildnotiz", + "loading": "Laden...", + "no_images": "Keine Bilder verfügbar", + "no_images_description": "Fügen Sie Bilder zu Ihrem Einsatz hinzu, um Dokumentation und Kommunikation zu unterstützen", + "select_from_gallery": "Aus Galerie auswählen", + "take_photo": "Foto aufnehmen", + "title": "Einsatzbilder", + "upload": "Hochladen" + }, + "callNotes": { + "addNote": "Notiz hinzufügen", + "addNotePlaceholder": "Neue Notiz hinzufügen...", + "noNotes": "Keine Notizen für diesen Einsatz verfügbar", + "noSearchResults": "Keine Notizen entsprechen Ihrer Suche", + "searchPlaceholder": "Notizen durchsuchen...", + "title": "Einsatznotizen" + }, + "call_detail": { + "address": "Adresse", + "call_location": "Einsatzort", + "close_call": "Einsatz schließen", + "close_call_confirmation": "Sind Sie sicher, dass Sie diesen Einsatz schließen möchten?", + "close_call_error": "Einsatz konnte nicht geschlossen werden", + "close_call_note": "Abschlussnotiz", + "close_call_note_placeholder": "Geben Sie eine Notiz zum Abschluss des Einsatzes ein", + "close_call_success": "Einsatz erfolgreich geschlossen", + "close_call_type": "Abschlussart", + "close_call_type_placeholder": "Abschlussart auswählen", + "close_call_type_required": "Bitte wählen Sie eine Abschlussart", + "close_call_types": { + "cancelled": "Storniert", + "closed": "Geschlossen", + "false_alarm": "Fehlalarm", + "founded": "Bestätigt", + "minor": "Geringfügig", + "transferred": "Übergeben", + "unfounded": "Unbegründet" + }, + "contact_email": "E-Mail", + "contact_info": "Kontaktdaten", + "contact_name": "Kontaktname", + "contact_phone": "Telefon", + "edit_call": "Einsatz bearbeiten", + "external_id": "Externe ID", + "failed_to_open_maps": "Kartenanwendung konnte nicht geöffnet werden", + "files": { + "add_file": "Datei hinzufügen", + "button": "Dateien", + "empty": "Keine Dateien verfügbar", + "empty_description": "Fügen Sie Dateien zu Ihrem Einsatz hinzu, um Dokumentation und Kommunikation zu unterstützen", + "error": "Fehler beim Abrufen der Dateien", + "file_name": "Dateiname", + "name_required": "Bitte geben Sie einen Namen für die Datei ein", + "no_files": "Keine Dateien verfügbar", + "no_files_description": "Fügen Sie Dateien zu Ihrem Einsatz hinzu, um Dokumentation und Kommunikation zu unterstützen", + "open_error": "Fehler beim Öffnen der Datei", + "select_error": "Fehler beim Auswählen der Datei", + "select_file": "Datei auswählen", + "share_error": "Fehler beim Teilen der Datei", + "title": "Einsatzdateien", + "upload": "Hochladen", + "upload_error": "Fehler beim Hochladen der Datei", + "uploading": "Wird hochgeladen..." + }, + "group": "Gruppe", + "images": "Bilder", + "loading": "Einsatzdetails werden geladen...", + "nature": "Art des Einsatzes", + "no_additional_info": "Keine zusätzlichen Informationen verfügbar", + "no_contact_info": "Keine Kontaktdaten verfügbar", + "no_dispatched": "Keine Einheiten zu diesem Einsatz disponiert", + "no_location": "Keine Standortdaten verfügbar", + "no_location_for_routing": "Keine Standortdaten für die Routenführung verfügbar", + "no_protocols": "Keine Protokolle diesem Einsatz zugeordnet", + "no_timeline": "Keine Zeitverlaufsereignisse verfügbar", + "not_available": "k. A.", + "not_found": "Einsatz nicht gefunden", + "missing_call_id": "Einsatz-ID fehlt", + "note": "Notiz", + "notes": "Notizen", + "priority": "Priorität", + "reference_id": "Referenz-ID", + "set_active": "Als aktiv setzen", + "set_active_error": "Einsatz konnte nicht als aktiv gesetzt werden", + "set_active_success": "Einsatz als aktiv gesetzt", + "setting_active": "Wird aktiviert...", + "status": "Status", + "tabs": { + "contact": "Kontakt", + "dispatched": "Disponiert", + "info": "Info", + "protocols": "Protokolle", + "timeline": "Aktivität", + "video": "Video" + }, + "timestamp": "Zeitstempel", + "title": "Einsatzdetails", + "type": "Typ", + "unit": "Einheit", + "update_call_error": "Einsatz konnte nicht aktualisiert werden", + "update_call_success": "Einsatz erfolgreich aktualisiert" + }, + "calls": { + "address": "Adresse", + "address_found": "Adresse gefunden und Standort aktualisiert", + "address_not_found": "Adresse nicht gefunden, bitte versuchen Sie eine andere Adresse", + "address_placeholder": "Geben Sie die Adresse des Einsatzes ein", + "address_required": "Bitte geben Sie eine Adresse für die Suche ein", + "call_details": "Einsatzdetails", + "call_location": "Einsatzort", + "call_number": "Einsatznummer", + "call_priority": "Einsatzpriorität", + "confirm_deselect_message": "Sind Sie sicher, dass Sie den aktuellen aktiven Einsatz abwählen möchten?", + "confirm_deselect_title": "Aktiven Einsatz abwählen", + "contact_info": "Kontaktdaten", + "contact_info_placeholder": "Geben Sie die Kontaktinformationen ein", + "contact_name": "Kontaktname", + "contact_name_placeholder": "Geben Sie den Namen des Kontakts ein", + "contact_phone": "Kontakttelefon", + "contact_phone_placeholder": "Geben Sie die Telefonnummer des Kontakts ein", + "coordinates": "GPS-Koordinaten", + "coordinates_found": "Koordinaten gefunden und Adresse aktualisiert", + "coordinates_geocoding_error": "Adresse für Koordinaten konnte nicht ermittelt werden, Standort aber auf Karte gesetzt", + "coordinates_invalid_format": "Ungültiges Koordinatenformat. Bitte verwenden Sie das Format: Breitengrad, Längengrad", + "coordinates_no_address": "Koordinaten auf Karte gesetzt, aber keine Adresse gefunden", + "coordinates_out_of_range": "Koordinaten außerhalb des Bereichs. Breitengrad muss -90 bis 90 sein, Längengrad -180 bis 180", + "coordinates_placeholder": "GPS-Koordinaten eingeben (z. B. 37.7749, -122.4194)", + "coordinates_required": "Bitte geben Sie Koordinaten für die Suche ein", + "create": "Erstellen", + "create_error": "Fehler beim Erstellen des Einsatzes", + "create_new_call": "Neuen Einsatz erstellen", + "create_success": "Einsatz erfolgreich erstellt", + "description": "Beschreibung", + "description_placeholder": "Geben Sie die Beschreibung des Einsatzes ein", + "deselect": "Abwählen", + "directions": "Wegbeschreibung", + "dispatch_to": "Disponieren an", + "dispatch_to_everyone": "An alle verfügbaren Einsatzkräfte disponieren", + "edit_call": "Einsatz bearbeiten", + "edit_call_description": "Einsatzinformationen aktualisieren", + "everyone": "Alle", + "files": { + "no_files": "Keine Dateien verfügbar", + "no_files_description": "Diesem Einsatz wurden noch keine Dateien hinzugefügt", + "title": "Einsatzdateien" + }, + "geocoding_error": "Adresssuche fehlgeschlagen, bitte versuchen Sie es erneut", + "groups": "Gruppen", + "invalid_priority": "Ungültige Priorität ausgewählt. Bitte wählen Sie eine gültige Priorität.", + "invalid_type": "Ungültiger Typ ausgewählt. Bitte wählen Sie einen gültigen Einsatztyp.", + "loading": "Einsätze werden geladen...", + "loading_calls": "Einsätze werden geladen...", + "name": "Name", + "name_placeholder": "Geben Sie den Namen des Einsatzes ein", + "nature": "Art des Einsatzes", + "nature_placeholder": "Geben Sie die Art des Einsatzes ein", + "new_call": "Neuer Einsatz", + "new_call_description": "Erstellen Sie einen neuen Einsatz, um einen neuen Vorfall zu starten", + "no_call_selected": "Kein aktiver Einsatz", + "no_call_selected_info": "Diese Einheit ist derzeit keinem Einsatz zugeordnet", + "no_calls": "Keine aktiven Einsätze", + "no_calls_available": "Keine Einsätze verfügbar", + "no_calls_description": "Keine aktiven Einsätze gefunden. Wählen Sie einen aktiven Einsatz aus, um Details anzuzeigen.", + "no_location_message": "Für diesen Einsatz sind keine Standortdaten zur Navigation verfügbar.", + "no_location_title": "Kein Standort verfügbar", + "no_open_calls": "Keine offenen Einsätze verfügbar", + "note": "Notiz", + "note_placeholder": "Geben Sie die Notiz zum Einsatz ein", + "plus_code": "Plus Code", + "plus_code_found": "Plus Code gefunden und Standort aktualisiert", + "plus_code_geocoding_error": "Plus Code-Suche fehlgeschlagen, bitte versuchen Sie es erneut", + "plus_code_not_found": "Plus Code nicht gefunden, bitte versuchen Sie einen anderen Plus Code", + "plus_code_placeholder": "Plus Code eingeben (z. B. 849VCWC8+R9)", + "plus_code_required": "Bitte geben Sie einen Plus Code für die Suche ein", + "priority": "Priorität", + "priority_placeholder": "Priorität des Einsatzes auswählen", + "roles": "Rollen", + "search": "Einsätze durchsuchen...", + "select_active_call": "Aktiven Einsatz auswählen", + "select_address": "Adresse auswählen", + "select_address_placeholder": "Adresse des Einsatzes auswählen", + "select_description": "Beschreibung auswählen", + "select_dispatch_recipients": "Alarmierungsempfänger auswählen", + "select_location": "Standort auf Karte auswählen", + "select_name": "Name auswählen", + "select_nature": "Art auswählen", + "select_nature_placeholder": "Art des Einsatzes auswählen", + "select_priority": "Priorität auswählen", + "select_priority_placeholder": "Priorität des Einsatzes auswählen", + "select_recipients": "Empfänger auswählen", + "select_type": "Typ auswählen", + "selected": "ausgewählt", + "title": "Einsätze", + "type": "Typ", + "units": "Einheiten", + "users": "Benutzer", + "viewNotes": "Notizen", + "view_details": "Details anzeigen", + "what3words": "what3words", + "what3words_found": "what3words-Adresse gefunden und Standort aktualisiert", + "what3words_geocoding_error": "what3words-Adresssuche fehlgeschlagen, bitte versuchen Sie es erneut", + "what3words_invalid_format": "Ungültiges what3words-Format. Bitte verwenden Sie das Format: wort.wort.wort", + "what3words_not_found": "what3words-Adresse nicht gefunden, bitte versuchen Sie eine andere Adresse", + "what3words_placeholder": "what3words-Adresse eingeben (z. B. filled.count.soap)", + "what3words_required": "Bitte geben Sie eine what3words-Adresse für die Suche ein", + "expand_map": "Karte vergrößern", + "schedule_dispatch": "Disposition planen", + "schedule_dispatch_description": "Legen Sie ein zukünftiges Datum und eine Uhrzeit fest, um diesen Einsatz später zu disponieren", + "scheduled_on": "Geplantes Datum & Uhrzeit", + "scheduled_on_helper": "Leer lassen für sofortige Disposition", + "scheduled_on_past_error": "Der geplante Zeitpunkt muss in der Zukunft liegen", + "contact_information": "Kontaktinformationen", + "new_call_web_hint": "Füllen Sie die Einsatzdetails unten aus. Drücken Sie Strg+Eingabe zum Erstellen.", + "edit_call_web_hint": "Aktualisieren Sie die Einsatzdetails unten. Drücken Sie Strg+S zum Speichern.", + "keyboard_shortcuts": "Tipp: Strg+Eingabe zum Erstellen, Escape zum Abbrechen", + "edit_keyboard_shortcuts": "Tipp: Strg+S zum Speichern, Escape zum Abbrechen", + "templates": { + "title": "Einsatzvorlagen", + "select_template": "Vorlage auswählen", + "search_placeholder": "Vorlagen durchsuchen...", + "none": "Keine Vorlagen verfügbar", + "template_applied": "Vorlage angewendet" + }, + "form": { + "title": "Einsatzformular", + "no_form": "Kein Einsatzformular konfiguriert" + }, + "linked_calls": { + "title": "Verknüpfter Einsatz", + "select": "Mit bestehendem Einsatz verknüpfen", + "change": "Verknüpften Einsatz ändern", + "none": "Keine aktiven Einsätze verfügbar", + "search_placeholder": "Einsätze durchsuchen...", + "linked": "Verknüpft" + }, + "contact_picker": { + "title": "Kontakt auswählen", + "search_placeholder": "Kontakte durchsuchen...", + "none": "Keine Kontakte verfügbar" + }, + "protocols": { + "title": "Protokolle", + "select": "Protokolle auswählen", + "none": "Keine Protokolle verfügbar", + "selected_count": "ausgewählt", + "expand_questions": "Fragen anzeigen", + "collapse": "Einklappen" + } + }, + "common": { + "add": "Hinzufügen", + "back": "Zurück", + "cancel": "Abbrechen", + "creating": "Wird erstellt...", + "saving": "Wird gespeichert...", + "unsaved_changes": "Ungespeicherte Änderungen", + "close": "Schließen", + "confirm": "Bestätigen", + "confirm_location": "Standort bestätigen", + "delete": "Löschen", + "dismiss": "Schließen", + "done": "Fertig", + "edit": "Bearbeiten", + "error": "Fehler", + "errorOccurred": "Ein Fehler ist aufgetreten", + "get_my_location": "Meinen Standort ermitteln", + "go_back": "Zurück", + "loading": "Laden...", + "loading_address": "Adresse wird geladen...", + "next": "Weiter", + "noActiveUnit": "Keine aktive Einheit gesetzt", + "noActiveUnitDescription": "Bitte setzen Sie eine aktive Einheit auf der Einstellungsseite, um auf Statuskontrollen zuzugreifen", + "noDataAvailable": "Keine Daten verfügbar", + "no_address_found": "Keine Adresse gefunden", + "no_location": "Keine Standortdaten verfügbar", + "no_results_found": "Keine Ergebnisse gefunden", + "no_unit_selected": "Keine Einheit ausgewählt", + "nothingToDisplay": "Es gibt derzeit nichts anzuzeigen", + "of": "von", + "ok": "Ok", + "optional": "optional", + "permission_denied": "Berechtigung verweigert", + "previous": "Zurück", + "remove": "Entfernen", + "retry": "Erneut versuchen", + "route": "Route", + "save": "Speichern", + "search": "Suchen...", + "set_location": "Standort setzen", + "share": "Teilen", + "step": "Schritt", + "submit": "Absenden", + "submitting": "Wird gesendet...", + "tryAgainLater": "Bitte versuchen Sie es später erneut", + "unknown": "Unbekannt", + "unknown_department": "Unbekannte Abteilung", + "unknown_user": "Unbekannter Benutzer", + "upload": "Hochladen", + "uploading": "Wird hochgeladen..." + }, + "contacts": { + "add": "Kontakt hinzufügen", + "addedBy": "Hinzugefügt von", + "addedOn": "Hinzugefügt am", + "additionalInformation": "Zusätzliche Informationen", + "address": "Adresse", + "bluesky": "Bluesky", + "cancel": "Abbrechen", + "cellPhone": "Mobiltelefon", + "city": "Stadt", + "cityState": "Stadt & Bundesland", + "cityStateZip": "Stadt, Bundesland, PLZ", + "company": "Unternehmen", + "contactInformation": "Kontaktinformationen", + "contactNotes": "Kontaktnotizen", + "contactNotesEmpty": "Keine Notizen für diesen Kontakt gefunden", + "contactNotesEmptyDescription": "Notizen, die zu diesem Kontakt hinzugefügt werden, erscheinen hier", + "contactNotesExpired": "Diese Notiz ist abgelaufen", + "contactNotesLoading": "Kontaktnotizen werden geladen...", + "contactType": "Kontakttyp", + "countryId": "Länderkennung", + "delete": "Löschen", + "deleteConfirm": "Sind Sie sicher, dass Sie diesen Kontakt löschen möchten?", + "deleteSuccess": "Kontakt erfolgreich gelöscht", + "description": "Kontakte hinzufügen und verwalten", + "details": "Kontaktdetails", + "detailsTab": "Details", + "edit": "Kontakt bearbeiten", + "editedBy": "Bearbeitet von", + "editedOn": "Bearbeitet am", + "email": "E-Mail", + "empty": "Keine Kontakte gefunden", + "emptyDescription": "Fügen Sie Kontakte hinzu, um Ihre persönlichen und geschäftlichen Verbindungen zu verwalten", + "entranceCoordinates": "Eingangskoordinaten", + "exitCoordinates": "Ausgangskoordinaten", + "expires": "Läuft ab", + "facebook": "Facebook", + "faxPhone": "Faxnummer", + "formError": "Bitte beheben Sie die Fehler im Formular", + "homePhone": "Festnetztelefon", + "identification": "Identifikation", + "important": "Als wichtig markieren", + "instagram": "Instagram", + "internal": "Intern", + "invalidEmail": "Ungültige E-Mail-Adresse", + "linkedin": "LinkedIn", + "locationCoordinates": "Standortkoordinaten", + "locationInformation": "Standortinformationen", + "mastodon": "Mastodon", + "mobile": "Mobil", + "name": "Name", + "noteAlert": "Warnung", + "noteType": "Typ", + "notes": "Notizen", + "notesTab": "Notizen", + "officePhone": "Bürotelefon", + "otherInfo": "Sonstige Informationen", + "person": "Person", + "phone": "Telefon", + "public": "Öffentlich", + "required": "Erforderlich", + "save": "Kontakt speichern", + "saveSuccess": "Kontakt erfolgreich gespeichert", + "search": "Kontakte durchsuchen...", + "shouldAlert": "Soll alarmieren", + "socialMediaWeb": "Soziale Medien & Web", + "state": "Bundesland", + "stateId": "Bundesland-ID", + "systemInformation": "Systeminformationen", + "tabs": { + "details": "Details", + "notes": "Notizen" + }, + "threads": "Threads", + "title": "Kontakte", + "twitter": "Twitter", + "visibility": "Sichtbarkeit", + "website": "Webseite", + "zip": "Postleitzahl" + }, + "form": { + "invalid_url": "Bitte geben Sie eine gültige URL ein, die mit http:// oder https:// beginnt", + "required": "Dieses Feld ist erforderlich" + }, + "livekit": { + "audio_devices": "Audiogeräte", + "audio_settings": "Audioeinstellungen", + "connected_to_room": "Mit Kanal verbunden", + "connecting": "Verbindung wird hergestellt...", + "disconnect": "Trennen", + "join": "Beitreten", + "microphone": "Mikrofon", + "mute": "Stummschalten", + "no_rooms_available": "Keine Sprachkanäle verfügbar", + "speaker": "Lautsprecher", + "speaking": "Spricht", + "title": "Sprachkanäle", + "unmute": "Stummschaltung aufheben" + }, + "loading": { + "loading": "Laden...", + "loadingData": "Daten werden geladen...", + "pleaseWait": "Bitte warten", + "processingRequest": "Ihre Anfrage wird verarbeitet..." + }, + "sso": { + "authenticating": "Authentifizierung läuft...", + "back_to_login": "Zurück zur Anmeldung", + "back_to_lookup": "Benutzer wechseln", + "continue_button": "Weiter", + "department_id_label": "Abteilungs-ID", + "department_id_placeholder": "Abteilungs-ID eingeben", + "error_generic": "Anmeldung fehlgeschlagen. Bitte versuchen Sie es erneut.", + "error_oidc_cancelled": "Anmeldung wurde abgebrochen.", + "error_oidc_not_ready": "SSO-Anbieter wird geladen, bitte warten.", + "error_sso_not_enabled": "Single Sign-On ist für diesen Benutzer nicht aktiviert.", + "error_token_exchange": "Anmeldung konnte nicht abgeschlossen werden. Bitte versuchen Sie es erneut.", + "error_user_not_found": "Benutzer nicht gefunden. Bitte überprüfen und erneut versuchen.", + "looking_up": "Wird gesucht...", + "optional": "optional", + "page_subtitle": "Geben Sie Ihren Benutzernamen ein, um die Anmeldeoptionen Ihrer Organisation zu finden.", + "page_title": "Single Sign-On", + "provider_oidc": "OpenID Connect (OIDC)", + "provider_saml": "SAML 2.0", + "sign_in_button": "Mit SSO anmelden", + "sign_in_title": "Anmelden", + "sso_button": "SSO-Anmeldung" + }, + "login": { + "branding_subtitle": "Leistungsstarke Leitstellensoftware für Feuerwehren, Rettungsdienste und Organisationen der öffentlichen Sicherheit.", + "branding_title": "Einsatzmanagement", + "errorModal": { + "confirmButton": "OK", + "message": "Bitte überprüfen Sie Ihren Benutzernamen und Ihr Passwort und versuchen Sie es erneut.", + "title": "Anmeldung fehlgeschlagen" + }, + "feature_dispatch_desc": "Disponieren Sie Einheiten sofort und verwalten Sie Einsätze mit Live-Aktualisierungen auf allen Geräten.", + "feature_dispatch_title": "Echtzeit-Disposition", + "feature_mapping_desc": "Verfolgen Sie Einheiten in Echtzeit mit detaillierten Karten, Routenführung und Standortverwaltung.", + "feature_mapping_title": "Erweiterte Kartendarstellung", + "feature_personnel_desc": "Verwalten Sie Ihr Team mit rollenbasiertem Zugriff, Statusverfolgung und Kommunikationswerkzeugen.", + "feature_personnel_title": "Personalverwaltung", + "footer_text": "Erstellt mit ❤️ in Lake Tahoe", + "login": "Anmelden", + "login_button": "Anmelden", + "login_button_description": "Melden Sie sich bei Ihrem Konto an, um fortzufahren", + "login_button_error": "Fehler bei der Anmeldung", + "login_button_loading": "Anmeldung läuft...", + "login_button_success": "Erfolgreich angemeldet", + "no_account": "Noch kein Konto?", + "page_subtitle": "Geben Sie Ihre Anmeldedaten ein.", + "page_title": "Resgrid Dispatch", + "password": "Passwort", + "password_incorrect": "Passwort war falsch", + "password_placeholder": "Passwort eingeben", + "register": "Registrieren", + "title": "Anmeldung", + "username": "Benutzername", + "username_placeholder": "Benutzernamen eingeben", + "welcome_title": "Willkommen zurück" + }, + "lockscreen": { + "message": "Geben Sie Ihr Passwort ein, um den Bildschirm zu entsperren", + "not_you": "Nicht Sie? Zurück zur Anmeldung", + "password": "Passwort", + "password_placeholder": "Passwort eingeben", + "title": "Bildschirmsperre", + "unlock_button": "Entsperren", + "unlock_failed": "Entsperren fehlgeschlagen. Bitte versuchen Sie es erneut.", + "unlocking": "Wird entsperrt...", + "welcome_back": "Willkommen zurück" + }, + "maintenance": { + "downtime_message": "Wir arbeiten daran, die Wartung so schnell wie möglich abzuschließen. Bitte schauen Sie bald wieder vorbei.", + "downtime_title": "Wie lange dauert die Ausfallzeit?", + "message": "Bitte schauen Sie später wieder vorbei.", + "support_message": "Wenn Sie Hilfe benötigen, kontaktieren Sie uns unter", + "support_title": "Benötigen Sie Unterstützung?", + "title": "Wartungsarbeiten", + "why_down_message": "Wir führen geplante Wartungsarbeiten durch, um Ihre Erfahrung zu verbessern. Wir entschuldigen uns für etwaige Unannehmlichkeiten.", + "why_down_title": "Warum ist die Seite nicht erreichbar?" + }, + "menu": { + "calls": "Einsätze", + "calls_list": "Einsatzliste", + "contacts": "Kontakte", + "home": "Startseite", + "map": "Karte", + "menu": "Menü", + "messages": "Nachrichten", + "new_call": "Neuer Einsatz", + "personnel": "Personal", + "protocols": "Protokolle", + "settings": "Einstellungen", + "units": "Einheiten", + "weatherAlerts": "Wetterwarnungen" + }, + "map": { + "call_set_as_current": "Einsatz als aktuellen Einsatz gesetzt", + "failed_to_open_maps": "Kartenanwendung konnte nicht geöffnet werden", + "failed_to_set_current_call": "Einsatz konnte nicht als aktueller Einsatz gesetzt werden", + "layers": "Kartenebenen", + "no_layers": "Keine Ebenen verfügbar", + "no_location_for_routing": "Keine Standortdaten für die Routenführung verfügbar", + "pin_color": "Stecknadel-Farbe", + "recenter_map": "Karte zentrieren", + "set_as_current_call": "Als aktuellen Einsatz setzen", + "show_all": "Alle anzeigen", + "hide_all": "Alle ausblenden", + "view_call_details": "Einsatzdetails anzeigen" + }, + "notes": { + "actions": { + "add": "Notiz hinzufügen", + "delete_confirm": "Sind Sie sicher, dass Sie diese Notiz löschen möchten?" + }, + "details": { + "close": "Schließen", + "created": "Erstellt", + "delete": "Löschen", + "edit": "Bearbeiten", + "tags": "Schlagwörter", + "title": "Notizdetails", + "updated": "Aktualisiert" + }, + "empty": "Keine Notizen gefunden", + "emptyDescription": "Für Ihre Abteilung wurden noch keine Notizen erstellt.", + "search": "Notizen durchsuchen...", + "title": "Notizen" + }, + "personnel": { + "title": "Personal", + "search": "Personal suchen...", + "loading": "Personal wird geladen...", + "empty": "Kein Personal gefunden", + "empty_description": "In Ihrer Abteilung ist derzeit kein Personal verfügbar.", + "no_results": "Kein Personal entspricht Ihrer Suche", + "no_results_description": "Versuchen Sie, Ihre Suchbegriffe anzupassen.", + "status": "Status", + "staffing": "Besetzung", + "group": "Gruppe", + "roles": "Rollen", + "email": "E-Mail", + "phone": "Telefon", + "id_number": "ID-Nummer", + "responding_to": "Reagiert auf", + "status_updated": "Status aktualisiert", + "staffing_updated": "Besetzung aktualisiert", + "details": "Personaldetails", + "contact_info": "Kontaktinformationen", + "status_info": "Status und Besetzung", + "no_email": "Keine E-Mail verfügbar", + "no_phone": "Kein Telefon verfügbar", + "no_group": "Nicht zugewiesen", + "no_roles": "Keine Rollen zugewiesen", + "unknown_status": "Unbekannt", + "on_duty": "Im Dienst", + "off_duty": "Außer Dienst", + "call_phone": "Anrufen", + "send_email": "E-Mail", + "custom_fields": "Zusätzliche Informationen" + }, + "onboarding": { + "screen1": { + "title": "Resgrid Dispatch", + "description": "Erstellen, disponieren und verwalten Sie Einsätze mit einer leistungsstarken mobilen Leitstelle direkt in Ihrer Hand" + }, + "screen2": { + "title": "Echtzeit-Lagebild", + "description": "Verfolgen Sie alle Einheiten, Einsatzkräfte und Ressourcen auf einer interaktiven Karte mit Live-Statusaktualisierungen und AVL" + }, + "screen3": { + "title": "Nahtlose Koordination", + "description": "Kommunizieren Sie sofort mit Einheiten im Feld, aktualisieren Sie den Einsatzstatus und koordinieren Sie den Einsatz von überall aus" + }, + "skip": "Überspringen", + "next": "Weiter", + "getStarted": "Los geht's" + }, + "protocols": { + "details": { + "close": "Schließen", + "code": "Code", + "created": "Erstellt", + "title": "Protokolldetails", + "updated": "Aktualisiert" + }, + "empty": "Keine Protokolle gefunden", + "emptyDescription": "Für Ihre Abteilung wurden noch keine Protokolle erstellt.", + "search": "Protokolle durchsuchen...", + "title": "Protokolle" + }, + "push_notifications": { + "close": "Schließen", + "message": "Nachricht", + "new_notification": "Neue Benachrichtigung", + "title": "Titel", + "types": { + "call": "Einsatz", + "chat": "Chat", + "group_chat": "Gruppenchat", + "message": "Nachricht", + "notification": "Benachrichtigung" + }, + "unknown_type_warning": "Unbekannter Benachrichtigungstyp empfangen", + "view_call": "Einsatz anzeigen", + "view_message": "Nachricht anzeigen" + }, + "roles": { + "modal": { + "title": "Rollenzuweisung der Einheit" + }, + "selectUser": "Benutzer auswählen", + "status": "{{active}} von {{total}} Rollen", + "tap_to_manage": "Tippen, um Rollen zu verwalten", + "unassigned": "Nicht zugewiesen" + }, + "settings": { + "about": "Über", + "account": "Konto", + "active_unit": "Aktive Einheit", + "app_info": "App-Info", + "app_name": "App-Name", + "arabic": "Arabisch", + "audio_device_selection": { + "bluetooth_device": "Bluetooth-Gerät", + "current_selection": "Aktuelle Auswahl", + "microphone": "Mikrofon", + "no_microphones_available": "Keine Mikrofone verfügbar", + "no_speakers_available": "Keine Lautsprecher verfügbar", + "none_selected": "Keine Auswahl", + "speaker": "Lautsprecher", + "speaker_device": "Lautsprechergerät", + "title": "Audiogeräteauswahl", + "unavailable": "Nicht verfügbar", + "wired_device": "Kabelgebundenes Gerät" + }, + "background_geolocation": "Hintergrund-Geolokalisierung", + "background_geolocation_warning": "Diese Funktion ermöglicht es der App, Ihren Standort im Hintergrund zu verfolgen. Sie hilft bei der Koordination von Einsätzen, kann aber die Akkulaufzeit beeinträchtigen.", + "background_location": "Hintergrund-Standort", + "contact_us": "Kontaktieren Sie uns", + "current_unit": "Aktuelle Einheit", + "arabic": "Arabisch", + "english": "Englisch", + "french": "Französisch", + "german": "Deutsch", + "italian": "Italienisch", + "enter_password": "Passwort eingeben", + "enter_server_url": "Resgrid-API-URL eingeben (z. B. https://api.resgrid.com)", + "enter_username": "Benutzernamen eingeben", + "environment": "Umgebung", + "general": "Allgemein", + "generale": "Allgemein", + "github": "Github", + "help_center": "Hilfezentrum", + "keep_alive": "Dauerbetrieb", + "keep_alive_warning": "Warnung: Durch die Aktivierung des Dauerbetriebs wird der Ruhemodus Ihres Geräts verhindert und der Akkuverbrauch kann erheblich steigen.", + "keep_screen_on": "Bildschirm anlassen", + "language": "Sprache", + "links": "Links", + "login_info": "Anmeldedaten", + "logout": "Abmelden", + "more": "Mehr", + "no_units_available": "Keine Einheiten verfügbar", + "none_selected": "Keine Auswahl", + "notifications": "Push-Benachrichtigungen", + "notifications_description": "Aktivieren Sie Benachrichtigungen, um Alarme und Aktualisierungen zu erhalten", + "notifications_enable": "Benachrichtigungen aktivieren", + "password": "Passwort", + "preferences": "Einstellungen", + "privacy": "Datenschutzrichtlinie", + "privacy_policy": "Datenschutzrichtlinie", + "rate": "Bewerten", + "select_unit": "Einheit auswählen", + "server": "Server", + "server_url": "Server-URL", + "server_url_note": "Hinweis: Dies ist die URL der Resgrid-API. Sie wird verwendet, um eine Verbindung zum Resgrid-Server herzustellen. Fügen Sie /api/v4 oder einen abschließenden Schrägstrich nicht in die URL ein.", + "set_active_unit": "Aktive Einheit setzen", + "share": "Teilen", + "polish": "Polnisch", + "spanish": "Spanisch", + "swedish": "Schwedisch", + "ukrainian": "Ukrainisch", + "status_page": "Systemstatus", + "support": "Support", + "support_us": "Unterstützen Sie uns", + "terms": "Nutzungsbedingungen", + "theme": { + "dark": "Dunkel", + "light": "Hell", + "system": "System", + "title": "Design" + }, + "title": "Einstellungen", + "unit_selected_successfully": "{{unitName}} erfolgreich ausgewählt", + "unit_selection": "Einheitenauswahl", + "unit_selection_failed": "Einheit konnte nicht ausgewählt werden. Bitte versuchen Sie es erneut.", + "username": "Benutzername", + "version": "Version", + "website": "Webseite" + }, + "status": { + "add_note": "Notiz hinzufügen", + "both_destinations_enabled": "Kann auf Einsätze oder Wachen reagieren", + "call_destination_enabled": "Kann auf Einsätze reagieren", + "calls_tab": "Einsätze", + "failed_to_save_status": "Status konnte nicht gespeichert werden. Bitte versuchen Sie es erneut.", + "general_status": "Allgemeiner Status ohne spezifisches Ziel", + "loading_stations": "Wachen werden geladen...", + "no_destination": "Kein Ziel", + "no_stations_available": "Keine Wachen verfügbar", + "no_statuses_available": "Keine Status verfügbar", + "note": "Notiz", + "note_optional": "Optionale Notiz zu dieser Statusänderung hinzufügen", + "note_required": "Bitte geben Sie eine Notiz für diese Statusänderung ein", + "select_destination": "Ziel für {{status}} auswählen", + "select_destination_type": "Wohin möchten Sie reagieren?", + "select_status": "Status auswählen", + "select_status_type": "Welchen Status möchten Sie setzen?", + "selected_destination": "Ausgewähltes Ziel", + "selected_status": "Ausgewählter Status", + "set_status": "Status setzen", + "station_destination_enabled": "Kann auf Wachen reagieren", + "stations_tab": "Wachen", + "status_saved_successfully": "Status erfolgreich gespeichert!" + }, + "dispatch": { + "active_calls": "Aktive Einsätze", + "pending_calls": "Ausstehend", + "scheduled_calls": "Geplant", + "units_available": "Verfügbar", + "personnel_available": "Verfügbar", + "personnel_on_duty": "Im Dienst", + "units": "Einheiten", + "personnel": "Personal", + "map": "Karte", + "notes": "Notizen", + "activity_log": "Aktivitätsprotokoll", + "communications": "Kommunikation", + "no_active_calls": "Keine aktiven Einsätze", + "no_units": "Keine Einheiten verfügbar", + "no_personnel": "Kein Personal verfügbar", + "no_notes": "Keine Notizen verfügbar", + "no_activity": "Keine aktuelle Aktivität", + "current_channel": "Aktueller Kanal", + "audio_stream": "Audiostream", + "no_stream": "Kein Stream aktiv", + "ptt": "PTT", + "ptt_start": "PTT Start", + "ptt_end": "PTT Ende", + "transmitting_on": "Senden auf {{channel}}", + "transmission_ended": "Übertragung beendet", + "voice_disabled": "Sprache deaktiviert", + "disconnected": "Getrennt", + "select_channel": "Kanal auswählen", + "select_channel_description": "Wählen Sie einen Sprachkanal zum Verbinden", + "change_channel_warning": "Das Auswählen eines neuen Kanals trennt die Verbindung zum aktuellen Kanal", + "default_channel": "Standard", + "no_channels_available": "Keine Sprachkanäle verfügbar", + "system_update": "Systemaktualisierung", + "data_refreshed": "Daten vom Server aktualisiert", + "call_selected": "Einsatz ausgewählt", + "unit_selected": "Einheit ausgewählt", + "unit_deselected": "Einheit abgewählt", + "personnel_selected": "Personal ausgewählt", + "personnel_deselected": "Personal abgewählt", + "loading_map": "Karte wird geladen...", + "map_not_available_web": "Karte auf der Webplattform nicht verfügbar", + "filtering_by_call": "Filterung nach Einsatz", + "clear_filter": "Filter löschen", + "call_filter_active": "Einsatzfilter aktiv", + "call_filter_cleared": "Einsatzfilter gelöscht", + "showing_all_data": "Alle Daten werden angezeigt", + "call_notes": "Einsatznotizen", + "no_call_notes": "Keine Einsatznotizen", + "add_call_note_placeholder": "Notiz hinzufügen...", + "note_added": "Notiz hinzugefügt", + "note_added_to_console": "Eine neue Notiz wurde zur Konsole hinzugefügt", + "add_note_title": "Neue Notiz hinzufügen", + "note_title_label": "Titel", + "note_title_placeholder": "Notiztitel eingeben...", + "note_category_label": "Kategorie", + "note_category_placeholder": "Kategorie auswählen", + "note_no_category": "Keine Kategorie", + "note_body_label": "Notizinhalt", + "note_body_placeholder": "Notizinhalt eingeben...", + "note_save_error": "Notiz konnte nicht gespeichert werden: {{error}}", + "note_created": "Notiz erstellt", + "units_on_call": "Einheiten im Einsatz", + "no_units_on_call": "Keine Einheiten im Einsatz", + "personnel_on_call": "Personal im Einsatz", + "no_personnel_on_call": "Kein Personal im Einsatz", + "call_activity": "Einsatzaktivität", + "no_call_activity": "Keine Einsatzaktivität", + "on_call": "Im Einsatz", + "filtered": "Gefiltert", + "active_filter": "Aktiver Filter", + "unit_status_change": "Statusänderung der Einheit", + "personnel_status_change": "Statusänderung des Personals", + "view_call_details": "Einsatzdetails anzeigen", + "dispatched_resources": "Disponiert", + "unassigned": "Nicht zugewiesen", + "available": "Verfügbar", + "unknown": "Unbekannt", + "search_personnel_placeholder": "Personal durchsuchen...", + "search_calls_placeholder": "Einsätze durchsuchen...", + "search_units_placeholder": "Einheiten durchsuchen...", + "search_notes_placeholder": "Notizen durchsuchen...", + "signalr_update": "Echtzeit-Aktualisierung", + "signalr_connected": "Verbunden", + "realtime_updates_active": "Echtzeit-Aktualisierungen sind jetzt aktiv", + "personnel_status_updated": "Personalstatus aktualisiert", + "personnel_staffing_updated": "Personalbesetzung aktualisiert", + "unit_status_updated": "Einheitenstatus aktualisiert", + "calls_updated": "Einsätze aktualisiert", + "call_added": "Neuer Einsatz hinzugefügt", + "call_closed": "Einsatz geschlossen", + "check_ins": "Kontrollen", + "no_check_ins": "Keine Einsätze mit Kontroll-Timern", + "radio_log": "Funkprotokoll", + "radio": "Funk", + "activity": "Aktivität", + "actions": "Aktionen", + "no_radio_activity": "Keine Funkübertragungen", + "live": "LIVE", + "currently_transmitting": "Sendet gerade...", + "duration": "Dauer", + "call_actions": "Einsatzaktionen", + "unit_actions": "Einheitenaktionen", + "personnel_actions": { + "title": "Personalaktionen", + "status_tab": "Status", + "staffing_tab": "Besetzung", + "select_status": "Status auswählen", + "select_staffing": "Besetzungsstufe auswählen", + "destination": "Ziel", + "no_destination": "Kein Ziel", + "note": "Notiz", + "note_placeholder": "Optionale Notiz hinzufügen...", + "update_status": "Status aktualisieren", + "update_staffing": "Besetzung aktualisieren", + "no_statuses_available": "Keine Status verfügbar", + "no_staffings_available": "Keine Besetzungsstufen verfügbar" + }, + "unit_actions_panel": { + "status": "Status", + "select_status": "Status auswählen", + "destination": "Ziel", + "no_destination": "Kein Ziel", + "note": "Notiz", + "note_placeholder": "Optionale Notiz hinzufügen...", + "update_status": "Status aktualisieren", + "no_statuses_available": "Keine Status verfügbar", + "no_active_calls": "Keine aktiven Einsätze", + "no_stations_available": "Keine Wachen verfügbar", + "no_destinations_available": "Keine Ziele verfügbar" + }, + "call": "Einsatz", + "station": "Wache", + "calls": "Einsätze", + "stations": "Wachen", + "no_stations_available": "Keine Wachen verfügbar", + "new_call": "Neuer Einsatz", + "view_details": "Details", + "add_note": "Notiz hinzufügen", + "close_call": "Schließen", + "set_status": "Status setzen", + "set_staffing": "Besetzung", + "dispatch": "Disponieren", + "select_items_for_actions": "Wählen Sie einen Einsatz, eine Einheit oder Personal aus, um kontextbezogene Aktionen zu aktivieren", + "weather": { + "clear": "Klar", + "mainly_clear": "Überwiegend klar", + "partly_cloudy": "Teilweise bewölkt", + "overcast": "Bedeckt", + "fog": "Nebel", + "drizzle": "Nieselregen", + "freezing_drizzle": "Gefrierender Nieselregen", + "rain": "Regen", + "freezing_rain": "Gefrierender Regen", + "snow": "Schnee", + "rain_showers": "Regenschauer", + "snow_showers": "Schneeschauer", + "thunderstorm": "Gewitter", + "thunderstorm_hail": "Gewitter mit Hagel", + "unknown": "Unbekannt" + } + }, + "tabs": { + "calls": "Einsätze", + "calendar": "Kalender", + "contacts": "Kontakte", + "home": "Startseite", + "map": "Karte", + "messages": "Nachrichten", + "notes": "Notizen", + "protocols": "Protokolle", + "settings": "Einstellungen", + "shifts": "Schichten", + "personnel": "Personal" + }, + "check_in": { + "tab_title": "Kontrolle", + "timer_status": "Timer-Status", + "perform_check_in": "Kontrolle durchführen", + "check_in_success": "Kontrolle erfolgreich erfasst", + "check_in_error": "Kontrolle konnte nicht erfasst werden", + "checked_in_by": "von {{name}}", + "last_check_in": "Letzte Kontrolle", + "elapsed": "Vergangen", + "duration": "Dauer", + "status_ok": "OK", + "status_green": "OK", + "status_warning": "Warnung", + "status_yellow": "Warnung", + "status_overdue": "Überfällig", + "status_red": "Überfällig", + "status_critical": "Kritisch", + "history": "Kontrollverlauf", + "no_timers": "Keine Kontroll-Timer konfiguriert", + "timers_disabled": "Kontroll-Timer sind für diesen Einsatz deaktiviert", + "type_personnel": "Personal", + "type_unit": "Einheit", + "type_ic": "Einsatzleiter", + "type_par": "PAR", + "type_hazmat": "Gefahrstoffexposition", + "type_sector_rotation": "Abschnittsrotation", + "type_rehab": "Erholung", + "add_note": "Notiz hinzufügen (optional)", + "confirm": "Kontrolle bestätigen", + "minutes_ago": "vor {{count}} Min.", + "select_target": "Einheit für Kontrolle auswählen", + "overdue_count": "{{count}} Überfällig", + "warning_count": "{{count}} Warnung", + "enable_timers": "Timer aktivieren", + "disable_timers": "Timer deaktivieren", + "summary": "{{overdue}} überfällig, {{warning}} Warnung, {{ok}} ok" + }, + "units": { + "title": "Einheiten" + }, + "weatherAlerts": { + "title": "Wetterwarnungen", + "activeAlerts": "Aktive Warnungen", + "noActiveAlerts": "Keine aktiven Wetterwarnungen", + "moreAlerts": "+{{count}} weitere", + "severity": { + "extreme": "Extrem", + "severe": "Schwer", + "moderate": "Mäßig", + "minor": "Gering", + "unknown": "Unbekannt" + }, + "category": { + "met": "Meteorologisch", + "fire": "Feuer", + "health": "Gesundheit", + "env": "Umwelt", + "other": "Sonstige" + }, + "urgency": { + "immediate": "Sofort", + "expected": "Erwartet", + "future": "Zukünftig", + "past": "Vergangen", + "unknown": "Unbekannt" + }, + "certainty": { + "observed": "Beobachtet", + "likely": "Wahrscheinlich", + "possible": "Möglich", + "unlikely": "Unwahrscheinlich", + "unknown": "Unbekannt" + }, + "status": { + "active": "Aktiv", + "updated": "Aktualisiert", + "expired": "Abgelaufen", + "cancelled": "Aufgehoben" + }, + "detail": { + "headline": "Überschrift", + "description": "Beschreibung", + "instruction": "Anweisungen", + "area": "Betroffenes Gebiet", + "effective": "Gültig ab", + "onset": "Beginn", + "expires": "Läuft ab", + "sent": "Gesendet", + "sender": "Quelle", + "urgency": "Dringlichkeit", + "certainty": "Wahrscheinlichkeit" + }, + "filter": { + "all": "Alle", + "nearby": "In der Nähe" + }, + "sort": { + "severity": "Schweregrad", + "expires": "Bald ablaufend", + "newest": "Neueste" + }, + "banner": { + "viewAll": "Alle anzeigen" + } + }, + "videoFeeds": { + "title": "Videoübertragungen", + "noFeeds": "Keine Videoübertragungen für diesen Einsatz", + "addFeed": "Videoübertragung hinzufügen", + "editFeed": "Videoübertragung bearbeiten", + "deleteFeed": "Videoübertragung löschen", + "deleteConfirm": "Sind Sie sicher, dass Sie diese Videoübertragung entfernen möchten?", + "watch": "Ansehen", + "goLive": "Live gehen", + "stopLive": "Live beenden", + "flipCamera": "Kamera wechseln", + "feedAdded": "Videoübertragung hinzugefügt", + "feedUpdated": "Videoübertragung aktualisiert", + "feedDeleted": "Videoübertragung entfernt", + "feedError": "Videoübertragung konnte nicht geladen werden", + "unsupportedFormat": "Dieses Streamformat wird auf Mobilgeräten nicht unterstützt", + "copyUrl": "URL kopieren", + "form": { + "name": "Name der Übertragung", + "namePlaceholder": "z. B. Löschfahrzeug 1 Drohne", + "url": "Stream-URL", + "urlPlaceholder": "z. B. https://stream.example.com/live.m3u8", + "feedType": "Kameratyp", + "feedFormat": "Streamformat", + "description": "Beschreibung", + "descriptionPlaceholder": "Optionale Beschreibung", + "status": "Status", + "sortOrder": "Sortierreihenfolge", + "cameraLocation": "Kamerastandort", + "useCurrentLocation": "Aktuellen Standort verwenden" + }, + "type": { + "drone": "Drohne", + "fixedCamera": "Festkamera", + "bodyCam": "Körperkamera", + "trafficCam": "Verkehrskamera", + "weatherCam": "Wetterkamera", + "satelliteFeed": "Satellitenübertragung", + "webCam": "Webcam", + "other": "Sonstige" + }, + "format": { + "rtsp": "RTSP", + "hls": "HLS", + "mjpeg": "MJPEG", + "youtubeLive": "YouTube Live", + "webrtc": "WebRTC", + "dash": "DASH", + "embed": "Einbetten", + "other": "Sonstige" + }, + "status": { + "active": "Aktiv", + "inactive": "Inaktiv", + "error": "Fehler" + } + }, + "welcome": "Willkommen bei der obytes App-Seite" +} diff --git a/src/translations/en.json b/src/translations/en.json index 9cbbba4..a8f138c 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -177,7 +177,8 @@ "dispatched": "Dispatched", "info": "Info", "protocols": "Protocols", - "timeline": "Activity" + "timeline": "Activity", + "video": "Video" }, "timestamp": "Timestamp", "title": "Call Details", @@ -578,7 +579,8 @@ "personnel": "Personnel", "protocols": "Protocols", "settings": "Settings", - "units": "Units" + "units": "Units", + "weatherAlerts": "Weather Alerts" }, "map": { "call_set_as_current": "Call set as current call", @@ -613,6 +615,38 @@ "search": "Search notes...", "title": "Notes" }, + "personnel": { + "title": "Personnel", + "search": "Search personnel...", + "loading": "Loading personnel...", + "empty": "No personnel found", + "empty_description": "No personnel are currently available in your department.", + "no_results": "No personnel match your search", + "no_results_description": "Try adjusting your search terms.", + "status": "Status", + "staffing": "Staffing", + "group": "Group", + "roles": "Roles", + "email": "Email", + "phone": "Phone", + "id_number": "ID Number", + "responding_to": "Responding To", + "status_updated": "Status Updated", + "staffing_updated": "Staffing Updated", + "details": "Personnel Details", + "contact_info": "Contact Information", + "status_info": "Status & Staffing", + "no_email": "No email available", + "no_phone": "No phone available", + "no_group": "Unassigned", + "no_roles": "No roles assigned", + "unknown_status": "Unknown", + "on_duty": "On Duty", + "off_duty": "Off Duty", + "call_phone": "Call", + "send_email": "Email", + "custom_fields": "Additional Information" + }, "onboarding": { "screen1": { "title": "Resgrid Dispatch", @@ -693,7 +727,11 @@ "background_location": "Background Location", "contact_us": "Contact Us", "current_unit": "Current Unit", + "arabic": "Arabic", "english": "English", + "french": "French", + "german": "German", + "italian": "Italian", "enter_password": "Enter your password", "enter_server_url": "Enter Resgrid API URL (e.g., https://api.resgrid.com)", "enter_username": "Enter your username", @@ -726,7 +764,10 @@ "server_url_note": "Note: This is the URL of the Resgrid API. It is used to connect to the Resgrid server. Do not include /api/v4 in the URL or a trailing slash.", "set_active_unit": "Set Active Unit", "share": "Share", + "polish": "Polish", "spanish": "Spanish", + "swedish": "Swedish", + "ukrainian": "Ukrainian", "status_page": "System Status", "support": "Support", "support_us": "Support Us", @@ -861,6 +902,8 @@ "calls_updated": "Calls updated", "call_added": "New call added", "call_closed": "Call closed", + "check_ins": "Check-Ins", + "no_check_ins": "No calls with check-in timers", "radio_log": "Radio Log", "radio": "Radio", "activity": "Activity", @@ -943,8 +986,193 @@ "shifts": "Shifts", "personnel": "Personnel" }, + "check_in": { + "tab_title": "Check-In", + "timer_status": "Timer Status", + "perform_check_in": "Check In", + "check_in_success": "Check-in recorded successfully", + "check_in_error": "Failed to record check-in", + "checked_in_by": "by {{name}}", + "last_check_in": "Last check-in", + "elapsed": "Elapsed", + "duration": "Duration", + "status_ok": "OK", + "status_green": "OK", + "status_warning": "Warning", + "status_yellow": "Warning", + "status_overdue": "Overdue", + "status_red": "Overdue", + "status_critical": "Critical", + "history": "Check-In History", + "no_timers": "No check-in timers configured", + "timers_disabled": "Check-in timers are disabled for this call", + "type_personnel": "Personnel", + "type_unit": "Unit", + "type_ic": "Incident Commander", + "type_par": "PAR", + "type_hazmat": "Hazmat Exposure", + "type_sector_rotation": "Sector Rotation", + "type_rehab": "Rehab", + "add_note": "Add Note (Optional)", + "confirm": "Confirm Check-In", + "minutes_ago": "{{count}} min ago", + "select_target": "Select Entity to Check In", + "overdue_count": "{{count}} Overdue", + "warning_count": "{{count}} Warning", + "enable_timers": "Enable Timers", + "disable_timers": "Disable Timers", + "summary": "{{overdue}} overdue, {{warning}} warning, {{ok}} ok" + }, "units": { - "title": "Units" + "title": "Units", + "search": "Search units...", + "loading": "Loading units...", + "empty": "No units found", + "empty_description": "No units are currently available in your department.", + "no_results": "No units match your search", + "no_results_description": "Try adjusting your search terms.", + "details": "Unit Details", + "status": "Status", + "group": "Group", + "type": "Type", + "vin": "VIN", + "plate_number": "Plate Number", + "four_wheel_drive": "4WD", + "special_permit": "Special Permit", + "yes": "Yes", + "no": "No", + "destination": "Destination", + "status_updated": "Status Updated", + "roles": "Assigned Roles", + "no_roles": "No roles assigned", + "unit_info": "Unit Information", + "status_info": "Status Information", + "vehicle_info": "Vehicle Information", + "custom_fields": "Additional Information", + "unknown_status": "Unknown", + "no_destination": "None" + }, + "weatherAlerts": { + "title": "Weather Alerts", + "activeAlerts": "Active Alerts", + "noActiveAlerts": "No active weather alerts", + "moreAlerts": "+{{count}} more", + "severity": { + "extreme": "Extreme", + "severe": "Severe", + "moderate": "Moderate", + "minor": "Minor", + "unknown": "Unknown" + }, + "category": { + "met": "Meteorological", + "fire": "Fire", + "health": "Health", + "env": "Environmental", + "other": "Other" + }, + "urgency": { + "immediate": "Immediate", + "expected": "Expected", + "future": "Future", + "past": "Past", + "unknown": "Unknown" + }, + "certainty": { + "observed": "Observed", + "likely": "Likely", + "possible": "Possible", + "unlikely": "Unlikely", + "unknown": "Unknown" + }, + "status": { + "active": "Active", + "updated": "Updated", + "expired": "Expired", + "cancelled": "Cancelled" + }, + "detail": { + "headline": "Headline", + "description": "Description", + "instruction": "Instructions", + "area": "Affected Area", + "effective": "Effective", + "onset": "Onset", + "expires": "Expires", + "sent": "Sent", + "sender": "Source", + "urgency": "Urgency", + "certainty": "Certainty" + }, + "filter": { + "all": "All", + "nearby": "Nearby" + }, + "sort": { + "severity": "Severity", + "expires": "Expiring Soon", + "newest": "Newest" + }, + "banner": { + "viewAll": "View All" + } + }, + "videoFeeds": { + "title": "Video Feeds", + "noFeeds": "No video feeds for this call", + "addFeed": "Add Video Feed", + "editFeed": "Edit Video Feed", + "deleteFeed": "Delete Video Feed", + "deleteConfirm": "Are you sure you want to remove this video feed?", + "watch": "Watch", + "goLive": "Go Live", + "stopLive": "Stop Live", + "flipCamera": "Flip Camera", + "feedAdded": "Video feed added", + "feedUpdated": "Video feed updated", + "feedDeleted": "Video feed removed", + "feedError": "Failed to load video feed", + "unsupportedFormat": "This stream format is not supported on mobile", + "copyUrl": "Copy URL", + "form": { + "name": "Feed Name", + "namePlaceholder": "e.g. Engine 1 Drone", + "url": "Stream URL", + "urlPlaceholder": "e.g. https://stream.example.com/live.m3u8", + "feedType": "Camera Type", + "feedFormat": "Stream Format", + "description": "Description", + "descriptionPlaceholder": "Optional description", + "status": "Status", + "sortOrder": "Sort Order", + "cameraLocation": "Camera Location", + "useCurrentLocation": "Use Current Location" + }, + "type": { + "drone": "Drone", + "fixedCamera": "Fixed Camera", + "bodyCam": "Body Cam", + "trafficCam": "Traffic Cam", + "weatherCam": "Weather Cam", + "satelliteFeed": "Satellite Feed", + "webCam": "Web Cam", + "other": "Other" + }, + "format": { + "rtsp": "RTSP", + "hls": "HLS", + "mjpeg": "MJPEG", + "youtubeLive": "YouTube Live", + "webrtc": "WebRTC", + "dash": "DASH", + "embed": "Embed", + "other": "Other" + }, + "status": { + "active": "Active", + "inactive": "Inactive", + "error": "Error" + } }, "welcome": "Welcome to obytes app site" } diff --git a/src/translations/es.json b/src/translations/es.json index 3e3c133..1af8ae6 100644 --- a/src/translations/es.json +++ b/src/translations/es.json @@ -177,7 +177,8 @@ "dispatched": "Despachadas", "info": "Información", "protocols": "Protocolos", - "timeline": "Actividad" + "timeline": "Actividad", + "video": "Video" }, "timestamp": "Marca de tiempo", "title": "Detalles de la llamada", @@ -545,6 +546,27 @@ "username_placeholder": "Introduce tu nombre de usuario", "welcome_title": "Bienvenido de Nuevo" }, + "lockscreen": { + "message": "Ingrese su contraseña para desbloquear la pantalla", + "not_you": "¿No eres tú? Volver al inicio de sesión", + "password": "Contraseña", + "password_placeholder": "Ingrese su contraseña", + "title": "Pantalla de Bloqueo", + "unlock_button": "Desbloquear", + "unlock_failed": "Error al desbloquear. Por favor, inténtelo de nuevo.", + "unlocking": "Desbloqueando...", + "welcome_back": "Bienvenido de Nuevo" + }, + "maintenance": { + "downtime_message": "Estamos trabajando arduamente para completar el mantenimiento lo antes posible. Por favor, vuelva pronto.", + "downtime_title": "¿Cuál es el tiempo de inactividad?", + "message": "Por favor, vuelva en algún momento.", + "support_message": "Si necesita asistencia, contáctenos en", + "support_title": "¿Necesita soporte?", + "title": "Sitio en Mantenimiento", + "why_down_message": "Estamos realizando mantenimiento programado para mejorar su experiencia. Nos disculpamos por cualquier inconveniente.", + "why_down_title": "¿Por qué está el sitio inactivo?" + }, "map": { "call_set_as_current": "Llamada establecida como llamada actual", "failed_to_open_maps": "Error al abrir la aplicación de mapas", @@ -558,7 +580,11 @@ "search_personnel_placeholder": "Buscar personal...", "search_calls_placeholder": "Buscar llamadas...", "search_units_placeholder": "Buscar unidades...", - "search_notes_placeholder": "Buscar notas..." + "search_notes_placeholder": "Buscar notas...", + "layers": "Capas del Mapa", + "no_layers": "No hay capas disponibles", + "show_all": "Mostrar Todo", + "hide_all": "Ocultar Todo" }, "notes": { "actions": { @@ -591,7 +617,40 @@ "personnel": "Personal", "protocols": "Protocolos", "settings": "Configuración", - "units": "Unidades" + "units": "Unidades", + "weatherAlerts": "Alertas Meteorológicas" + }, + "personnel": { + "title": "Personal", + "search": "Buscar personal...", + "loading": "Cargando personal...", + "empty": "No se encontró personal", + "empty_description": "No hay personal disponible actualmente en su departamento.", + "no_results": "Ningún personal coincide con su búsqueda", + "no_results_description": "Intente ajustar sus términos de búsqueda.", + "status": "Estado", + "staffing": "Dotación", + "group": "Grupo", + "roles": "Roles", + "email": "Correo electrónico", + "phone": "Teléfono", + "id_number": "Número de ID", + "responding_to": "Respondiendo a", + "status_updated": "Estado actualizado", + "staffing_updated": "Dotación actualizada", + "details": "Detalles del personal", + "contact_info": "Información de contacto", + "status_info": "Estado y dotación", + "no_email": "No hay correo electrónico disponible", + "no_phone": "No hay teléfono disponible", + "no_group": "Sin asignar", + "no_roles": "Sin roles asignados", + "unknown_status": "Desconocido", + "on_duty": "En servicio", + "off_duty": "Fuera de servicio", + "call_phone": "Llamar", + "send_email": "Correo", + "custom_fields": "Información adicional" }, "onboarding": { "screen1": { @@ -673,7 +732,11 @@ "background_location": "Ubicación en segundo plano", "contact_us": "Contáctanos", "current_unit": "Unidad Actual", + "arabic": "Árabe", "english": "Inglés", + "french": "Francés", + "german": "Alemán", + "italian": "Italiano", "enter_password": "Introduce tu contraseña", "enter_server_url": "Introduce la URL de la API de Resgrid (ej: https://api.resgrid.com)", "enter_username": "Introduce tu nombre de usuario", @@ -706,7 +769,10 @@ "server_url_note": "Nota: Esta es la URL de la API de Resgrid. Se utiliza para conectarse al servidor de Resgrid. No incluyas /api/v4 en la URL ni una barra diagonal al final.", "set_active_unit": "Establecer unidad activa", "share": "Compartir", + "polish": "Polaco", "spanish": "Español", + "swedish": "Sueco", + "ukrainian": "Ucraniano", "status_page": "Estado del sistema", "support": "Soporte", "support_us": "Apóyanos", @@ -760,7 +826,8 @@ "notes": "Notas", "protocols": "Protocolos", "settings": "Configuración", - "shifts": "Turnos" + "shifts": "Turnos", + "personnel": "Personal" }, "dispatch": { "active_calls": "Llamadas Activas", @@ -853,6 +920,8 @@ "calls_updated": "Llamadas actualizadas", "call_added": "Nueva llamada añadida", "call_closed": "Llamada cerrada", + "check_ins": "Registros", + "no_check_ins": "No hay llamadas con temporizadores de registro", "radio_log": "Registro de Radio", "radio": "Radio", "activity": "Actividad", @@ -903,7 +972,186 @@ "set_status": "Estado", "set_staffing": "Dotación", "dispatch": "Despachar", - "select_items_for_actions": "Selecciona una llamada, unidad o personal para habilitar acciones contextuales" + "select_items_for_actions": "Selecciona una llamada, unidad o personal para habilitar acciones contextuales", + "weather": { + "clear": "Despejado", + "mainly_clear": "Mayormente Despejado", + "partly_cloudy": "Parcialmente Nublado", + "overcast": "Nublado", + "fog": "Niebla", + "drizzle": "Llovizna", + "freezing_drizzle": "Llovizna Helada", + "rain": "Lluvia", + "freezing_rain": "Lluvia Helada", + "snow": "Nieve", + "rain_showers": "Chubascos", + "snow_showers": "Nevadas", + "thunderstorm": "Tormenta Eléctrica", + "thunderstorm_hail": "Tormenta con Granizo", + "unknown": "Desconocido" + } + }, + "check_in": { + "tab_title": "Registro", + "timer_status": "Estado del Temporizador", + "perform_check_in": "Registrar", + "check_in_success": "Registro realizado exitosamente", + "check_in_error": "Error al realizar el registro", + "checked_in_by": "por {{name}}", + "last_check_in": "Último registro", + "elapsed": "Transcurrido", + "duration": "Duración", + "status_ok": "OK", + "status_green": "OK", + "status_warning": "Advertencia", + "status_yellow": "Advertencia", + "status_overdue": "Vencido", + "status_red": "Vencido", + "status_critical": "Crítico", + "history": "Historial de Registros", + "no_timers": "No hay temporizadores de registro configurados", + "timers_disabled": "Los temporizadores de registro están desactivados para esta llamada", + "type_personnel": "Personal", + "type_unit": "Unidad", + "type_ic": "Comandante de Incidente", + "type_par": "PAR", + "type_hazmat": "Exposición a Materiales Peligrosos", + "type_sector_rotation": "Rotación de Sector", + "type_rehab": "Rehabilitación", + "add_note": "Agregar Nota (Opcional)", + "confirm": "Confirmar Registro", + "minutes_ago": "hace {{count}} min", + "select_target": "Seleccionar Entidad para Registrar", + "overdue_count": "{{count}} Vencidos", + "warning_count": "{{count}} Advertencias", + "enable_timers": "Activar Temporizadores", + "disable_timers": "Desactivar Temporizadores", + "summary": "{{overdue}} vencidos, {{warning}} advertencias, {{ok}} ok" + }, + "units": { + "title": "Unidades" + }, + "weatherAlerts": { + "title": "Alertas Meteorológicas", + "activeAlerts": "Alertas Activas", + "noActiveAlerts": "No hay alertas meteorológicas activas", + "moreAlerts": "+{{count}} más", + "severity": { + "extreme": "Extrema", + "severe": "Severa", + "moderate": "Moderada", + "minor": "Menor", + "unknown": "Desconocida" + }, + "category": { + "met": "Meteorológica", + "fire": "Incendio", + "health": "Salud", + "env": "Ambiental", + "other": "Otra" + }, + "urgency": { + "immediate": "Inmediata", + "expected": "Esperada", + "future": "Futura", + "past": "Pasada", + "unknown": "Desconocida" + }, + "certainty": { + "observed": "Observada", + "likely": "Probable", + "possible": "Posible", + "unlikely": "Improbable", + "unknown": "Desconocida" + }, + "status": { + "active": "Activa", + "updated": "Actualizada", + "expired": "Expirada", + "cancelled": "Cancelada" + }, + "detail": { + "headline": "Titular", + "description": "Descripción", + "instruction": "Instrucciones", + "area": "Área Afectada", + "effective": "Vigente", + "onset": "Inicio", + "expires": "Expira", + "sent": "Enviado", + "sender": "Fuente", + "urgency": "Urgencia", + "certainty": "Certeza" + }, + "filter": { + "all": "Todas", + "nearby": "Cercanas" + }, + "sort": { + "severity": "Severidad", + "expires": "Próximas a Expirar", + "newest": "Más Recientes" + }, + "banner": { + "viewAll": "Ver Todas" + } + }, + "videoFeeds": { + "title": "Transmisiones de Video", + "noFeeds": "No hay transmisiones de video para esta llamada", + "addFeed": "Agregar Transmisión", + "editFeed": "Editar Transmisión", + "deleteFeed": "Eliminar Transmisión", + "deleteConfirm": "¿Está seguro de que desea eliminar esta transmisión de video?", + "watch": "Ver", + "goLive": "Transmitir", + "stopLive": "Detener", + "flipCamera": "Voltear Cámara", + "feedAdded": "Transmisión de video agregada", + "feedUpdated": "Transmisión de video actualizada", + "feedDeleted": "Transmisión de video eliminada", + "feedError": "Error al cargar la transmisión de video", + "unsupportedFormat": "Este formato de transmisión no es compatible en dispositivos móviles", + "copyUrl": "Copiar URL", + "form": { + "name": "Nombre de la Transmisión", + "namePlaceholder": "ej. Dron Motor 1", + "url": "URL de Transmisión", + "urlPlaceholder": "ej. https://stream.example.com/live.m3u8", + "feedType": "Tipo de Cámara", + "feedFormat": "Formato de Transmisión", + "description": "Descripción", + "descriptionPlaceholder": "Descripción opcional", + "status": "Estado", + "sortOrder": "Orden", + "cameraLocation": "Ubicación de la Cámara", + "useCurrentLocation": "Usar Ubicación Actual" + }, + "type": { + "drone": "Dron", + "fixedCamera": "Cámara Fija", + "bodyCam": "Cámara Corporal", + "trafficCam": "Cámara de Tráfico", + "weatherCam": "Cámara Meteorológica", + "satelliteFeed": "Transmisión Satelital", + "webCam": "Cámara Web", + "other": "Otro" + }, + "format": { + "rtsp": "RTSP", + "hls": "HLS", + "mjpeg": "MJPEG", + "youtubeLive": "YouTube Live", + "webrtc": "WebRTC", + "dash": "DASH", + "embed": "Embed", + "other": "Otro" + }, + "status": { + "active": "Activo", + "inactive": "Inactivo", + "error": "Error" + } }, "welcome": "Bienvenido al sitio de la aplicación obytes" } diff --git a/src/translations/fr.json b/src/translations/fr.json new file mode 100644 index 0000000..bac655f --- /dev/null +++ b/src/translations/fr.json @@ -0,0 +1,1152 @@ +{ + "app": { + "title": "Resgrid Dispatch" + }, + "audio_streams": { + "buffering": "Mise en mémoire tampon", + "buffering_stream": "Mise en mémoire tampon du flux audio...", + "close": "Fermer", + "currently_playing": "En cours de lecture : {{streamName}}", + "loading": "Chargement", + "loading_stream": "Chargement du flux audio...", + "loading_streams": "Chargement des flux audio...", + "name": "Nom", + "no_stream_playing": "Aucun flux audio en cours de lecture", + "none": "Aucun", + "playing": "En lecture", + "refresh_streams": "Actualiser les flux", + "select_placeholder": "Sélectionner un flux audio", + "select_stream": "Sélectionner un flux audio", + "status": "Statut", + "stopped": "Arrêté", + "stream_info": "Informations sur le flux", + "stream_selected": "Sélectionné : {{streamName}}", + "title": "Flux audio", + "type": "Type" + }, + "bluetooth": { + "applied": "Appliqué", + "audio": "Audio", + "audioActive": "Audio actif", + "audio_device": "Appareil BT", + "availableDevices": "Appareils disponibles", + "available_devices": "Appareils disponibles", + "bluetooth_not_ready": "Le Bluetooth est {{state}}. Veuillez activer le Bluetooth.", + "buttonControlAvailable": "Contrôle par bouton disponible", + "checking": "Vérification du statut Bluetooth...", + "clear": "Effacer", + "connect": "Connecter", + "connected": "Connecté", + "connectionError": "Erreur de connexion", + "current_selection": "Sélection actuelle", + "disconnect": "Déconnecter", + "doublePress": "Double ", + "liveKitActive": "LiveKit actif", + "longPress": "Long ", + "micControl": "Contrôle micro", + "mute": "Couper le son", + "noDevicesFound": "Aucun appareil audio trouvé", + "noDevicesFoundRetry": "Aucun appareil audio trouvé. Réessayez la recherche.", + "no_device_selected": "Aucun appareil sélectionné", + "no_devices_found": "Aucun appareil Bluetooth audio trouvé", + "not_connected": "Non connecté", + "poweredOff": "Le Bluetooth est désactivé. Veuillez activer le Bluetooth pour connecter des appareils audio.", + "pttStart": "Début PTT", + "pttStop": "Fin PTT", + "recentButtonEvents": "Événements de bouton récents", + "scan": "Rechercher", + "scanAgain": "Rechercher à nouveau", + "scan_again": "Rechercher à nouveau", + "scan_error_message": "Impossible de rechercher les appareils Bluetooth", + "scan_error_title": "Erreur de recherche", + "scanning": "Recherche en cours...", + "select_device": "Sélectionner un appareil Bluetooth", + "selected": "Sélectionné", + "selection_error_message": "Impossible de sauvegarder l'appareil préféré", + "selection_error_title": "Erreur de sélection", + "startScanning": "Lancer la recherche", + "stopScan": "Arrêter la recherche", + "supports_mic_control": "Contrôle micro", + "tap_scan_to_find_devices": "Appuyez sur « Rechercher » pour trouver les appareils Bluetooth audio", + "title": "Audio Bluetooth", + "unauthorized": "Permission Bluetooth refusée. Veuillez accorder les permissions Bluetooth dans les Réglages.", + "unknown": "Inconnu", + "unknownDevice": "Appareil inconnu", + "unknown_device": "Appareil inconnu", + "unmute": "Réactiver le son", + "volumeDown": "Volume -", + "volumeUp": "Volume +" + }, + "callImages": { + "add": "Ajouter une image", + "add_new": "Ajouter une nouvelle image", + "default_name": "Image sans titre", + "error": "Erreur lors du chargement des images", + "failed_to_load": "Échec du chargement de l'image", + "image_alt": "Image de l'appel", + "image_name": "Nom de l'image", + "image_note": "Note de l'image", + "loading": "Chargement...", + "no_images": "Aucune image disponible", + "no_images_description": "Ajoutez des images à votre appel pour faciliter la documentation et la communication", + "select_from_gallery": "Sélectionner depuis la galerie", + "take_photo": "Prendre une photo", + "title": "Images de l'appel", + "upload": "Téléverser" + }, + "callNotes": { + "addNote": "Ajouter une note", + "addNotePlaceholder": "Ajouter une nouvelle note...", + "noNotes": "Aucune note disponible pour cet appel", + "noSearchResults": "Aucune note ne correspond à votre recherche", + "searchPlaceholder": "Rechercher dans les notes...", + "title": "Notes de l'appel" + }, + "call_detail": { + "address": "Adresse", + "call_location": "Lieu de l'appel", + "close_call": "Clôturer l'appel", + "close_call_confirmation": "Êtes-vous sûr de vouloir clôturer cet appel ?", + "close_call_error": "Échec de la clôture de l'appel", + "close_call_note": "Note de clôture", + "close_call_note_placeholder": "Saisissez une note concernant la clôture de l'appel", + "close_call_success": "Appel clôturé avec succès", + "close_call_type": "Type de clôture", + "close_call_type_placeholder": "Sélectionner le type de clôture", + "close_call_type_required": "Veuillez sélectionner un type de clôture", + "close_call_types": { + "cancelled": "Annulé", + "closed": "Clôturé", + "false_alarm": "Fausse alerte", + "founded": "Fondé", + "minor": "Mineur", + "transferred": "Transféré", + "unfounded": "Non fondé" + }, + "contact_email": "Courriel", + "contact_info": "Coordonnées", + "contact_name": "Nom du contact", + "contact_phone": "Téléphone", + "edit_call": "Modifier l'appel", + "external_id": "ID externe", + "failed_to_open_maps": "Échec de l'ouverture de l'application de cartographie", + "files": { + "add_file": "Ajouter un fichier", + "button": "Fichiers", + "empty": "Aucun fichier disponible", + "empty_description": "Ajoutez des fichiers à votre appel pour faciliter la documentation et la communication", + "error": "Erreur lors du chargement des fichiers", + "file_name": "Nom du fichier", + "name_required": "Veuillez saisir un nom pour le fichier", + "no_files": "Aucun fichier disponible", + "no_files_description": "Ajoutez des fichiers à votre appel pour faciliter la documentation et la communication", + "open_error": "Erreur lors de l'ouverture du fichier", + "select_error": "Erreur lors de la sélection du fichier", + "select_file": "Sélectionner un fichier", + "share_error": "Erreur lors du partage du fichier", + "title": "Fichiers de l'appel", + "upload": "Téléverser", + "upload_error": "Erreur lors du téléversement du fichier", + "uploading": "Téléversement en cours..." + }, + "group": "Groupe", + "images": "Images", + "loading": "Chargement des détails de l'appel...", + "nature": "Nature", + "no_additional_info": "Aucune information supplémentaire disponible", + "no_contact_info": "Aucune coordonnée disponible", + "no_dispatched": "Aucune unité dépêchée sur cet appel", + "no_location": "Aucune donnée de localisation disponible", + "no_location_for_routing": "Aucune donnée de localisation disponible pour le routage", + "no_protocols": "Aucun protocole associé à cet appel", + "no_timeline": "Aucun événement chronologique disponible", + "not_available": "N/D", + "not_found": "Appel introuvable", + "missing_call_id": "L'identifiant de l'appel est manquant", + "note": "Note", + "notes": "Notes", + "priority": "Priorité", + "reference_id": "ID de référence", + "set_active": "Définir comme actif", + "set_active_error": "Échec de la définition de l'appel comme actif", + "set_active_success": "Appel défini comme actif", + "setting_active": "Activation en cours...", + "status": "Statut", + "tabs": { + "contact": "Contact", + "dispatched": "Dépêché", + "info": "Infos", + "protocols": "Protocoles", + "timeline": "Activité", + "video": "Vidéo" + }, + "timestamp": "Horodatage", + "title": "Détails de l'appel", + "type": "Type", + "unit": "Unité", + "update_call_error": "Échec de la mise à jour de l'appel", + "update_call_success": "Appel mis à jour avec succès" + }, + "calls": { + "address": "Adresse", + "address_found": "Adresse trouvée et localisation mise à jour", + "address_not_found": "Adresse introuvable, veuillez essayer une autre adresse", + "address_placeholder": "Saisissez l'adresse de l'appel", + "address_required": "Veuillez saisir une adresse pour la recherche", + "call_details": "Détails de l'appel", + "call_location": "Lieu de l'appel", + "call_number": "Numéro d'appel", + "call_priority": "Priorité de l'appel", + "confirm_deselect_message": "Êtes-vous sûr de vouloir désélectionner l'appel actif actuel ?", + "confirm_deselect_title": "Désélectionner l'appel actif", + "contact_info": "Coordonnées", + "contact_info_placeholder": "Saisissez les informations du contact", + "contact_name": "Nom du contact", + "contact_name_placeholder": "Saisissez le nom du contact", + "contact_phone": "Téléphone du contact", + "contact_phone_placeholder": "Saisissez le téléphone du contact", + "coordinates": "Coordonnées GPS", + "coordinates_found": "Coordonnées trouvées et adresse mise à jour", + "coordinates_geocoding_error": "Échec de l'obtention de l'adresse pour les coordonnées, mais localisation définie sur la carte", + "coordinates_invalid_format": "Format de coordonnées invalide. Veuillez utiliser le format : latitude, longitude", + "coordinates_no_address": "Coordonnées définies sur la carte, mais aucune adresse trouvée", + "coordinates_out_of_range": "Coordonnées hors limites. La latitude doit être entre -90 et 90, la longitude entre -180 et 180", + "coordinates_placeholder": "Saisissez les coordonnées GPS (ex. : 37.7749, -122.4194)", + "coordinates_required": "Veuillez saisir des coordonnées pour la recherche", + "create": "Créer", + "create_error": "Erreur lors de la création de l'appel", + "create_new_call": "Créer un nouvel appel", + "create_success": "Appel créé avec succès", + "description": "Description", + "description_placeholder": "Saisissez la description de l'appel", + "deselect": "Désélectionner", + "directions": "Itinéraire", + "dispatch_to": "Dépêcher vers", + "dispatch_to_everyone": "Dépêcher tout le personnel disponible", + "edit_call": "Modifier l'appel", + "edit_call_description": "Mettre à jour les informations de l'appel", + "everyone": "Tout le monde", + "files": { + "no_files": "Aucun fichier disponible", + "no_files_description": "Aucun fichier n'a encore été ajouté à cet appel", + "title": "Fichiers de l'appel" + }, + "geocoding_error": "Échec de la recherche d'adresse, veuillez réessayer", + "groups": "Groupes", + "invalid_priority": "Priorité invalide sélectionnée. Veuillez sélectionner une priorité valide.", + "invalid_type": "Type invalide sélectionné. Veuillez sélectionner un type d'appel valide.", + "loading": "Chargement des appels...", + "loading_calls": "Chargement des appels...", + "name": "Nom", + "name_placeholder": "Saisissez le nom de l'appel", + "nature": "Nature", + "nature_placeholder": "Saisissez la nature de l'appel", + "new_call": "Nouvel appel", + "new_call_description": "Créer un nouvel appel pour démarrer un nouvel incident", + "no_call_selected": "Aucun appel actif", + "no_call_selected_info": "Cette unité ne répond actuellement à aucun appel", + "no_calls": "Aucun appel actif", + "no_calls_available": "Aucun appel disponible", + "no_calls_description": "Aucun appel actif trouvé. Sélectionnez un appel actif pour voir les détails.", + "no_location_message": "Cet appel ne dispose pas de données de localisation pour la navigation.", + "no_location_title": "Aucune localisation disponible", + "no_open_calls": "Aucun appel ouvert disponible", + "note": "Note", + "note_placeholder": "Saisissez la note de l'appel", + "plus_code": "Plus Code", + "plus_code_found": "Plus code trouvé et localisation mise à jour", + "plus_code_geocoding_error": "Échec de la recherche du plus code, veuillez réessayer", + "plus_code_not_found": "Plus code introuvable, veuillez essayer un autre plus code", + "plus_code_placeholder": "Saisissez le plus code (ex. : 849VCWC8+R9)", + "plus_code_required": "Veuillez saisir un plus code pour la recherche", + "priority": "Priorité", + "priority_placeholder": "Sélectionner la priorité de l'appel", + "roles": "Rôles", + "search": "Rechercher des appels...", + "select_active_call": "Sélectionner l'appel actif", + "select_address": "Sélectionner l'adresse", + "select_address_placeholder": "Sélectionner l'adresse de l'appel", + "select_description": "Sélectionner la description", + "select_dispatch_recipients": "Sélectionner les destinataires du dispatch", + "select_location": "Sélectionner l'emplacement sur la carte", + "select_name": "Sélectionner le nom", + "select_nature": "Sélectionner la nature", + "select_nature_placeholder": "Sélectionner la nature de l'appel", + "select_priority": "Sélectionner la priorité", + "select_priority_placeholder": "Sélectionner la priorité de l'appel", + "select_recipients": "Sélectionner les destinataires", + "select_type": "Sélectionner le type", + "selected": "sélectionné", + "title": "Appels", + "type": "Type", + "units": "Unités", + "users": "Utilisateurs", + "viewNotes": "Notes", + "view_details": "Voir les détails", + "what3words": "what3words", + "what3words_found": "Adresse what3words trouvée et localisation mise à jour", + "what3words_geocoding_error": "Échec de la recherche de l'adresse what3words, veuillez réessayer", + "what3words_invalid_format": "Format what3words invalide. Veuillez utiliser le format : mot.mot.mot", + "what3words_not_found": "Adresse what3words introuvable, veuillez essayer une autre adresse", + "what3words_placeholder": "Saisissez l'adresse what3words (ex. : filled.count.soap)", + "what3words_required": "Veuillez saisir une adresse what3words pour la recherche", + "expand_map": "Agrandir la carte", + "schedule_dispatch": "Planifier le dispatch", + "schedule_dispatch_description": "Définir une date et une heure futures pour planifier cet appel pour un dispatch ultérieur", + "scheduled_on": "Date et heure prévues", + "scheduled_on_helper": "Laisser vide pour dispatcher immédiatement", + "scheduled_on_past_error": "L'heure planifiée doit être dans le futur", + "contact_information": "Coordonnées du contact", + "new_call_web_hint": "Remplissez les détails de l'appel ci-dessous. Appuyez sur Ctrl+Entrée pour créer.", + "edit_call_web_hint": "Mettez à jour les détails de l'appel ci-dessous. Appuyez sur Ctrl+S pour sauvegarder.", + "keyboard_shortcuts": "Astuce : Appuyez sur Ctrl+Entrée pour créer, Échap pour annuler", + "edit_keyboard_shortcuts": "Astuce : Appuyez sur Ctrl+S pour sauvegarder, Échap pour annuler", + "templates": { + "title": "Modèles d'appel", + "select_template": "Sélectionner un modèle", + "search_placeholder": "Rechercher des modèles...", + "none": "Aucun modèle disponible", + "template_applied": "Modèle appliqué" + }, + "form": { + "title": "Formulaire d'appel", + "no_form": "Aucun formulaire d'appel configuré" + }, + "linked_calls": { + "title": "Appel lié", + "select": "Lier à un appel existant", + "change": "Changer l'appel lié", + "none": "Aucun appel actif disponible", + "search_placeholder": "Rechercher des appels...", + "linked": "Lié" + }, + "contact_picker": { + "title": "Sélectionner un contact", + "search_placeholder": "Rechercher des contacts...", + "none": "Aucun contact disponible" + }, + "protocols": { + "title": "Protocoles", + "select": "Sélectionner les protocoles", + "none": "Aucun protocole disponible", + "selected_count": "sélectionné", + "expand_questions": "Voir les questions", + "collapse": "Réduire" + } + }, + "common": { + "add": "Ajouter", + "back": "Retour", + "cancel": "Annuler", + "creating": "Création en cours...", + "saving": "Sauvegarde en cours...", + "unsaved_changes": "Modifications non sauvegardées", + "close": "Fermer", + "confirm": "Confirmer", + "confirm_location": "Confirmer l'emplacement", + "delete": "Supprimer", + "dismiss": "Fermer", + "done": "Terminé", + "edit": "Modifier", + "error": "Erreur", + "errorOccurred": "Une erreur est survenue", + "get_my_location": "Obtenir ma position", + "go_back": "Retour", + "loading": "Chargement...", + "loading_address": "Chargement de l'adresse...", + "next": "Suivant", + "noActiveUnit": "Aucune unité active définie", + "noActiveUnitDescription": "Veuillez définir une unité active depuis la page des paramètres pour accéder aux contrôles de statut", + "noDataAvailable": "Aucune donnée disponible", + "no_address_found": "Aucune adresse trouvée", + "no_location": "Aucune donnée de localisation disponible", + "no_results_found": "Aucun résultat trouvé", + "no_unit_selected": "Aucune unité sélectionnée", + "nothingToDisplay": "Il n'y a rien à afficher pour le moment", + "of": "de", + "ok": "Ok", + "optional": "optionnel", + "permission_denied": "Permission refusée", + "previous": "Précédent", + "remove": "Supprimer", + "retry": "Réessayer", + "route": "Itinéraire", + "save": "Sauvegarder", + "search": "Rechercher...", + "set_location": "Définir l'emplacement", + "share": "Partager", + "step": "Étape", + "submit": "Soumettre", + "submitting": "Envoi en cours...", + "tryAgainLater": "Veuillez réessayer plus tard", + "unknown": "Inconnu", + "unknown_department": "Service inconnu", + "unknown_user": "Utilisateur inconnu", + "upload": "Téléverser", + "uploading": "Téléversement en cours..." + }, + "contacts": { + "add": "Ajouter un contact", + "addedBy": "Ajouté par", + "addedOn": "Ajouté le", + "additionalInformation": "Informations supplémentaires", + "address": "Adresse", + "bluesky": "Bluesky", + "cancel": "Annuler", + "cellPhone": "Téléphone portable", + "city": "Ville", + "cityState": "Ville et État", + "cityStateZip": "Ville, État, Code postal", + "company": "Entreprise", + "contactInformation": "Coordonnées", + "contactNotes": "Notes du contact", + "contactNotesEmpty": "Aucune note trouvée pour ce contact", + "contactNotesEmptyDescription": "Les notes ajoutées à ce contact apparaîtront ici", + "contactNotesExpired": "Cette note a expiré", + "contactNotesLoading": "Chargement des notes du contact...", + "contactType": "Type de contact", + "countryId": "ID du pays", + "delete": "Supprimer", + "deleteConfirm": "Êtes-vous sûr de vouloir supprimer ce contact ?", + "deleteSuccess": "Contact supprimé avec succès", + "description": "Ajoutez et gérez vos contacts", + "details": "Détails du contact", + "detailsTab": "Détails", + "edit": "Modifier le contact", + "editedBy": "Modifié par", + "editedOn": "Modifié le", + "email": "Courriel", + "empty": "Aucun contact trouvé", + "emptyDescription": "Ajoutez des contacts pour gérer vos relations personnelles et professionnelles", + "entranceCoordinates": "Coordonnées d'entrée", + "exitCoordinates": "Coordonnées de sortie", + "expires": "Expire", + "facebook": "Facebook", + "faxPhone": "Fax", + "formError": "Veuillez corriger les erreurs dans le formulaire", + "homePhone": "Téléphone fixe", + "identification": "Identification", + "important": "Marquer comme important", + "instagram": "Instagram", + "internal": "Interne", + "invalidEmail": "Adresse courriel invalide", + "linkedin": "LinkedIn", + "locationCoordinates": "Coordonnées de localisation", + "locationInformation": "Informations de localisation", + "mastodon": "Mastodon", + "mobile": "Mobile", + "name": "Nom", + "noteAlert": "Alerte", + "noteType": "Type", + "notes": "Notes", + "notesTab": "Notes", + "officePhone": "Téléphone de bureau", + "otherInfo": "Autres informations", + "person": "Personne", + "phone": "Téléphone", + "public": "Public", + "required": "Obligatoire", + "save": "Sauvegarder le contact", + "saveSuccess": "Contact sauvegardé avec succès", + "search": "Rechercher des contacts...", + "shouldAlert": "Doit alerter", + "socialMediaWeb": "Réseaux sociaux et Web", + "state": "État", + "stateId": "ID de l'État", + "systemInformation": "Informations système", + "tabs": { + "details": "Détails", + "notes": "Notes" + }, + "threads": "Threads", + "title": "Contacts", + "twitter": "Twitter", + "visibility": "Visibilité", + "website": "Site web", + "zip": "Code postal" + }, + "form": { + "invalid_url": "Veuillez saisir une URL valide commençant par http:// ou https://", + "required": "Ce champ est obligatoire" + }, + "livekit": { + "audio_devices": "Appareils audio", + "audio_settings": "Paramètres audio", + "connected_to_room": "Connecté au canal", + "connecting": "Connexion en cours...", + "disconnect": "Déconnecter", + "join": "Rejoindre", + "microphone": "Microphone", + "mute": "Couper le son", + "no_rooms_available": "Aucun canal vocal disponible", + "speaker": "Haut-parleur", + "speaking": "En train de parler", + "title": "Canaux vocaux", + "unmute": "Réactiver le son" + }, + "loading": { + "loading": "Chargement...", + "loadingData": "Chargement des données...", + "pleaseWait": "Veuillez patienter", + "processingRequest": "Traitement de votre demande..." + }, + "sso": { + "authenticating": "Authentification en cours...", + "back_to_login": "Retour à la connexion", + "back_to_lookup": "Changer d'utilisateur", + "continue_button": "Continuer", + "department_id_label": "ID du service", + "department_id_placeholder": "Saisissez l'ID du service", + "error_generic": "La connexion a échoué. Veuillez réessayer.", + "error_oidc_cancelled": "La connexion a été annulée.", + "error_oidc_not_ready": "Le fournisseur SSO est en cours de chargement, veuillez patienter.", + "error_sso_not_enabled": "L'authentification unique n'est pas activée pour cet utilisateur.", + "error_token_exchange": "Échec de la connexion. Veuillez réessayer.", + "error_user_not_found": "Utilisateur introuvable. Veuillez vérifier et réessayer.", + "looking_up": "Recherche en cours...", + "optional": "optionnel", + "page_subtitle": "Saisissez votre nom d'utilisateur pour rechercher les options de connexion de votre organisation.", + "page_title": "Authentification unique", + "provider_oidc": "OpenID Connect (OIDC)", + "provider_saml": "SAML 2.0", + "sign_in_button": "Se connecter avec SSO", + "sign_in_title": "Connexion", + "sso_button": "Connexion SSO" + }, + "login": { + "branding_subtitle": "Logiciel de dispatch puissant pour les premiers intervenants, les équipes de recherche et de sauvetage et les organisations de sécurité publique.", + "branding_title": "Gestion des interventions d'urgence", + "errorModal": { + "confirmButton": "OK", + "message": "Veuillez vérifier votre nom d'utilisateur et votre mot de passe, puis réessayer.", + "title": "Échec de la connexion" + }, + "feature_dispatch_desc": "Dépêchez instantanément des unités et gérez les appels avec des mises à jour en temps réel sur tous les appareils.", + "feature_dispatch_title": "Dispatch en temps réel", + "feature_mapping_desc": "Suivez les unités en temps réel avec des cartes détaillées, le routage et la gestion de la localisation.", + "feature_mapping_title": "Cartographie avancée", + "feature_personnel_desc": "Gérez votre équipe avec un accès basé sur les rôles, le suivi des statuts et des outils de communication.", + "feature_personnel_title": "Gestion du personnel", + "footer_text": "Créé avec ❤️ à Lake Tahoe", + "login": "Connexion", + "login_button": "Se connecter", + "login_button_description": "Connectez-vous à votre compte pour continuer", + "login_button_error": "Erreur lors de la connexion", + "login_button_loading": "Connexion en cours...", + "login_button_success": "Connecté avec succès", + "no_account": "Vous n'avez pas de compte ?", + "page_subtitle": "Saisissez vos identifiants pour vous connecter.", + "page_title": "Resgrid Dispatch", + "password": "Mot de passe", + "password_incorrect": "Le mot de passe est incorrect", + "password_placeholder": "Saisissez votre mot de passe", + "register": "S'inscrire", + "title": "Connexion", + "username": "Nom d'utilisateur", + "username_placeholder": "Saisissez votre nom d'utilisateur", + "welcome_title": "Bon retour" + }, + "lockscreen": { + "message": "Saisissez votre mot de passe pour déverrouiller l'écran", + "not_you": "Ce n'est pas vous ? Retour à la connexion", + "password": "Mot de passe", + "password_placeholder": "Saisissez votre mot de passe", + "title": "Écran de verrouillage", + "unlock_button": "Déverrouiller", + "unlock_failed": "Échec du déverrouillage. Veuillez réessayer.", + "unlocking": "Déverrouillage en cours...", + "welcome_back": "Bon retour" + }, + "maintenance": { + "downtime_message": "Nous travaillons activement pour terminer la maintenance le plus rapidement possible. Veuillez revenir bientôt.", + "downtime_title": "Quelle est la durée de l'interruption ?", + "message": "Veuillez revenir dans quelques instants.", + "support_message": "Si vous avez besoin d'aide, veuillez nous contacter à", + "support_title": "Avez-vous besoin d'aide ?", + "title": "Le site est en maintenance", + "why_down_message": "Nous effectuons une maintenance planifiée pour améliorer votre expérience. Nous nous excusons pour tout inconvénient.", + "why_down_title": "Pourquoi le site est-il indisponible ?" + }, + "menu": { + "calls": "Appels", + "calls_list": "Liste des appels", + "contacts": "Contacts", + "home": "Accueil", + "map": "Carte", + "menu": "Menu", + "messages": "Messages", + "new_call": "Nouvel appel", + "personnel": "Personnel", + "protocols": "Protocoles", + "settings": "Paramètres", + "units": "Unités", + "weatherAlerts": "Alertes météo" + }, + "map": { + "call_set_as_current": "Appel défini comme appel en cours", + "failed_to_open_maps": "Échec de l'ouverture de l'application de cartographie", + "failed_to_set_current_call": "Échec de la définition de l'appel en cours", + "layers": "Couches de carte", + "no_layers": "Aucune couche disponible", + "no_location_for_routing": "Aucune donnée de localisation disponible pour le routage", + "pin_color": "Couleur de l'épingle", + "recenter_map": "Recentrer la carte", + "set_as_current_call": "Définir comme appel en cours", + "show_all": "Tout afficher", + "hide_all": "Tout masquer", + "view_call_details": "Voir les détails de l'appel" + }, + "notes": { + "actions": { + "add": "Ajouter une note", + "delete_confirm": "Êtes-vous sûr de vouloir supprimer cette note ?" + }, + "details": { + "close": "Fermer", + "created": "Créé", + "delete": "Supprimer", + "edit": "Modifier", + "tags": "Étiquettes", + "title": "Détails de la note", + "updated": "Mis à jour" + }, + "empty": "Aucune note trouvée", + "emptyDescription": "Aucune note n'a encore été créée pour votre service.", + "search": "Rechercher dans les notes...", + "title": "Notes" + }, + "personnel": { + "title": "Personnel", + "search": "Rechercher du personnel...", + "loading": "Chargement du personnel...", + "empty": "Aucun personnel trouvé", + "empty_description": "Aucun personnel n'est actuellement disponible dans votre département.", + "no_results": "Aucun personnel ne correspond à votre recherche", + "no_results_description": "Essayez d'ajuster vos termes de recherche.", + "status": "Statut", + "staffing": "Effectifs", + "group": "Groupe", + "roles": "Rôles", + "email": "E-mail", + "phone": "Téléphone", + "id_number": "Numéro d'identification", + "responding_to": "Répond à", + "status_updated": "Statut mis à jour", + "staffing_updated": "Effectifs mis à jour", + "details": "Détails du personnel", + "contact_info": "Coordonnées", + "status_info": "Statut et effectifs", + "no_email": "Aucun e-mail disponible", + "no_phone": "Aucun téléphone disponible", + "no_group": "Non assigné", + "no_roles": "Aucun rôle assigné", + "unknown_status": "Inconnu", + "on_duty": "En service", + "off_duty": "Hors service", + "call_phone": "Appeler", + "send_email": "E-mail", + "custom_fields": "Informations supplémentaires" + }, + "onboarding": { + "screen1": { + "title": "Resgrid Dispatch", + "description": "Créez, dépêchez et gérez les appels d'urgence avec un puissant centre de commandement mobile à portée de main" + }, + "screen2": { + "title": "Connaissance situationnelle en temps réel", + "description": "Suivez toutes les unités, le personnel et les ressources sur une carte interactive avec des mises à jour de statut en direct et l'AVL" + }, + "screen3": { + "title": "Coordination fluide", + "description": "Communiquez instantanément avec les unités sur le terrain, mettez à jour les statuts des appels et coordonnez les efforts d'intervention depuis n'importe où" + }, + "skip": "Passer", + "next": "Suivant", + "getStarted": "C'est parti" + }, + "protocols": { + "details": { + "close": "Fermer", + "code": "Code", + "created": "Créé", + "title": "Détails du protocole", + "updated": "Mis à jour" + }, + "empty": "Aucun protocole trouvé", + "emptyDescription": "Aucun protocole n'a encore été créé pour votre service.", + "search": "Rechercher des protocoles...", + "title": "Protocoles" + }, + "push_notifications": { + "close": "Fermer", + "message": "Message", + "new_notification": "Nouvelle notification", + "title": "Titre", + "types": { + "call": "Appel", + "chat": "Discussion", + "group_chat": "Discussion de groupe", + "message": "Message", + "notification": "Notification" + }, + "unknown_type_warning": "Type de notification inconnu reçu", + "view_call": "Voir l'appel", + "view_message": "Voir le message" + }, + "roles": { + "modal": { + "title": "Affectations de rôles d'unité" + }, + "selectUser": "Sélectionner un utilisateur", + "status": "{{active}} sur {{total}} rôles", + "tap_to_manage": "Appuyez pour gérer les rôles", + "unassigned": "Non affecté" + }, + "settings": { + "about": "À propos", + "account": "Compte", + "active_unit": "Unité active", + "app_info": "Infos de l'application", + "app_name": "Nom de l'application", + "arabic": "Arabe", + "audio_device_selection": { + "bluetooth_device": "Appareil Bluetooth", + "current_selection": "Sélection actuelle", + "microphone": "Microphone", + "no_microphones_available": "Aucun microphone disponible", + "no_speakers_available": "Aucun haut-parleur disponible", + "none_selected": "Aucun sélectionné", + "speaker": "Haut-parleur", + "speaker_device": "Appareil haut-parleur", + "title": "Sélection de l'appareil audio", + "unavailable": "Indisponible", + "wired_device": "Appareil filaire" + }, + "background_geolocation": "Géolocalisation en arrière-plan", + "background_geolocation_warning": "Cette fonctionnalité permet à l'application de suivre votre position en arrière-plan. Elle facilite la coordination des interventions d'urgence mais peut affecter l'autonomie de la batterie.", + "background_location": "Localisation en arrière-plan", + "contact_us": "Nous contacter", + "current_unit": "Unité actuelle", + "arabic": "Arabe", + "english": "Anglais", + "french": "Français", + "german": "Allemand", + "italian": "Italien", + "enter_password": "Saisissez votre mot de passe", + "enter_server_url": "Saisissez l'URL de l'API Resgrid (ex. : https://api.resgrid.com)", + "enter_username": "Saisissez votre nom d'utilisateur", + "environment": "Environnement", + "general": "Général", + "generale": "Général", + "github": "Github", + "help_center": "Centre d'aide", + "keep_alive": "Maintenir actif", + "keep_alive_warning": "Avertissement : L'activation du maintien actif empêchera votre appareil de se mettre en veille et peut augmenter considérablement la consommation de batterie.", + "keep_screen_on": "Garder l'écran allumé", + "language": "Langue", + "links": "Liens", + "login_info": "Informations de connexion", + "logout": "Déconnexion", + "more": "Plus", + "no_units_available": "Aucune unité disponible", + "none_selected": "Aucun sélectionné", + "notifications": "Notifications push", + "notifications_description": "Activez les notifications pour recevoir des alertes et des mises à jour", + "notifications_enable": "Activer les notifications", + "password": "Mot de passe", + "preferences": "Préférences", + "privacy": "Politique de confidentialité", + "privacy_policy": "Politique de confidentialité", + "rate": "Évaluer", + "select_unit": "Sélectionner une unité", + "server": "Serveur", + "server_url": "URL du serveur", + "server_url_note": "Remarque : Il s'agit de l'URL de l'API Resgrid. Elle est utilisée pour se connecter au serveur Resgrid. N'incluez pas /api/v4 dans l'URL ni de barre oblique finale.", + "set_active_unit": "Définir l'unité active", + "share": "Partager", + "polish": "Polonais", + "spanish": "Espagnol", + "swedish": "Suédois", + "ukrainian": "Ukrainien", + "status_page": "État du système", + "support": "Support", + "support_us": "Nous soutenir", + "terms": "Conditions d'utilisation", + "theme": { + "dark": "Sombre", + "light": "Clair", + "system": "Système", + "title": "Thème" + }, + "title": "Paramètres", + "unit_selected_successfully": "{{unitName}} sélectionné avec succès", + "unit_selection": "Sélection de l'unité", + "unit_selection_failed": "Échec de la sélection de l'unité. Veuillez réessayer.", + "username": "Nom d'utilisateur", + "version": "Version", + "website": "Site web" + }, + "status": { + "add_note": "Ajouter une note", + "both_destinations_enabled": "Peut répondre aux appels ou aux casernes", + "call_destination_enabled": "Peut répondre aux appels", + "calls_tab": "Appels", + "failed_to_save_status": "Échec de la sauvegarde du statut. Veuillez réessayer.", + "general_status": "Statut général sans destination spécifique", + "loading_stations": "Chargement des casernes...", + "no_destination": "Aucune destination", + "no_stations_available": "Aucune caserne disponible", + "no_statuses_available": "Aucun statut disponible", + "note": "Note", + "note_optional": "Ajoutez une note optionnelle pour cette mise à jour de statut", + "note_required": "Veuillez saisir une note pour cette mise à jour de statut", + "select_destination": "Sélectionner la destination pour {{status}}", + "select_destination_type": "Où souhaitez-vous intervenir ?", + "select_status": "Sélectionner le statut", + "select_status_type": "Quel statut souhaitez-vous définir ?", + "selected_destination": "Destination sélectionnée", + "selected_status": "Statut sélectionné", + "set_status": "Définir le statut", + "station_destination_enabled": "Peut répondre aux casernes", + "stations_tab": "Casernes", + "status_saved_successfully": "Statut sauvegardé avec succès !" + }, + "dispatch": { + "active_calls": "Appels actifs", + "pending_calls": "En attente", + "scheduled_calls": "Planifiés", + "units_available": "Disponibles", + "personnel_available": "Disponibles", + "personnel_on_duty": "En service", + "units": "Unités", + "personnel": "Personnel", + "map": "Carte", + "notes": "Notes", + "activity_log": "Journal d'activité", + "communications": "Communications", + "no_active_calls": "Aucun appel actif", + "no_units": "Aucune unité disponible", + "no_personnel": "Aucun personnel disponible", + "no_notes": "Aucune note disponible", + "no_activity": "Aucune activité récente", + "current_channel": "Canal actuel", + "audio_stream": "Flux audio", + "no_stream": "Aucun flux actif", + "ptt": "PTT", + "ptt_start": "Début PTT", + "ptt_end": "Fin PTT", + "transmitting_on": "Transmission sur {{channel}}", + "transmission_ended": "Transmission terminée", + "voice_disabled": "Voix désactivée", + "disconnected": "Déconnecté", + "select_channel": "Sélectionner un canal", + "select_channel_description": "Choisissez un canal vocal auquel vous connecter", + "change_channel_warning": "Sélectionner un nouveau canal déconnectera le canal actuel", + "default_channel": "Par défaut", + "no_channels_available": "Aucun canal vocal disponible", + "system_update": "Mise à jour système", + "data_refreshed": "Données actualisées depuis le serveur", + "call_selected": "Appel sélectionné", + "unit_selected": "Unité sélectionnée", + "unit_deselected": "Unité désélectionnée", + "personnel_selected": "Personnel sélectionné", + "personnel_deselected": "Personnel désélectionné", + "loading_map": "Chargement de la carte...", + "map_not_available_web": "Carte non disponible sur la plateforme web", + "filtering_by_call": "Filtrage par appel", + "clear_filter": "Effacer le filtre", + "call_filter_active": "Filtre d'appel actif", + "call_filter_cleared": "Filtre d'appel effacé", + "showing_all_data": "Affichage de toutes les données", + "call_notes": "Notes de l'appel", + "no_call_notes": "Aucune note d'appel", + "add_call_note_placeholder": "Ajouter une note...", + "note_added": "Note ajoutée", + "note_added_to_console": "Une nouvelle note a été ajoutée à la console", + "add_note_title": "Ajouter une nouvelle note", + "note_title_label": "Titre", + "note_title_placeholder": "Saisissez le titre de la note...", + "note_category_label": "Catégorie", + "note_category_placeholder": "Sélectionner une catégorie", + "note_no_category": "Sans catégorie", + "note_body_label": "Contenu de la note", + "note_body_placeholder": "Saisissez le contenu de la note...", + "note_save_error": "Échec de la sauvegarde de la note : {{error}}", + "note_created": "Note créée", + "units_on_call": "Unités en intervention", + "no_units_on_call": "Aucune unité en intervention", + "personnel_on_call": "Personnel en intervention", + "no_personnel_on_call": "Aucun personnel en intervention", + "call_activity": "Activité de l'appel", + "no_call_activity": "Aucune activité d'appel", + "on_call": "En intervention", + "filtered": "Filtré", + "active_filter": "Filtre actif", + "unit_status_change": "Changement de statut d'unité", + "personnel_status_change": "Changement de statut du personnel", + "view_call_details": "Voir les détails de l'appel", + "dispatched_resources": "Dépêchés", + "unassigned": "Non affecté", + "available": "Disponible", + "unknown": "Inconnu", + "search_personnel_placeholder": "Rechercher du personnel...", + "search_calls_placeholder": "Rechercher des appels...", + "search_units_placeholder": "Rechercher des unités...", + "search_notes_placeholder": "Rechercher des notes...", + "signalr_update": "Mise à jour en temps réel", + "signalr_connected": "Connecté", + "realtime_updates_active": "Les mises à jour en temps réel sont maintenant actives", + "personnel_status_updated": "Statut du personnel mis à jour", + "personnel_staffing_updated": "Effectifs du personnel mis à jour", + "unit_status_updated": "Statut de l'unité mis à jour", + "calls_updated": "Appels mis à jour", + "call_added": "Nouvel appel ajouté", + "call_closed": "Appel clôturé", + "check_ins": "Pointages", + "no_check_ins": "Aucun appel avec minuterie de pointage", + "radio_log": "Journal radio", + "radio": "Radio", + "activity": "Activité", + "actions": "Actions", + "no_radio_activity": "Aucune transmission radio", + "live": "EN DIRECT", + "currently_transmitting": "Transmission en cours...", + "duration": "Durée", + "call_actions": "Actions de l'appel", + "unit_actions": "Actions de l'unité", + "personnel_actions": { + "title": "Actions du personnel", + "status_tab": "Statut", + "staffing_tab": "Effectifs", + "select_status": "Sélectionner le statut", + "select_staffing": "Sélectionner le niveau d'effectifs", + "destination": "Destination", + "no_destination": "Aucune destination", + "note": "Note", + "note_placeholder": "Ajouter une note optionnelle...", + "update_status": "Mettre à jour le statut", + "update_staffing": "Mettre à jour les effectifs", + "no_statuses_available": "Aucun statut disponible", + "no_staffings_available": "Aucun niveau d'effectifs disponible" + }, + "unit_actions_panel": { + "status": "Statut", + "select_status": "Sélectionner le statut", + "destination": "Destination", + "no_destination": "Aucune destination", + "note": "Note", + "note_placeholder": "Ajouter une note optionnelle...", + "update_status": "Mettre à jour le statut", + "no_statuses_available": "Aucun statut disponible", + "no_active_calls": "Aucun appel actif", + "no_stations_available": "Aucune caserne disponible", + "no_destinations_available": "Aucune destination disponible" + }, + "call": "Appel", + "station": "Caserne", + "calls": "Appels", + "stations": "Casernes", + "no_stations_available": "Aucune caserne disponible", + "new_call": "Nouvel appel", + "view_details": "Détails", + "add_note": "Ajouter une note", + "close_call": "Clôturer", + "set_status": "Définir le statut", + "set_staffing": "Effectifs", + "dispatch": "Dispatch", + "select_items_for_actions": "Sélectionnez un appel, une unité ou du personnel pour activer les actions contextuelles", + "weather": { + "clear": "Dégagé", + "mainly_clear": "Principalement dégagé", + "partly_cloudy": "Partiellement nuageux", + "overcast": "Couvert", + "fog": "Brouillard", + "drizzle": "Bruine", + "freezing_drizzle": "Bruine verglaçante", + "rain": "Pluie", + "freezing_rain": "Pluie verglaçante", + "snow": "Neige", + "rain_showers": "Averses de pluie", + "snow_showers": "Averses de neige", + "thunderstorm": "Orage", + "thunderstorm_hail": "Orage avec grêle", + "unknown": "Inconnu" + } + }, + "tabs": { + "calls": "Appels", + "calendar": "Calendrier", + "contacts": "Contacts", + "home": "Accueil", + "map": "Carte", + "messages": "Messages", + "notes": "Notes", + "protocols": "Protocoles", + "settings": "Paramètres", + "shifts": "Quarts", + "personnel": "Personnel" + }, + "check_in": { + "tab_title": "Pointage", + "timer_status": "Statut de la minuterie", + "perform_check_in": "Pointer", + "check_in_success": "Pointage enregistré avec succès", + "check_in_error": "Échec de l'enregistrement du pointage", + "checked_in_by": "par {{name}}", + "last_check_in": "Dernier pointage", + "elapsed": "Écoulé", + "duration": "Durée", + "status_ok": "OK", + "status_green": "OK", + "status_warning": "Avertissement", + "status_yellow": "Avertissement", + "status_overdue": "En retard", + "status_red": "En retard", + "status_critical": "Critique", + "history": "Historique des pointages", + "no_timers": "Aucune minuterie de pointage configurée", + "timers_disabled": "Les minuteries de pointage sont désactivées pour cet appel", + "type_personnel": "Personnel", + "type_unit": "Unité", + "type_ic": "Commandant des opérations", + "type_par": "PAR", + "type_hazmat": "Exposition Hazmat", + "type_sector_rotation": "Rotation de secteur", + "type_rehab": "Réhabilitation", + "add_note": "Ajouter une note (optionnel)", + "confirm": "Confirmer le pointage", + "minutes_ago": "il y a {{count}} min", + "select_target": "Sélectionner l'entité à pointer", + "overdue_count": "{{count}} en retard", + "warning_count": "{{count}} avertissement", + "enable_timers": "Activer les minuteries", + "disable_timers": "Désactiver les minuteries", + "summary": "{{overdue}} en retard, {{warning}} avertissement, {{ok}} ok" + }, + "units": { + "title": "Unités" + }, + "weatherAlerts": { + "title": "Alertes météo", + "activeAlerts": "Alertes actives", + "noActiveAlerts": "Aucune alerte météo active", + "moreAlerts": "+{{count}} de plus", + "severity": { + "extreme": "Extrême", + "severe": "Sévère", + "moderate": "Modéré", + "minor": "Mineur", + "unknown": "Inconnu" + }, + "category": { + "met": "Météorologique", + "fire": "Incendie", + "health": "Santé", + "env": "Environnemental", + "other": "Autre" + }, + "urgency": { + "immediate": "Immédiate", + "expected": "Prévue", + "future": "Future", + "past": "Passée", + "unknown": "Inconnue" + }, + "certainty": { + "observed": "Observée", + "likely": "Probable", + "possible": "Possible", + "unlikely": "Peu probable", + "unknown": "Inconnue" + }, + "status": { + "active": "Active", + "updated": "Mise à jour", + "expired": "Expirée", + "cancelled": "Annulée" + }, + "detail": { + "headline": "Titre", + "description": "Description", + "instruction": "Instructions", + "area": "Zone touchée", + "effective": "En vigueur", + "onset": "Début", + "expires": "Expire", + "sent": "Envoyé", + "sender": "Source", + "urgency": "Urgence", + "certainty": "Certitude" + }, + "filter": { + "all": "Toutes", + "nearby": "À proximité" + }, + "sort": { + "severity": "Sévérité", + "expires": "Expire bientôt", + "newest": "Plus récentes" + }, + "banner": { + "viewAll": "Voir tout" + } + }, + "videoFeeds": { + "title": "Flux vidéo", + "noFeeds": "Aucun flux vidéo pour cet appel", + "addFeed": "Ajouter un flux vidéo", + "editFeed": "Modifier le flux vidéo", + "deleteFeed": "Supprimer le flux vidéo", + "deleteConfirm": "Êtes-vous sûr de vouloir supprimer ce flux vidéo ?", + "watch": "Regarder", + "goLive": "Diffuser en direct", + "stopLive": "Arrêter la diffusion", + "flipCamera": "Inverser la caméra", + "feedAdded": "Flux vidéo ajouté", + "feedUpdated": "Flux vidéo mis à jour", + "feedDeleted": "Flux vidéo supprimé", + "feedError": "Échec du chargement du flux vidéo", + "unsupportedFormat": "Ce format de flux n'est pas pris en charge sur mobile", + "copyUrl": "Copier l'URL", + "form": { + "name": "Nom du flux", + "namePlaceholder": "ex. : Drone Véhicule 1", + "url": "URL du flux", + "urlPlaceholder": "ex. : https://stream.example.com/live.m3u8", + "feedType": "Type de caméra", + "feedFormat": "Format du flux", + "description": "Description", + "descriptionPlaceholder": "Description optionnelle", + "status": "Statut", + "sortOrder": "Ordre de tri", + "cameraLocation": "Emplacement de la caméra", + "useCurrentLocation": "Utiliser la position actuelle" + }, + "type": { + "drone": "Drone", + "fixedCamera": "Caméra fixe", + "bodyCam": "Caméra corporelle", + "trafficCam": "Caméra de circulation", + "weatherCam": "Caméra météo", + "satelliteFeed": "Flux satellite", + "webCam": "Webcam", + "other": "Autre" + }, + "format": { + "rtsp": "RTSP", + "hls": "HLS", + "mjpeg": "MJPEG", + "youtubeLive": "YouTube Live", + "webrtc": "WebRTC", + "dash": "DASH", + "embed": "Intégré", + "other": "Autre" + }, + "status": { + "active": "Actif", + "inactive": "Inactif", + "error": "Erreur" + } + }, + "welcome": "Bienvenue sur le site de l'application obytes" +} diff --git a/src/translations/it.json b/src/translations/it.json new file mode 100644 index 0000000..fa02d0d --- /dev/null +++ b/src/translations/it.json @@ -0,0 +1,1151 @@ +{ + "app": { + "title": "Resgrid Dispatch" + }, + "audio_streams": { + "buffering": "Buffering", + "buffering_stream": "Buffering del flusso audio...", + "close": "Chiudi", + "currently_playing": "In riproduzione: {{streamName}}", + "loading": "Caricamento", + "loading_stream": "Caricamento del flusso audio...", + "loading_streams": "Caricamento dei flussi audio...", + "name": "Nome", + "no_stream_playing": "Nessun flusso audio in riproduzione", + "none": "Nessuno", + "playing": "In riproduzione", + "refresh_streams": "Aggiorna flussi", + "select_placeholder": "Seleziona un flusso audio", + "select_stream": "Seleziona flusso audio", + "status": "Stato", + "stopped": "Interrotto", + "stream_info": "Informazioni flusso", + "stream_selected": "Selezionato: {{streamName}}", + "title": "Flussi audio", + "type": "Tipo" + }, + "bluetooth": { + "applied": "Applicato", + "audio": "Audio", + "audioActive": "Audio attivo", + "audio_device": "Auricolare BT", + "availableDevices": "Dispositivi disponibili", + "available_devices": "Dispositivi disponibili", + "bluetooth_not_ready": "Il Bluetooth è {{state}}. Si prega di abilitare il Bluetooth.", + "buttonControlAvailable": "Controllo pulsante disponibile", + "checking": "Verifica stato Bluetooth...", + "clear": "Cancella", + "connect": "Connetti", + "connected": "Connesso", + "connectionError": "Errore di connessione", + "current_selection": "Selezione attuale", + "disconnect": "Disconnetti", + "doublePress": "Doppia ", + "liveKitActive": "LiveKit attivo", + "longPress": "Lunga ", + "micControl": "Controllo microfono", + "mute": "Silenzia", + "noDevicesFound": "Nessun dispositivo audio trovato", + "noDevicesFoundRetry": "Nessun dispositivo audio trovato. Riprova la scansione.", + "no_device_selected": "Nessun dispositivo selezionato", + "no_devices_found": "Nessun dispositivo audio Bluetooth trovato", + "not_connected": "Non connesso", + "poweredOff": "Il Bluetooth è spento. Si prega di abilitare il Bluetooth per connettere i dispositivi audio.", + "pttStart": "Inizio PTT", + "pttStop": "Fine PTT", + "recentButtonEvents": "Eventi pulsante recenti", + "scan": "Scansiona", + "scanAgain": "Scansiona di nuovo", + "scan_again": "Scansiona di nuovo", + "scan_error_message": "Impossibile scansionare i dispositivi Bluetooth", + "scan_error_title": "Errore di scansione", + "scanning": "Scansione...", + "select_device": "Seleziona dispositivo Bluetooth", + "selected": "Selezionato", + "selection_error_message": "Impossibile salvare il dispositivo preferito", + "selection_error_title": "Errore di selezione", + "startScanning": "Avvia scansione", + "stopScan": "Interrompi scansione", + "supports_mic_control": "Controllo microfono", + "tap_scan_to_find_devices": "Tocca 'Scansiona' per trovare i dispositivi audio Bluetooth", + "title": "Audio Bluetooth", + "unauthorized": "Permesso Bluetooth negato. Si prega di concedere i permessi Bluetooth nelle Impostazioni.", + "unknown": "Sconosciuto", + "unknownDevice": "Dispositivo sconosciuto", + "unknown_device": "Dispositivo sconosciuto", + "unmute": "Riattiva audio", + "volumeDown": "Volume -", + "volumeUp": "Volume +" + }, + "callImages": { + "add": "Aggiungi immagine", + "add_new": "Aggiungi nuova immagine", + "default_name": "Immagine senza titolo", + "error": "Errore nel caricamento delle immagini", + "failed_to_load": "Impossibile caricare l'immagine", + "image_alt": "Immagine intervento", + "image_name": "Nome immagine", + "image_note": "Nota immagine", + "loading": "Caricamento...", + "no_images": "Nessuna immagine disponibile", + "no_images_description": "Aggiungi immagini al tuo intervento per facilitare la documentazione e la comunicazione", + "select_from_gallery": "Seleziona dalla galleria", + "take_photo": "Scatta foto", + "title": "Immagini intervento", + "upload": "Carica" + }, + "callNotes": { + "addNote": "Aggiungi nota", + "addNotePlaceholder": "Aggiungi una nuova nota...", + "noNotes": "Nessuna nota disponibile per questo intervento", + "noSearchResults": "Nessuna nota corrisponde alla ricerca", + "searchPlaceholder": "Cerca note...", + "title": "Note intervento" + }, + "call_detail": { + "address": "Indirizzo", + "call_location": "Posizione intervento", + "close_call": "Chiudi intervento", + "close_call_confirmation": "Sei sicuro di voler chiudere questo intervento?", + "close_call_error": "Impossibile chiudere l'intervento", + "close_call_note": "Nota di chiusura", + "close_call_note_placeholder": "Inserisci una nota sulla chiusura dell'intervento", + "close_call_success": "Intervento chiuso con successo", + "close_call_type": "Tipo di chiusura", + "close_call_type_placeholder": "Seleziona tipo di chiusura", + "close_call_type_required": "Seleziona un tipo di chiusura", + "close_call_types": { + "cancelled": "Annullato", + "closed": "Chiuso", + "false_alarm": "Falso allarme", + "founded": "Fondato", + "minor": "Minore", + "transferred": "Trasferito", + "unfounded": "Infondato" + }, + "contact_email": "Email", + "contact_info": "Informazioni di contatto", + "contact_name": "Nome contatto", + "contact_phone": "Telefono", + "edit_call": "Modifica intervento", + "external_id": "ID esterno", + "failed_to_open_maps": "Impossibile aprire l'applicazione mappe", + "files": { + "add_file": "Aggiungi file", + "button": "File", + "empty": "Nessun file disponibile", + "empty_description": "Aggiungi file al tuo intervento per facilitare la documentazione e la comunicazione", + "error": "Errore nel caricamento dei file", + "file_name": "Nome file", + "name_required": "Inserisci un nome per il file", + "no_files": "Nessun file disponibile", + "no_files_description": "Aggiungi file al tuo intervento per facilitare la documentazione e la comunicazione", + "open_error": "Errore nell'apertura del file", + "select_error": "Errore nella selezione del file", + "select_file": "Seleziona file", + "share_error": "Errore nella condivisione del file", + "title": "File intervento", + "upload": "Carica", + "upload_error": "Errore nel caricamento del file", + "uploading": "Caricamento in corso..." + }, + "group": "Gruppo", + "images": "Immagini", + "loading": "Caricamento dettagli intervento...", + "nature": "Natura", + "no_additional_info": "Nessuna informazione aggiuntiva disponibile", + "no_contact_info": "Nessuna informazione di contatto disponibile", + "no_dispatched": "Nessuna unità inviata per questo intervento", + "no_location": "Nessun dato di posizione disponibile", + "no_location_for_routing": "Nessun dato di posizione disponibile per il percorso", + "no_protocols": "Nessun protocollo associato a questo intervento", + "no_timeline": "Nessun evento nella cronologia disponibile", + "not_available": "N/D", + "not_found": "Intervento non trovato", + "missing_call_id": "ID intervento mancante", + "note": "Nota", + "notes": "Note", + "priority": "Priorità", + "reference_id": "ID di riferimento", + "set_active": "Imposta come attivo", + "set_active_error": "Impossibile impostare l'intervento come attivo", + "set_active_success": "Intervento impostato come attivo", + "setting_active": "Impostazione in corso...", + "status": "Stato", + "tabs": { + "contact": "Contatto", + "dispatched": "Inviati", + "info": "Info", + "protocols": "Protocolli", + "timeline": "Attività", + "video": "Video" + }, + "timestamp": "Data e ora", + "title": "Dettagli intervento", + "type": "Tipo", + "unit": "Unità", + "update_call_error": "Impossibile aggiornare l'intervento", + "update_call_success": "Intervento aggiornato con successo" + }, + "calls": { + "address": "Indirizzo", + "address_found": "Indirizzo trovato e posizione aggiornata", + "address_not_found": "Indirizzo non trovato, prova un indirizzo diverso", + "address_placeholder": "Inserisci l'indirizzo dell'intervento", + "address_required": "Inserisci un indirizzo da cercare", + "call_details": "Dettagli intervento", + "call_location": "Posizione intervento", + "call_number": "Numero intervento", + "call_priority": "Priorità intervento", + "confirm_deselect_message": "Sei sicuro di voler deselezionare l'intervento attivo corrente?", + "confirm_deselect_title": "Deseleziona intervento attivo", + "contact_info": "Informazioni di contatto", + "contact_info_placeholder": "Inserisci le informazioni del contatto", + "contact_name": "Nome contatto", + "contact_name_placeholder": "Inserisci il nome del contatto", + "contact_phone": "Telefono contatto", + "contact_phone_placeholder": "Inserisci il telefono del contatto", + "coordinates": "Coordinate GPS", + "coordinates_found": "Coordinate trovate e indirizzo aggiornato", + "coordinates_geocoding_error": "Impossibile ottenere l'indirizzo per le coordinate, ma la posizione è stata impostata sulla mappa", + "coordinates_invalid_format": "Formato coordinate non valido. Usa il formato: latitudine, longitudine", + "coordinates_no_address": "Coordinate impostate sulla mappa, ma nessun indirizzo trovato", + "coordinates_out_of_range": "Coordinate fuori intervallo. La latitudine deve essere tra -90 e 90, la longitudine tra -180 e 180", + "coordinates_placeholder": "Inserisci le coordinate GPS (es. 37.7749, -122.4194)", + "coordinates_required": "Inserisci le coordinate da cercare", + "create": "Crea", + "create_error": "Errore nella creazione dell'intervento", + "create_new_call": "Crea nuovo intervento", + "create_success": "Intervento creato con successo", + "description": "Descrizione", + "description_placeholder": "Inserisci la descrizione dell'intervento", + "deselect": "Deseleziona", + "directions": "Indicazioni", + "dispatch_to": "Invia a", + "dispatch_to_everyone": "Invia a tutto il personale disponibile", + "edit_call": "Modifica intervento", + "edit_call_description": "Aggiorna le informazioni dell'intervento", + "everyone": "Tutti", + "files": { + "no_files": "Nessun file disponibile", + "no_files_description": "Nessun file è stato ancora aggiunto a questo intervento", + "title": "File intervento" + }, + "geocoding_error": "Impossibile cercare l'indirizzo, riprova", + "groups": "Gruppi", + "invalid_priority": "Priorità selezionata non valida. Seleziona una priorità valida.", + "invalid_type": "Tipo selezionato non valido. Seleziona un tipo di intervento valido.", + "loading": "Caricamento interventi...", + "loading_calls": "Caricamento interventi...", + "name": "Nome", + "name_placeholder": "Inserisci il nome dell'intervento", + "nature": "Natura", + "nature_placeholder": "Inserisci la natura dell'intervento", + "new_call": "Nuovo intervento", + "new_call_description": "Crea un nuovo intervento per avviare un nuovo incidente", + "no_call_selected": "Nessun intervento attivo", + "no_call_selected_info": "Questa unità non sta attualmente rispondendo a nessun intervento", + "no_calls": "Nessun intervento attivo", + "no_calls_available": "Nessun intervento disponibile", + "no_calls_description": "Nessun intervento attivo trovato. Seleziona un intervento attivo per visualizzare i dettagli.", + "no_location_message": "Questo intervento non dispone di dati di posizione per la navigazione.", + "no_location_title": "Posizione non disponibile", + "no_open_calls": "Nessun intervento aperto disponibile", + "note": "Nota", + "note_placeholder": "Inserisci la nota dell'intervento", + "plus_code": "Plus Code", + "plus_code_found": "Plus code trovato e posizione aggiornata", + "plus_code_geocoding_error": "Impossibile cercare il plus code, riprova", + "plus_code_not_found": "Plus code non trovato, prova un plus code diverso", + "plus_code_placeholder": "Inserisci il plus code (es. 849VCWC8+R9)", + "plus_code_required": "Inserisci un plus code da cercare", + "priority": "Priorità", + "priority_placeholder": "Seleziona la priorità dell'intervento", + "roles": "Ruoli", + "search": "Cerca interventi...", + "select_active_call": "Seleziona intervento attivo", + "select_address": "Seleziona indirizzo", + "select_address_placeholder": "Seleziona l'indirizzo dell'intervento", + "select_description": "Seleziona descrizione", + "select_dispatch_recipients": "Seleziona destinatari invio", + "select_location": "Seleziona posizione sulla mappa", + "select_name": "Seleziona nome", + "select_nature": "Seleziona natura", + "select_nature_placeholder": "Seleziona la natura dell'intervento", + "select_priority": "Seleziona priorità", + "select_priority_placeholder": "Seleziona la priorità dell'intervento", + "select_recipients": "Seleziona destinatari", + "select_type": "Seleziona tipo", + "selected": "selezionato", + "title": "Interventi", + "type": "Tipo", + "units": "Unità", + "users": "Utenti", + "viewNotes": "Note", + "view_details": "Visualizza dettagli", + "what3words": "what3words", + "what3words_found": "Indirizzo what3words trovato e posizione aggiornata", + "what3words_geocoding_error": "Impossibile cercare l'indirizzo what3words, riprova", + "what3words_invalid_format": "Formato what3words non valido. Usa il formato: parola.parola.parola", + "what3words_not_found": "Indirizzo what3words non trovato, prova un indirizzo diverso", + "what3words_placeholder": "Inserisci l'indirizzo what3words (es. filled.count.soap)", + "what3words_required": "Inserisci un indirizzo what3words da cercare", + "expand_map": "Espandi mappa", + "schedule_dispatch": "Programma invio", + "schedule_dispatch_description": "Imposta una data e ora futura per programmare l'invio di questo intervento", + "scheduled_on": "Data e ora programmata", + "scheduled_on_helper": "Lascia vuoto per inviare immediatamente", + "scheduled_on_past_error": "L'orario programmato deve essere nel futuro", + "contact_information": "Informazioni di contatto", + "new_call_web_hint": "Compila i dettagli dell'intervento qui sotto. Premi Ctrl+Invio per creare.", + "edit_call_web_hint": "Aggiorna i dettagli dell'intervento qui sotto. Premi Ctrl+S per salvare.", + "keyboard_shortcuts": "Suggerimento: Premi Ctrl+Invio per creare, Esc per annullare", + "edit_keyboard_shortcuts": "Suggerimento: Premi Ctrl+S per salvare, Esc per annullare", + "templates": { + "title": "Modelli intervento", + "select_template": "Seleziona modello", + "search_placeholder": "Cerca modelli...", + "none": "Nessun modello disponibile", + "template_applied": "Modello applicato" + }, + "form": { + "title": "Modulo intervento", + "no_form": "Nessun modulo intervento configurato" + }, + "linked_calls": { + "title": "Intervento collegato", + "select": "Collega a un intervento esistente", + "change": "Cambia intervento collegato", + "none": "Nessun intervento attivo disponibile", + "search_placeholder": "Cerca interventi...", + "linked": "Collegato" + }, + "contact_picker": { + "title": "Seleziona contatto", + "search_placeholder": "Cerca contatti...", + "none": "Nessun contatto disponibile" + }, + "protocols": { + "title": "Protocolli", + "select": "Seleziona protocolli", + "none": "Nessun protocollo disponibile", + "selected_count": "selezionati", + "expand_questions": "Visualizza domande", + "collapse": "Comprimi" + } + }, + "common": { + "add": "Aggiungi", + "back": "Indietro", + "cancel": "Annulla", + "creating": "Creazione...", + "saving": "Salvataggio...", + "unsaved_changes": "Modifiche non salvate", + "close": "Chiudi", + "confirm": "Conferma", + "confirm_location": "Conferma posizione", + "delete": "Elimina", + "dismiss": "Chiudi", + "done": "Fatto", + "edit": "Modifica", + "error": "Errore", + "errorOccurred": "Si è verificato un errore", + "get_my_location": "Ottieni la mia posizione", + "go_back": "Torna indietro", + "loading": "Caricamento...", + "loading_address": "Caricamento indirizzo...", + "next": "Avanti", + "noActiveUnit": "Nessuna unità attiva impostata", + "noActiveUnitDescription": "Imposta un'unità attiva dalla pagina delle impostazioni per accedere ai controlli di stato", + "noDataAvailable": "Nessun dato disponibile", + "no_address_found": "Nessun indirizzo trovato", + "no_location": "Nessun dato di posizione disponibile", + "no_results_found": "Nessun risultato trovato", + "no_unit_selected": "Nessuna unità selezionata", + "nothingToDisplay": "Non c'è nulla da visualizzare al momento", + "of": "di", + "ok": "Ok", + "optional": "facoltativo", + "permission_denied": "Permesso negato", + "previous": "Precedente", + "remove": "Rimuovi", + "retry": "Riprova", + "route": "Percorso", + "save": "Salva", + "search": "Cerca...", + "set_location": "Imposta posizione", + "share": "Condividi", + "step": "Passo", + "submit": "Invia", + "submitting": "Invio in corso...", + "tryAgainLater": "Riprova più tardi", + "unknown": "Sconosciuto", + "unknown_department": "Dipartimento sconosciuto", + "unknown_user": "Utente sconosciuto", + "upload": "Carica", + "uploading": "Caricamento in corso..." + }, + "contacts": { + "add": "Aggiungi contatto", + "addedBy": "Aggiunto da", + "addedOn": "Aggiunto il", + "additionalInformation": "Informazioni aggiuntive", + "address": "Indirizzo", + "bluesky": "Bluesky", + "cancel": "Annulla", + "cellPhone": "Cellulare", + "city": "Città", + "cityState": "Città e Provincia", + "cityStateZip": "Città, Provincia, CAP", + "company": "Azienda", + "contactInformation": "Informazioni di contatto", + "contactNotes": "Note contatto", + "contactNotesEmpty": "Nessuna nota trovata per questo contatto", + "contactNotesEmptyDescription": "Le note aggiunte a questo contatto appariranno qui", + "contactNotesExpired": "Questa nota è scaduta", + "contactNotesLoading": "Caricamento note contatto...", + "contactType": "Tipo contatto", + "countryId": "ID paese", + "delete": "Elimina", + "deleteConfirm": "Sei sicuro di voler eliminare questo contatto?", + "deleteSuccess": "Contatto eliminato con successo", + "description": "Aggiungi e gestisci i tuoi contatti", + "details": "Dettagli contatto", + "detailsTab": "Dettagli", + "edit": "Modifica contatto", + "editedBy": "Modificato da", + "editedOn": "Modificato il", + "email": "Email", + "empty": "Nessun contatto trovato", + "emptyDescription": "Aggiungi contatti per gestire le tue connessioni personali e professionali", + "entranceCoordinates": "Coordinate ingresso", + "exitCoordinates": "Coordinate uscita", + "expires": "Scadenza", + "facebook": "Facebook", + "faxPhone": "Fax", + "formError": "Correggi gli errori nel modulo", + "homePhone": "Telefono casa", + "identification": "Identificazione", + "important": "Segna come importante", + "instagram": "Instagram", + "internal": "Interno", + "invalidEmail": "Indirizzo email non valido", + "linkedin": "LinkedIn", + "locationCoordinates": "Coordinate posizione", + "locationInformation": "Informazioni posizione", + "mastodon": "Mastodon", + "mobile": "Cellulare", + "name": "Nome", + "noteAlert": "Avviso", + "noteType": "Tipo", + "notes": "Note", + "notesTab": "Note", + "officePhone": "Telefono ufficio", + "otherInfo": "Altre informazioni", + "person": "Persona", + "phone": "Telefono", + "public": "Pubblico", + "required": "Obbligatorio", + "save": "Salva contatto", + "saveSuccess": "Contatto salvato con successo", + "search": "Cerca contatti...", + "shouldAlert": "Deve avvisare", + "socialMediaWeb": "Social media e Web", + "state": "Provincia", + "stateId": "ID provincia", + "systemInformation": "Informazioni di sistema", + "tabs": { + "details": "Dettagli", + "notes": "Note" + }, + "threads": "Threads", + "title": "Contatti", + "twitter": "Twitter", + "visibility": "Visibilità", + "website": "Sito web", + "zip": "CAP" + }, + "form": { + "invalid_url": "Inserisci un URL valido che inizi con http:// o https://", + "required": "Questo campo è obbligatorio" + }, + "livekit": { + "audio_devices": "Dispositivi audio", + "audio_settings": "Impostazioni audio", + "connected_to_room": "Connesso al canale", + "connecting": "Connessione...", + "disconnect": "Disconnetti", + "join": "Entra", + "microphone": "Microfono", + "mute": "Silenzia", + "no_rooms_available": "Nessun canale vocale disponibile", + "speaker": "Altoparlante", + "speaking": "In conversazione", + "title": "Canali vocali", + "unmute": "Riattiva audio" + }, + "loading": { + "loading": "Caricamento...", + "loadingData": "Caricamento dati...", + "pleaseWait": "Attendere prego", + "processingRequest": "Elaborazione della richiesta..." + }, + "sso": { + "authenticating": "Autenticazione...", + "back_to_login": "Torna al login", + "back_to_lookup": "Cambia utente", + "continue_button": "Continua", + "department_id_label": "ID dipartimento", + "department_id_placeholder": "Inserisci l'ID del dipartimento", + "error_generic": "Accesso fallito. Riprova.", + "error_oidc_cancelled": "L'accesso è stato annullato.", + "error_oidc_not_ready": "Il provider SSO si sta caricando, attendere prego.", + "error_sso_not_enabled": "L'accesso unico non è abilitato per questo utente.", + "error_token_exchange": "Impossibile completare l'accesso. Riprova.", + "error_user_not_found": "Utente non trovato. Verifica e riprova.", + "looking_up": "Ricerca in corso...", + "optional": "facoltativo", + "page_subtitle": "Inserisci il tuo nome utente per cercare le opzioni di accesso della tua organizzazione.", + "page_title": "Single Sign-On", + "provider_oidc": "OpenID Connect (OIDC)", + "provider_saml": "SAML 2.0", + "sign_in_button": "Accedi con SSO", + "sign_in_title": "Accedi", + "sso_button": "Accesso SSO" + }, + "login": { + "branding_subtitle": "Software di centrale operativa potente per vigili del fuoco, soccorso e organizzazioni di sicurezza pubblica.", + "branding_title": "Gestione delle emergenze", + "errorModal": { + "confirmButton": "OK", + "message": "Verifica nome utente e password e riprova.", + "title": "Accesso fallito" + }, + "feature_dispatch_desc": "Invia istantaneamente le unità e gestisci gli interventi con aggiornamenti in tempo reale su tutti i dispositivi.", + "feature_dispatch_title": "Centrale operativa in tempo reale", + "feature_mapping_desc": "Traccia le unità in tempo reale con mappe dettagliate, percorsi e gestione della posizione.", + "feature_mapping_title": "Cartografia avanzata", + "feature_personnel_desc": "Gestisci il tuo team con accesso basato sui ruoli, monitoraggio dello stato e strumenti di comunicazione.", + "feature_personnel_title": "Gestione personale", + "footer_text": "Creato con ❤️ a Lake Tahoe", + "login": "Accedi", + "login_button": "Accedi", + "login_button_description": "Accedi al tuo account per continuare", + "login_button_error": "Errore durante l'accesso", + "login_button_loading": "Accesso in corso...", + "login_button_success": "Accesso effettuato con successo", + "no_account": "Non hai un account?", + "page_subtitle": "Inserisci le tue credenziali per accedere.", + "page_title": "Resgrid Dispatch", + "password": "Password", + "password_incorrect": "Password errata", + "password_placeholder": "Inserisci la tua password", + "register": "Registrati", + "title": "Accesso", + "username": "Nome utente", + "username_placeholder": "Inserisci il tuo nome utente", + "welcome_title": "Bentornato" + }, + "lockscreen": { + "message": "Inserisci la tua password per sbloccare lo schermo", + "not_you": "Non sei tu? Torna al login", + "password": "Password", + "password_placeholder": "Inserisci la tua password", + "title": "Schermata di blocco", + "unlock_button": "Sblocca", + "unlock_failed": "Sblocco fallito. Riprova.", + "unlocking": "Sblocco in corso...", + "welcome_back": "Bentornato" + }, + "maintenance": { + "downtime_message": "Stiamo lavorando per completare la manutenzione il prima possibile. Ricontrolla presto.", + "downtime_title": "Qual è il tempo di inattività?", + "message": "Ricontrolla tra qualche momento.", + "support_message": "Se hai bisogno di assistenza, contattaci a", + "support_title": "Hai bisogno di supporto?", + "title": "Sito in manutenzione", + "why_down_message": "Stiamo effettuando una manutenzione programmata per migliorare la tua esperienza. Ci scusiamo per l'inconveniente.", + "why_down_title": "Perché il sito è offline?" + }, + "menu": { + "calls": "Interventi", + "calls_list": "Lista interventi", + "contacts": "Contatti", + "home": "Home", + "map": "Mappa", + "menu": "Menu", + "messages": "Messaggi", + "new_call": "Nuovo intervento", + "personnel": "Personale", + "protocols": "Protocolli", + "settings": "Impostazioni", + "units": "Unità", + "weatherAlerts": "Allerte meteo" + }, + "map": { + "call_set_as_current": "Intervento impostato come intervento corrente", + "failed_to_open_maps": "Impossibile aprire l'applicazione mappe", + "failed_to_set_current_call": "Impossibile impostare l'intervento come intervento corrente", + "layers": "Livelli mappa", + "no_layers": "Nessun livello disponibile", + "no_location_for_routing": "Nessun dato di posizione disponibile per il percorso", + "pin_color": "Colore segnaposto", + "recenter_map": "Ricentra mappa", + "set_as_current_call": "Imposta come intervento corrente", + "show_all": "Mostra tutto", + "hide_all": "Nascondi tutto", + "view_call_details": "Visualizza dettagli intervento" + }, + "notes": { + "actions": { + "add": "Aggiungi nota", + "delete_confirm": "Sei sicuro di voler eliminare questa nota?" + }, + "details": { + "close": "Chiudi", + "created": "Creato", + "delete": "Elimina", + "edit": "Modifica", + "tags": "Etichette", + "title": "Dettagli nota", + "updated": "Aggiornato" + }, + "empty": "Nessuna nota trovata", + "emptyDescription": "Non sono state ancora create note per il tuo dipartimento.", + "search": "Cerca note...", + "title": "Note" + }, + "personnel": { + "title": "Personale", + "search": "Cerca personale...", + "loading": "Caricamento personale...", + "empty": "Nessun personale trovato", + "empty_description": "Nessun personale è attualmente disponibile nel tuo dipartimento.", + "no_results": "Nessun personale corrisponde alla tua ricerca", + "no_results_description": "Prova a modificare i termini di ricerca.", + "status": "Stato", + "staffing": "Organico", + "group": "Gruppo", + "roles": "Ruoli", + "email": "E-mail", + "phone": "Telefono", + "id_number": "Numero ID", + "responding_to": "Risponde a", + "status_updated": "Stato aggiornato", + "staffing_updated": "Organico aggiornato", + "details": "Dettagli personale", + "contact_info": "Informazioni di contatto", + "status_info": "Stato e organico", + "no_email": "Nessuna e-mail disponibile", + "no_phone": "Nessun telefono disponibile", + "no_group": "Non assegnato", + "no_roles": "Nessun ruolo assegnato", + "unknown_status": "Sconosciuto", + "on_duty": "In servizio", + "off_duty": "Fuori servizio", + "call_phone": "Chiama", + "send_email": "E-mail", + "custom_fields": "Informazioni aggiuntive" + }, + "onboarding": { + "screen1": { + "title": "Resgrid Dispatch", + "description": "Crea, invia e gestisci gli interventi di emergenza con un potente centro di comando mobile a portata di mano" + }, + "screen2": { + "title": "Consapevolezza situazionale in tempo reale", + "description": "Traccia tutte le unità, il personale e le risorse su una mappa interattiva con aggiornamenti di stato in tempo reale e AVL" + }, + "screen3": { + "title": "Coordinamento senza interruzioni", + "description": "Comunica istantaneamente con le unità sul campo, aggiorna lo stato degli interventi e coordina gli sforzi di risposta da qualsiasi luogo" + }, + "skip": "Salta", + "next": "Avanti", + "getStarted": "Iniziamo" + }, + "protocols": { + "details": { + "close": "Chiudi", + "code": "Codice", + "created": "Creato", + "title": "Dettagli protocollo", + "updated": "Aggiornato" + }, + "empty": "Nessun protocollo trovato", + "emptyDescription": "Non sono stati ancora creati protocolli per il tuo dipartimento.", + "search": "Cerca protocolli...", + "title": "Protocolli" + }, + "push_notifications": { + "close": "Chiudi", + "message": "Messaggio", + "new_notification": "Nuova notifica", + "title": "Titolo", + "types": { + "call": "Intervento", + "chat": "Chat", + "group_chat": "Chat di gruppo", + "message": "Messaggio", + "notification": "Notifica" + }, + "unknown_type_warning": "Ricevuto tipo di notifica sconosciuto", + "view_call": "Visualizza intervento", + "view_message": "Visualizza messaggio" + }, + "roles": { + "modal": { + "title": "Assegnazione ruoli unità" + }, + "selectUser": "Seleziona utente", + "status": "{{active}} di {{total}} ruoli", + "tap_to_manage": "Tocca per gestire i ruoli", + "unassigned": "Non assegnato" + }, + "settings": { + "about": "Informazioni", + "account": "Account", + "active_unit": "Unità attiva", + "app_info": "Informazioni app", + "app_name": "Nome app", + "arabic": "Arabo", + "audio_device_selection": { + "bluetooth_device": "Dispositivo Bluetooth", + "current_selection": "Selezione attuale", + "microphone": "Microfono", + "no_microphones_available": "Nessun microfono disponibile", + "no_speakers_available": "Nessun altoparlante disponibile", + "none_selected": "Nessuno selezionato", + "speaker": "Altoparlante", + "speaker_device": "Dispositivo altoparlante", + "title": "Selezione dispositivo audio", + "unavailable": "Non disponibile", + "wired_device": "Dispositivo cablato" + }, + "background_geolocation": "Geolocalizzazione in background", + "background_geolocation_warning": "Questa funzione consente all'app di tracciare la tua posizione in background. Aiuta con il coordinamento della risposta alle emergenze ma potrebbe influire sulla durata della batteria.", + "background_location": "Posizione in background", + "contact_us": "Contattaci", + "current_unit": "Unità attuale", + "english": "Inglese", + "french": "Francese", + "german": "Tedesco", + "italian": "Italiano", + "enter_password": "Inserisci la tua password", + "enter_server_url": "Inserisci l'URL dell'API Resgrid (es. https://api.resgrid.com)", + "enter_username": "Inserisci il tuo nome utente", + "environment": "Ambiente", + "general": "Generale", + "generale": "Generale", + "github": "Github", + "help_center": "Centro assistenza", + "keep_alive": "Mantieni attivo", + "keep_alive_warning": "Attenzione: abilitare il mantieni attivo impedirà al dispositivo di andare in standby e potrebbe aumentare significativamente il consumo della batteria.", + "keep_screen_on": "Mantieni schermo acceso", + "language": "Lingua", + "links": "Link", + "login_info": "Informazioni di accesso", + "logout": "Esci", + "more": "Altro", + "no_units_available": "Nessuna unità disponibile", + "none_selected": "Nessuna selezione", + "notifications": "Notifiche push", + "notifications_description": "Abilita le notifiche per ricevere avvisi e aggiornamenti", + "notifications_enable": "Abilita notifiche", + "password": "Password", + "preferences": "Preferenze", + "privacy": "Informativa sulla privacy", + "privacy_policy": "Informativa sulla privacy", + "rate": "Valuta", + "select_unit": "Seleziona unità", + "server": "Server", + "server_url": "URL del server", + "server_url_note": "Nota: questo è l'URL dell'API Resgrid. Viene utilizzato per connettersi al server Resgrid. Non includere /api/v4 nell'URL o una barra finale.", + "set_active_unit": "Imposta unità attiva", + "share": "Condividi", + "polish": "Polacco", + "spanish": "Spagnolo", + "swedish": "Svedese", + "ukrainian": "Ucraino", + "status_page": "Stato del sistema", + "support": "Supporto", + "support_us": "Supportaci", + "terms": "Termini di servizio", + "theme": { + "dark": "Scuro", + "light": "Chiaro", + "system": "Sistema", + "title": "Tema" + }, + "title": "Impostazioni", + "unit_selected_successfully": "{{unitName}} selezionata con successo", + "unit_selection": "Selezione unità", + "unit_selection_failed": "Impossibile selezionare l'unità. Riprova.", + "username": "Nome utente", + "version": "Versione", + "website": "Sito web" + }, + "status": { + "add_note": "Aggiungi nota", + "both_destinations_enabled": "Può rispondere a interventi o stazioni", + "call_destination_enabled": "Può rispondere agli interventi", + "calls_tab": "Interventi", + "failed_to_save_status": "Impossibile salvare lo stato. Riprova.", + "general_status": "Stato generale senza destinazione specifica", + "loading_stations": "Caricamento stazioni...", + "no_destination": "Nessuna destinazione", + "no_stations_available": "Nessuna stazione disponibile", + "no_statuses_available": "Nessuno stato disponibile", + "note": "Nota", + "note_optional": "Aggiungi una nota facoltativa per questo aggiornamento di stato", + "note_required": "Inserisci una nota per questo aggiornamento di stato", + "select_destination": "Seleziona destinazione per {{status}}", + "select_destination_type": "Dove vorresti rispondere?", + "select_status": "Seleziona stato", + "select_status_type": "Quale stato vorresti impostare?", + "selected_destination": "Destinazione selezionata", + "selected_status": "Stato selezionato", + "set_status": "Imposta stato", + "station_destination_enabled": "Può rispondere alle stazioni", + "stations_tab": "Stazioni", + "status_saved_successfully": "Stato salvato con successo!" + }, + "dispatch": { + "active_calls": "Interventi attivi", + "pending_calls": "In attesa", + "scheduled_calls": "Programmati", + "units_available": "Disponibili", + "personnel_available": "Disponibili", + "personnel_on_duty": "In servizio", + "units": "Unità", + "personnel": "Personale", + "map": "Mappa", + "notes": "Note", + "activity_log": "Registro attività", + "communications": "Comunicazioni", + "no_active_calls": "Nessun intervento attivo", + "no_units": "Nessuna unità disponibile", + "no_personnel": "Nessun personale disponibile", + "no_notes": "Nessuna nota disponibile", + "no_activity": "Nessuna attività recente", + "current_channel": "Canale attuale", + "audio_stream": "Flusso audio", + "no_stream": "Nessun flusso attivo", + "ptt": "PTT", + "ptt_start": "Inizio PTT", + "ptt_end": "Fine PTT", + "transmitting_on": "Trasmissione su {{channel}}", + "transmission_ended": "Trasmissione terminata", + "voice_disabled": "Voce disabilitata", + "disconnected": "Disconnesso", + "select_channel": "Seleziona canale", + "select_channel_description": "Scegli un canale vocale a cui connettersi", + "change_channel_warning": "Selezionare un nuovo canale disconnetterà dal canale attuale", + "default_channel": "Predefinito", + "no_channels_available": "Nessun canale vocale disponibile", + "system_update": "Aggiornamento di sistema", + "data_refreshed": "Dati aggiornati dal server", + "call_selected": "Intervento selezionato", + "unit_selected": "Unità selezionata", + "unit_deselected": "Unità deselezionata", + "personnel_selected": "Personale selezionato", + "personnel_deselected": "Personale deselezionato", + "loading_map": "Caricamento mappa...", + "map_not_available_web": "Mappa non disponibile sulla piattaforma web", + "filtering_by_call": "Filtro per intervento", + "clear_filter": "Cancella filtro", + "call_filter_active": "Filtro intervento attivo", + "call_filter_cleared": "Filtro intervento cancellato", + "showing_all_data": "Visualizzazione di tutti i dati", + "call_notes": "Note intervento", + "no_call_notes": "Nessuna nota intervento", + "add_call_note_placeholder": "Aggiungi una nota...", + "note_added": "Nota aggiunta", + "note_added_to_console": "Una nuova nota è stata aggiunta alla console", + "add_note_title": "Aggiungi nuova nota", + "note_title_label": "Titolo", + "note_title_placeholder": "Inserisci il titolo della nota...", + "note_category_label": "Categoria", + "note_category_placeholder": "Seleziona una categoria", + "note_no_category": "Nessuna categoria", + "note_body_label": "Contenuto nota", + "note_body_placeholder": "Inserisci il contenuto della nota...", + "note_save_error": "Impossibile salvare la nota: {{error}}", + "note_created": "Nota creata", + "units_on_call": "Unità in servizio", + "no_units_on_call": "Nessuna unità in servizio", + "personnel_on_call": "Personale in servizio", + "no_personnel_on_call": "Nessun personale in servizio", + "call_activity": "Attività intervento", + "no_call_activity": "Nessuna attività intervento", + "on_call": "In servizio", + "filtered": "Filtrato", + "active_filter": "Filtro attivo", + "unit_status_change": "Cambio stato unità", + "personnel_status_change": "Cambio stato personale", + "view_call_details": "Visualizza dettagli intervento", + "dispatched_resources": "Inviati", + "unassigned": "Non assegnato", + "available": "Disponibile", + "unknown": "Sconosciuto", + "search_personnel_placeholder": "Cerca personale...", + "search_calls_placeholder": "Cerca interventi...", + "search_units_placeholder": "Cerca unità...", + "search_notes_placeholder": "Cerca note...", + "signalr_update": "Aggiornamento in tempo reale", + "signalr_connected": "Connesso", + "realtime_updates_active": "Aggiornamenti in tempo reale attivi", + "personnel_status_updated": "Stato personale aggiornato", + "personnel_staffing_updated": "Organico personale aggiornato", + "unit_status_updated": "Stato unità aggiornato", + "calls_updated": "Interventi aggiornati", + "call_added": "Nuovo intervento aggiunto", + "call_closed": "Intervento chiuso", + "check_ins": "Check-in", + "no_check_ins": "Nessun intervento con timer di check-in", + "radio_log": "Registro radio", + "radio": "Radio", + "activity": "Attività", + "actions": "Azioni", + "no_radio_activity": "Nessuna trasmissione radio", + "live": "LIVE", + "currently_transmitting": "Trasmissione in corso...", + "duration": "Durata", + "call_actions": "Azioni intervento", + "unit_actions": "Azioni unità", + "personnel_actions": { + "title": "Azioni personale", + "status_tab": "Stato", + "staffing_tab": "Organico", + "select_status": "Seleziona stato", + "select_staffing": "Seleziona livello organico", + "destination": "Destinazione", + "no_destination": "Nessuna destinazione", + "note": "Nota", + "note_placeholder": "Aggiungi una nota facoltativa...", + "update_status": "Aggiorna stato", + "update_staffing": "Aggiorna organico", + "no_statuses_available": "Nessuno stato disponibile", + "no_staffings_available": "Nessun livello di organico disponibile" + }, + "unit_actions_panel": { + "status": "Stato", + "select_status": "Seleziona stato", + "destination": "Destinazione", + "no_destination": "Nessuna destinazione", + "note": "Nota", + "note_placeholder": "Aggiungi una nota facoltativa...", + "update_status": "Aggiorna stato", + "no_statuses_available": "Nessuno stato disponibile", + "no_active_calls": "Nessun intervento attivo", + "no_stations_available": "Nessuna stazione disponibile", + "no_destinations_available": "Nessuna destinazione disponibile" + }, + "call": "Intervento", + "station": "Stazione", + "calls": "Interventi", + "stations": "Stazioni", + "no_stations_available": "Nessuna stazione disponibile", + "new_call": "Nuovo intervento", + "view_details": "Dettagli", + "add_note": "Aggiungi nota", + "close_call": "Chiudi", + "set_status": "Imposta stato", + "set_staffing": "Organico", + "dispatch": "Invia", + "select_items_for_actions": "Seleziona un intervento, un'unità o personale per abilitare le azioni contestuali", + "weather": { + "clear": "Sereno", + "mainly_clear": "Prevalentemente sereno", + "partly_cloudy": "Parzialmente nuvoloso", + "overcast": "Coperto", + "fog": "Nebbia", + "drizzle": "Pioggerella", + "freezing_drizzle": "Pioggerella gelata", + "rain": "Pioggia", + "freezing_rain": "Pioggia gelata", + "snow": "Neve", + "rain_showers": "Rovesci di pioggia", + "snow_showers": "Rovesci di neve", + "thunderstorm": "Temporale", + "thunderstorm_hail": "Temporale con grandine", + "unknown": "Sconosciuto" + } + }, + "tabs": { + "calls": "Interventi", + "calendar": "Calendario", + "contacts": "Contatti", + "home": "Home", + "map": "Mappa", + "messages": "Messaggi", + "notes": "Note", + "protocols": "Protocolli", + "settings": "Impostazioni", + "shifts": "Turni", + "personnel": "Personale" + }, + "check_in": { + "tab_title": "Check-in", + "timer_status": "Stato timer", + "perform_check_in": "Check-in", + "check_in_success": "Check-in registrato con successo", + "check_in_error": "Impossibile registrare il check-in", + "checked_in_by": "da {{name}}", + "last_check_in": "Ultimo check-in", + "elapsed": "Trascorso", + "duration": "Durata", + "status_ok": "OK", + "status_green": "OK", + "status_warning": "Attenzione", + "status_yellow": "Attenzione", + "status_overdue": "Scaduto", + "status_red": "Scaduto", + "status_critical": "Critico", + "history": "Cronologia check-in", + "no_timers": "Nessun timer di check-in configurato", + "timers_disabled": "I timer di check-in sono disabilitati per questo intervento", + "type_personnel": "Personale", + "type_unit": "Unità", + "type_ic": "Comandante dell'incidente", + "type_par": "PAR", + "type_hazmat": "Esposizione Hazmat", + "type_sector_rotation": "Rotazione settore", + "type_rehab": "Riabilitazione", + "add_note": "Aggiungi nota (facoltativo)", + "confirm": "Conferma check-in", + "minutes_ago": "{{count}} min fa", + "select_target": "Seleziona entità per il check-in", + "overdue_count": "{{count}} scaduti", + "warning_count": "{{count}} in attenzione", + "enable_timers": "Abilita timer", + "disable_timers": "Disabilita timer", + "summary": "{{overdue}} scaduti, {{warning}} in attenzione, {{ok}} ok" + }, + "units": { + "title": "Unità" + }, + "weatherAlerts": { + "title": "Allerte meteo", + "activeAlerts": "Allerte attive", + "noActiveAlerts": "Nessuna allerta meteo attiva", + "moreAlerts": "+{{count}} altre", + "severity": { + "extreme": "Estrema", + "severe": "Grave", + "moderate": "Moderata", + "minor": "Lieve", + "unknown": "Sconosciuta" + }, + "category": { + "met": "Meteorologica", + "fire": "Incendio", + "health": "Sanitaria", + "env": "Ambientale", + "other": "Altra" + }, + "urgency": { + "immediate": "Immediata", + "expected": "Prevista", + "future": "Futura", + "past": "Passata", + "unknown": "Sconosciuta" + }, + "certainty": { + "observed": "Osservata", + "likely": "Probabile", + "possible": "Possibile", + "unlikely": "Improbabile", + "unknown": "Sconosciuta" + }, + "status": { + "active": "Attiva", + "updated": "Aggiornata", + "expired": "Scaduta", + "cancelled": "Annullata" + }, + "detail": { + "headline": "Titolo", + "description": "Descrizione", + "instruction": "Istruzioni", + "area": "Area interessata", + "effective": "In vigore", + "onset": "Inizio", + "expires": "Scadenza", + "sent": "Inviata", + "sender": "Fonte", + "urgency": "Urgenza", + "certainty": "Certezza" + }, + "filter": { + "all": "Tutte", + "nearby": "Nelle vicinanze" + }, + "sort": { + "severity": "Gravità", + "expires": "In scadenza", + "newest": "Più recenti" + }, + "banner": { + "viewAll": "Visualizza tutte" + } + }, + "videoFeeds": { + "title": "Feed video", + "noFeeds": "Nessun feed video per questo intervento", + "addFeed": "Aggiungi feed video", + "editFeed": "Modifica feed video", + "deleteFeed": "Elimina feed video", + "deleteConfirm": "Sei sicuro di voler rimuovere questo feed video?", + "watch": "Guarda", + "goLive": "Vai in diretta", + "stopLive": "Interrompi diretta", + "flipCamera": "Inverti fotocamera", + "feedAdded": "Feed video aggiunto", + "feedUpdated": "Feed video aggiornato", + "feedDeleted": "Feed video rimosso", + "feedError": "Impossibile caricare il feed video", + "unsupportedFormat": "Questo formato di streaming non è supportato su dispositivi mobili", + "copyUrl": "Copia URL", + "form": { + "name": "Nome feed", + "namePlaceholder": "es. Drone Autopompa 1", + "url": "URL dello stream", + "urlPlaceholder": "es. https://stream.example.com/live.m3u8", + "feedType": "Tipo di telecamera", + "feedFormat": "Formato stream", + "description": "Descrizione", + "descriptionPlaceholder": "Descrizione facoltativa", + "status": "Stato", + "sortOrder": "Ordine", + "cameraLocation": "Posizione telecamera", + "useCurrentLocation": "Usa posizione attuale" + }, + "type": { + "drone": "Drone", + "fixedCamera": "Telecamera fissa", + "bodyCam": "Body cam", + "trafficCam": "Telecamera traffico", + "weatherCam": "Telecamera meteo", + "satelliteFeed": "Feed satellitare", + "webCam": "Webcam", + "other": "Altro" + }, + "format": { + "rtsp": "RTSP", + "hls": "HLS", + "mjpeg": "MJPEG", + "youtubeLive": "YouTube Live", + "webrtc": "WebRTC", + "dash": "DASH", + "embed": "Incorporato", + "other": "Altro" + }, + "status": { + "active": "Attivo", + "inactive": "Inattivo", + "error": "Errore" + } + }, + "welcome": "Benvenuto nel sito dell'app obytes" +} diff --git a/src/translations/pl.json b/src/translations/pl.json new file mode 100644 index 0000000..9bbb2ee --- /dev/null +++ b/src/translations/pl.json @@ -0,0 +1,1151 @@ +{ + "app": { + "title": "Resgrid Dispatch" + }, + "audio_streams": { + "buffering": "Buforowanie", + "buffering_stream": "Buforowanie strumienia audio...", + "close": "Zamknij", + "currently_playing": "Aktualnie odtwarzane: {{streamName}}", + "loading": "Ładowanie", + "loading_stream": "Ładowanie strumienia audio...", + "loading_streams": "Ładowanie strumieni audio...", + "name": "Nazwa", + "no_stream_playing": "Żaden strumień audio nie jest aktualnie odtwarzany", + "none": "Brak", + "playing": "Odtwarzanie", + "refresh_streams": "Odśwież strumienie", + "select_placeholder": "Wybierz strumień audio", + "select_stream": "Wybierz strumień audio", + "status": "Status", + "stopped": "Zatrzymano", + "stream_info": "Informacje o strumieniu", + "stream_selected": "Wybrano: {{streamName}}", + "title": "Strumienie audio", + "type": "Typ" + }, + "bluetooth": { + "applied": "Zastosowano", + "audio": "Audio", + "audioActive": "Audio aktywne", + "audio_device": "Słuchawka BT", + "availableDevices": "Dostępne urządzenia", + "available_devices": "Dostępne urządzenia", + "bluetooth_not_ready": "Bluetooth jest w stanie {{state}}. Proszę włączyć Bluetooth.", + "buttonControlAvailable": "Dostępne sterowanie przyciskami", + "checking": "Sprawdzanie stanu Bluetooth...", + "clear": "Wyczyść", + "connect": "Połącz", + "connected": "Połączono", + "connectionError": "Błąd połączenia", + "current_selection": "Bieżący wybór", + "disconnect": "Rozłącz", + "doublePress": "Podwójne ", + "liveKitActive": "LiveKit aktywny", + "longPress": "Długie ", + "micControl": "Sterowanie mikrofonem", + "mute": "Wycisz", + "noDevicesFound": "Nie znaleziono urządzeń audio", + "noDevicesFoundRetry": "Nie znaleziono urządzeń audio. Spróbuj ponownie wyszukać.", + "no_device_selected": "Nie wybrano urządzenia", + "no_devices_found": "Nie znaleziono urządzeń audio Bluetooth", + "not_connected": "Nie połączono", + "poweredOff": "Bluetooth jest wyłączony. Proszę włączyć Bluetooth, aby połączyć urządzenia audio.", + "pttStart": "PTT Start", + "pttStop": "PTT Stop", + "recentButtonEvents": "Ostatnie zdarzenia przycisków", + "scan": "Skanuj", + "scanAgain": "Skanuj ponownie", + "scan_again": "Skanuj ponownie", + "scan_error_message": "Nie można wyszukać urządzeń Bluetooth", + "scan_error_title": "Błąd skanowania", + "scanning": "Skanowanie...", + "select_device": "Wybierz urządzenie Bluetooth", + "selected": "Wybrano", + "selection_error_message": "Nie można zapisać preferowanego urządzenia", + "selection_error_title": "Błąd wyboru", + "startScanning": "Rozpocznij skanowanie", + "stopScan": "Zatrzymaj skanowanie", + "supports_mic_control": "Sterowanie mikrofonem", + "tap_scan_to_find_devices": "Naciśnij 'Skanuj', aby wyszukać urządzenia audio Bluetooth", + "title": "Audio Bluetooth", + "unauthorized": "Odmowa uprawnień Bluetooth. Proszę przyznać uprawnienia Bluetooth w Ustawieniach.", + "unknown": "Nieznane", + "unknownDevice": "Nieznane urządzenie", + "unknown_device": "Nieznane urządzenie", + "unmute": "Wyłącz wyciszenie", + "volumeDown": "Głośność -", + "volumeUp": "Głośność +" + }, + "callImages": { + "add": "Dodaj zdjęcie", + "add_new": "Dodaj nowe zdjęcie", + "default_name": "Zdjęcie bez tytułu", + "error": "Błąd pobierania zdjęć", + "failed_to_load": "Nie udało się załadować zdjęcia", + "image_alt": "Zdjęcie zgłoszenia", + "image_name": "Nazwa zdjęcia", + "image_note": "Notatka do zdjęcia", + "loading": "Ładowanie...", + "no_images": "Brak dostępnych zdjęć", + "no_images_description": "Dodaj zdjęcia do zgłoszenia, aby ułatwić dokumentację i komunikację", + "select_from_gallery": "Wybierz z galerii", + "take_photo": "Zrób zdjęcie", + "title": "Zdjęcia zgłoszenia", + "upload": "Prześlij" + }, + "callNotes": { + "addNote": "Dodaj notatkę", + "addNotePlaceholder": "Dodaj nową notatkę...", + "noNotes": "Brak notatek dla tego zgłoszenia", + "noSearchResults": "Żadne notatki nie pasują do wyszukiwania", + "searchPlaceholder": "Szukaj notatek...", + "title": "Notatki zgłoszenia" + }, + "call_detail": { + "address": "Adres", + "call_location": "Lokalizacja zgłoszenia", + "close_call": "Zamknij zgłoszenie", + "close_call_confirmation": "Czy na pewno chcesz zamknąć to zgłoszenie?", + "close_call_error": "Nie udało się zamknąć zgłoszenia", + "close_call_note": "Notatka zamknięcia", + "close_call_note_placeholder": "Wprowadź notatkę dotyczącą zamknięcia zgłoszenia", + "close_call_success": "Zgłoszenie zamknięte pomyślnie", + "close_call_type": "Typ zamknięcia", + "close_call_type_placeholder": "Wybierz typ zamknięcia", + "close_call_type_required": "Proszę wybrać typ zamknięcia", + "close_call_types": { + "cancelled": "Anulowane", + "closed": "Zamknięte", + "false_alarm": "Fałszywy alarm", + "founded": "Potwierdzone", + "minor": "Drobne", + "transferred": "Przekazane", + "unfounded": "Niepotwierdzone" + }, + "contact_email": "E-mail", + "contact_info": "Dane kontaktowe", + "contact_name": "Nazwa kontaktu", + "contact_phone": "Telefon", + "edit_call": "Edytuj zgłoszenie", + "external_id": "Identyfikator zewnętrzny", + "failed_to_open_maps": "Nie udało się otworzyć aplikacji mapowej", + "files": { + "add_file": "Dodaj plik", + "button": "Pliki", + "empty": "Brak dostępnych plików", + "empty_description": "Dodaj pliki do zgłoszenia, aby ułatwić dokumentację i komunikację", + "error": "Błąd pobierania plików", + "file_name": "Nazwa pliku", + "name_required": "Proszę podać nazwę pliku", + "no_files": "Brak dostępnych plików", + "no_files_description": "Dodaj pliki do zgłoszenia, aby ułatwić dokumentację i komunikację", + "open_error": "Błąd otwierania pliku", + "select_error": "Błąd wyboru pliku", + "select_file": "Wybierz plik", + "share_error": "Błąd udostępniania pliku", + "title": "Pliki zgłoszenia", + "upload": "Prześlij", + "upload_error": "Błąd przesyłania pliku", + "uploading": "Przesyłanie..." + }, + "group": "Grupa", + "images": "Zdjęcia", + "loading": "Ładowanie szczegółów zgłoszenia...", + "nature": "Charakter", + "no_additional_info": "Brak dodatkowych informacji", + "no_contact_info": "Brak danych kontaktowych", + "no_dispatched": "Brak jednostek zadysponowanych do tego zgłoszenia", + "no_location": "Brak danych lokalizacyjnych", + "no_location_for_routing": "Brak danych lokalizacyjnych do wyznaczenia trasy", + "no_protocols": "Brak protokołów dołączonych do tego zgłoszenia", + "no_timeline": "Brak zdarzeń na osi czasu", + "not_available": "Nd.", + "not_found": "Nie znaleziono zgłoszenia", + "missing_call_id": "Brak identyfikatora zgłoszenia", + "note": "Notatka", + "notes": "Notatki", + "priority": "Priorytet", + "reference_id": "Numer referencyjny", + "set_active": "Ustaw jako aktywne", + "set_active_error": "Nie udało się ustawić zgłoszenia jako aktywne", + "set_active_success": "Zgłoszenie ustawione jako aktywne", + "setting_active": "Ustawianie jako aktywne...", + "status": "Status", + "tabs": { + "contact": "Kontakt", + "dispatched": "Zadysponowane", + "info": "Info", + "protocols": "Protokoły", + "timeline": "Aktywność", + "video": "Wideo" + }, + "timestamp": "Znacznik czasu", + "title": "Szczegóły zgłoszenia", + "type": "Typ", + "unit": "Jednostka", + "update_call_error": "Nie udało się zaktualizować zgłoszenia", + "update_call_success": "Zgłoszenie zaktualizowane pomyślnie" + }, + "calls": { + "address": "Adres", + "address_found": "Znaleziono adres i zaktualizowano lokalizację", + "address_not_found": "Nie znaleziono adresu, spróbuj podać inny adres", + "address_placeholder": "Wprowadź adres zgłoszenia", + "address_required": "Proszę podać adres do wyszukania", + "call_details": "Szczegóły zgłoszenia", + "call_location": "Lokalizacja zgłoszenia", + "call_number": "Numer zgłoszenia", + "call_priority": "Priorytet zgłoszenia", + "confirm_deselect_message": "Czy na pewno chcesz odznaczyć bieżące aktywne zgłoszenie?", + "confirm_deselect_title": "Odznacz aktywne zgłoszenie", + "contact_info": "Dane kontaktowe", + "contact_info_placeholder": "Wprowadź dane kontaktowe", + "contact_name": "Nazwa kontaktu", + "contact_name_placeholder": "Wprowadź nazwę kontaktu", + "contact_phone": "Telefon kontaktowy", + "contact_phone_placeholder": "Wprowadź numer telefonu kontaktu", + "coordinates": "Współrzędne GPS", + "coordinates_found": "Znaleziono współrzędne i zaktualizowano adres", + "coordinates_geocoding_error": "Nie udało się uzyskać adresu dla współrzędnych, ale lokalizacja została ustawiona na mapie", + "coordinates_invalid_format": "Nieprawidłowy format współrzędnych. Użyj formatu: szerokość, długość", + "coordinates_no_address": "Współrzędne ustawione na mapie, ale nie znaleziono adresu", + "coordinates_out_of_range": "Współrzędne poza zakresem. Szerokość geograficzna musi wynosić od -90 do 90, długość od -180 do 180", + "coordinates_placeholder": "Wprowadź współrzędne GPS (np. 37.7749, -122.4194)", + "coordinates_required": "Proszę podać współrzędne do wyszukania", + "create": "Utwórz", + "create_error": "Błąd tworzenia zgłoszenia", + "create_new_call": "Utwórz nowe zgłoszenie", + "create_success": "Zgłoszenie utworzone pomyślnie", + "description": "Opis", + "description_placeholder": "Wprowadź opis zgłoszenia", + "deselect": "Odznacz", + "directions": "Nawigacja", + "dispatch_to": "Zadysponuj do", + "dispatch_to_everyone": "Zadysponuj do całego dostępnego personelu", + "edit_call": "Edytuj zgłoszenie", + "edit_call_description": "Zaktualizuj informacje o zgłoszeniu", + "everyone": "Wszyscy", + "files": { + "no_files": "Brak dostępnych plików", + "no_files_description": "Do tego zgłoszenia nie dodano jeszcze żadnych plików", + "title": "Pliki zgłoszenia" + }, + "geocoding_error": "Nie udało się wyszukać adresu, spróbuj ponownie", + "groups": "Grupy", + "invalid_priority": "Wybrano nieprawidłowy priorytet. Proszę wybrać prawidłowy priorytet.", + "invalid_type": "Wybrano nieprawidłowy typ. Proszę wybrać prawidłowy typ zgłoszenia.", + "loading": "Ładowanie zgłoszeń...", + "loading_calls": "Ładowanie zgłoszeń...", + "name": "Nazwa", + "name_placeholder": "Wprowadź nazwę zgłoszenia", + "nature": "Charakter", + "nature_placeholder": "Wprowadź charakter zgłoszenia", + "new_call": "Nowe zgłoszenie", + "new_call_description": "Utwórz nowe zgłoszenie, aby rozpocząć nowe zdarzenie", + "no_call_selected": "Brak aktywnego zgłoszenia", + "no_call_selected_info": "Ta jednostka nie reaguje obecnie na żadne zgłoszenie", + "no_calls": "Brak aktywnych zgłoszeń", + "no_calls_available": "Brak dostępnych zgłoszeń", + "no_calls_description": "Nie znaleziono aktywnych zgłoszeń. Wybierz aktywne zgłoszenie, aby wyświetlić szczegóły.", + "no_location_message": "To zgłoszenie nie posiada danych lokalizacyjnych do nawigacji.", + "no_location_title": "Brak lokalizacji", + "no_open_calls": "Brak otwartych zgłoszeń", + "note": "Notatka", + "note_placeholder": "Wprowadź notatkę do zgłoszenia", + "plus_code": "Plus Code", + "plus_code_found": "Znaleziono Plus Code i zaktualizowano lokalizację", + "plus_code_geocoding_error": "Nie udało się wyszukać Plus Code, spróbuj ponownie", + "plus_code_not_found": "Nie znaleziono Plus Code, spróbuj podać inny Plus Code", + "plus_code_placeholder": "Wprowadź Plus Code (np. 849VCWC8+R9)", + "plus_code_required": "Proszę podać Plus Code do wyszukania", + "priority": "Priorytet", + "priority_placeholder": "Wybierz priorytet zgłoszenia", + "roles": "Role", + "search": "Szukaj zgłoszeń...", + "select_active_call": "Wybierz aktywne zgłoszenie", + "select_address": "Wybierz adres", + "select_address_placeholder": "Wybierz adres zgłoszenia", + "select_description": "Wybierz opis", + "select_dispatch_recipients": "Wybierz odbiorców dyspozycji", + "select_location": "Wybierz lokalizację na mapie", + "select_name": "Wybierz nazwę", + "select_nature": "Wybierz charakter", + "select_nature_placeholder": "Wybierz charakter zgłoszenia", + "select_priority": "Wybierz priorytet", + "select_priority_placeholder": "Wybierz priorytet zgłoszenia", + "select_recipients": "Wybierz odbiorców", + "select_type": "Wybierz typ", + "selected": "wybrano", + "title": "Zgłoszenia", + "type": "Typ", + "units": "Jednostki", + "users": "Użytkownicy", + "viewNotes": "Notatki", + "view_details": "Zobacz szczegóły", + "what3words": "what3words", + "what3words_found": "Znaleziono adres what3words i zaktualizowano lokalizację", + "what3words_geocoding_error": "Nie udało się wyszukać adresu what3words, spróbuj ponownie", + "what3words_invalid_format": "Nieprawidłowy format what3words. Użyj formatu: słowo.słowo.słowo", + "what3words_not_found": "Nie znaleziono adresu what3words, spróbuj podać inny adres", + "what3words_placeholder": "Wprowadź adres what3words (np. filled.count.soap)", + "what3words_required": "Proszę podać adres what3words do wyszukania", + "expand_map": "Powiększ mapę", + "schedule_dispatch": "Zaplanuj dyspozycję", + "schedule_dispatch_description": "Ustaw przyszłą datę i godzinę, aby zaplanować to zgłoszenie do późniejszej dyspozycji", + "scheduled_on": "Zaplanowana data i godzina", + "scheduled_on_helper": "Pozostaw puste, aby zadysponować natychmiast", + "scheduled_on_past_error": "Zaplanowany czas musi być w przyszłości", + "contact_information": "Dane kontaktowe", + "new_call_web_hint": "Wypełnij szczegóły zgłoszenia poniżej. Naciśnij Ctrl+Enter, aby utworzyć.", + "edit_call_web_hint": "Zaktualizuj szczegóły zgłoszenia poniżej. Naciśnij Ctrl+S, aby zapisać.", + "keyboard_shortcuts": "Wskazówka: Naciśnij Ctrl+Enter, aby utworzyć, Escape, aby anulować", + "edit_keyboard_shortcuts": "Wskazówka: Naciśnij Ctrl+S, aby zapisać, Escape, aby anulować", + "templates": { + "title": "Szablony zgłoszeń", + "select_template": "Wybierz szablon", + "search_placeholder": "Szukaj szablonów...", + "none": "Brak dostępnych szablonów", + "template_applied": "Szablon zastosowany" + }, + "form": { + "title": "Formularz zgłoszenia", + "no_form": "Nie skonfigurowano formularza zgłoszenia" + }, + "linked_calls": { + "title": "Powiązane zgłoszenie", + "select": "Powiąż z istniejącym zgłoszeniem", + "change": "Zmień powiązane zgłoszenie", + "none": "Brak dostępnych aktywnych zgłoszeń", + "search_placeholder": "Szukaj zgłoszeń...", + "linked": "Powiązane" + }, + "contact_picker": { + "title": "Wybierz kontakt", + "search_placeholder": "Szukaj kontaktów...", + "none": "Brak dostępnych kontaktów" + }, + "protocols": { + "title": "Protokoły", + "select": "Wybierz protokoły", + "none": "Brak dostępnych protokołów", + "selected_count": "wybrano", + "expand_questions": "Zobacz pytania", + "collapse": "Zwiń" + } + }, + "common": { + "add": "Dodaj", + "back": "Wstecz", + "cancel": "Anuluj", + "creating": "Tworzenie...", + "saving": "Zapisywanie...", + "unsaved_changes": "Niezapisane zmiany", + "close": "Zamknij", + "confirm": "Potwierdź", + "confirm_location": "Potwierdź lokalizację", + "delete": "Usuń", + "dismiss": "Zamknij", + "done": "Gotowe", + "edit": "Edytuj", + "error": "Błąd", + "errorOccurred": "Wystąpił błąd", + "get_my_location": "Pobierz moją lokalizację", + "go_back": "Powrót", + "loading": "Ładowanie...", + "loading_address": "Ładowanie adresu...", + "next": "Dalej", + "noActiveUnit": "Brak ustawionej aktywnej jednostki", + "noActiveUnitDescription": "Proszę ustawić aktywną jednostkę na stronie ustawień, aby uzyskać dostęp do kontroli statusu", + "noDataAvailable": "Brak dostępnych danych", + "no_address_found": "Nie znaleziono adresu", + "no_location": "Brak danych lokalizacyjnych", + "no_results_found": "Nie znaleziono wyników", + "no_unit_selected": "Nie wybrano jednostki", + "nothingToDisplay": "Brak danych do wyświetlenia", + "of": "z", + "ok": "Ok", + "optional": "opcjonalne", + "permission_denied": "Odmowa uprawnień", + "previous": "Poprzedni", + "remove": "Usuń", + "retry": "Ponów", + "route": "Trasa", + "save": "Zapisz", + "search": "Szukaj...", + "set_location": "Ustaw lokalizację", + "share": "Udostępnij", + "step": "Krok", + "submit": "Wyślij", + "submitting": "Wysyłanie...", + "tryAgainLater": "Proszę spróbować ponownie później", + "unknown": "Nieznane", + "unknown_department": "Nieznany wydział", + "unknown_user": "Nieznany użytkownik", + "upload": "Prześlij", + "uploading": "Przesyłanie..." + }, + "contacts": { + "add": "Dodaj kontakt", + "addedBy": "Dodany przez", + "addedOn": "Dodany dnia", + "additionalInformation": "Dodatkowe informacje", + "address": "Adres", + "bluesky": "Bluesky", + "cancel": "Anuluj", + "cellPhone": "Telefon komórkowy", + "city": "Miasto", + "cityState": "Miasto i województwo", + "cityStateZip": "Miasto, województwo, kod pocztowy", + "company": "Firma", + "contactInformation": "Dane kontaktowe", + "contactNotes": "Notatki kontaktu", + "contactNotesEmpty": "Nie znaleziono notatek dla tego kontaktu", + "contactNotesEmptyDescription": "Notatki dodane do tego kontaktu pojawią się tutaj", + "contactNotesExpired": "Ta notatka wygasła", + "contactNotesLoading": "Ładowanie notatek kontaktu...", + "contactType": "Typ kontaktu", + "countryId": "Identyfikator kraju", + "delete": "Usuń", + "deleteConfirm": "Czy na pewno chcesz usunąć ten kontakt?", + "deleteSuccess": "Kontakt usunięty pomyślnie", + "description": "Dodawaj i zarządzaj swoimi kontaktami", + "details": "Szczegóły kontaktu", + "detailsTab": "Szczegóły", + "edit": "Edytuj kontakt", + "editedBy": "Edytowany przez", + "editedOn": "Edytowany dnia", + "email": "E-mail", + "empty": "Nie znaleziono kontaktów", + "emptyDescription": "Dodaj kontakty, aby zarządzać swoimi połączeniami osobistymi i służbowymi", + "entranceCoordinates": "Współrzędne wejścia", + "exitCoordinates": "Współrzędne wyjścia", + "expires": "Wygasa", + "facebook": "Facebook", + "faxPhone": "Faks", + "formError": "Proszę poprawić błędy w formularzu", + "homePhone": "Telefon domowy", + "identification": "Identyfikacja", + "important": "Oznacz jako ważne", + "instagram": "Instagram", + "internal": "Wewnętrzny", + "invalidEmail": "Nieprawidłowy adres e-mail", + "linkedin": "LinkedIn", + "locationCoordinates": "Współrzędne lokalizacji", + "locationInformation": "Informacje o lokalizacji", + "mastodon": "Mastodon", + "mobile": "Komórkowy", + "name": "Nazwa", + "noteAlert": "Alert", + "noteType": "Typ", + "notes": "Notatki", + "notesTab": "Notatki", + "officePhone": "Telefon biurowy", + "otherInfo": "Inne informacje", + "person": "Osoba", + "phone": "Telefon", + "public": "Publiczny", + "required": "Wymagane", + "save": "Zapisz kontakt", + "saveSuccess": "Kontakt zapisany pomyślnie", + "search": "Szukaj kontaktów...", + "shouldAlert": "Powiadamiać", + "socialMediaWeb": "Media społecznościowe i strony", + "state": "Województwo", + "stateId": "Identyfikator województwa", + "systemInformation": "Informacje systemowe", + "tabs": { + "details": "Szczegóły", + "notes": "Notatki" + }, + "threads": "Threads", + "title": "Kontakty", + "twitter": "Twitter", + "visibility": "Widoczność", + "website": "Strona internetowa", + "zip": "Kod pocztowy" + }, + "form": { + "invalid_url": "Proszę podać prawidłowy adres URL zaczynający się od http:// lub https://", + "required": "To pole jest wymagane" + }, + "livekit": { + "audio_devices": "Urządzenia audio", + "audio_settings": "Ustawienia audio", + "connected_to_room": "Połączono z kanałem", + "connecting": "Łączenie...", + "disconnect": "Rozłącz", + "join": "Dołącz", + "microphone": "Mikrofon", + "mute": "Wycisz", + "no_rooms_available": "Brak dostępnych kanałów głosowych", + "speaker": "Głośnik", + "speaking": "Mówi", + "title": "Kanały głosowe", + "unmute": "Wyłącz wyciszenie" + }, + "loading": { + "loading": "Ładowanie...", + "loadingData": "Ładowanie danych...", + "pleaseWait": "Proszę czekać", + "processingRequest": "Przetwarzanie żądania..." + }, + "sso": { + "authenticating": "Uwierzytelnianie...", + "back_to_login": "Powrót do logowania", + "back_to_lookup": "Zmień użytkownika", + "continue_button": "Kontynuuj", + "department_id_label": "ID wydziału", + "department_id_placeholder": "Wprowadź ID wydziału", + "error_generic": "Logowanie nie powiodło się. Proszę spróbować ponownie.", + "error_oidc_cancelled": "Logowanie zostało anulowane.", + "error_oidc_not_ready": "Dostawca SSO się ładuje, proszę czekać.", + "error_sso_not_enabled": "Logowanie jednokrotne nie jest włączone dla tego użytkownika.", + "error_token_exchange": "Nie udało się zakończyć logowania. Proszę spróbować ponownie.", + "error_user_not_found": "Nie znaleziono użytkownika. Proszę sprawdzić i spróbować ponownie.", + "looking_up": "Wyszukiwanie...", + "optional": "opcjonalne", + "page_subtitle": "Wprowadź swoją nazwę użytkownika, aby wyszukać opcje logowania Twojej organizacji.", + "page_title": "Logowanie jednokrotne", + "provider_oidc": "OpenID Connect (OIDC)", + "provider_saml": "SAML 2.0", + "sign_in_button": "Zaloguj się przez SSO", + "sign_in_title": "Logowanie", + "sso_button": "Logowanie SSO" + }, + "login": { + "branding_subtitle": "Zaawansowane oprogramowanie dyspozytorskie dla służb ratowniczych, poszukiwawczo-ratowniczych i organizacji bezpieczeństwa publicznego.", + "branding_title": "Zarządzanie reagowaniem kryzysowym", + "errorModal": { + "confirmButton": "OK", + "message": "Proszę sprawdzić nazwę użytkownika i hasło i spróbować ponownie.", + "title": "Logowanie nie powiodło się" + }, + "feature_dispatch_desc": "Natychmiastowe dysponowanie jednostek i zarządzanie zgłoszeniami z aktualizacjami na żywo na wszystkich urządzeniach.", + "feature_dispatch_title": "Dyspozycja w czasie rzeczywistym", + "feature_mapping_desc": "Śledzenie jednostek w czasie rzeczywistym ze szczegółowymi mapami, trasami i zarządzaniem lokalizacją.", + "feature_mapping_title": "Zaawansowane mapowanie", + "feature_personnel_desc": "Zarządzanie zespołem z kontrolą dostępu opartą na rolach, śledzeniem statusu i narzędziami komunikacyjnymi.", + "feature_personnel_title": "Zarządzanie personelem", + "footer_text": "Stworzone z ❤️ w Lake Tahoe", + "login": "Zaloguj się", + "login_button": "Zaloguj się", + "login_button_description": "Zaloguj się na swoje konto, aby kontynuować", + "login_button_error": "Błąd logowania", + "login_button_loading": "Logowanie...", + "login_button_success": "Zalogowano pomyślnie", + "no_account": "Nie masz konta?", + "page_subtitle": "Wprowadź swoje dane uwierzytelniające, aby się zalogować.", + "page_title": "Resgrid Dispatch", + "password": "Hasło", + "password_incorrect": "Hasło jest nieprawidłowe", + "password_placeholder": "Wprowadź swoje hasło", + "register": "Zarejestruj się", + "title": "Logowanie", + "username": "Nazwa użytkownika", + "username_placeholder": "Wprowadź swoją nazwę użytkownika", + "welcome_title": "Witaj ponownie" + }, + "lockscreen": { + "message": "Wprowadź hasło, aby odblokować ekran", + "not_you": "To nie Ty? Wróć do logowania", + "password": "Hasło", + "password_placeholder": "Wprowadź swoje hasło", + "title": "Ekran blokady", + "unlock_button": "Odblokuj", + "unlock_failed": "Nie udało się odblokować. Proszę spróbować ponownie.", + "unlocking": "Odblokowywanie...", + "welcome_back": "Witaj ponownie" + }, + "maintenance": { + "downtime_message": "Pracujemy nad jak najszybszym zakończeniem konserwacji. Proszę sprawdzić ponownie wkrótce.", + "downtime_title": "Jaki jest czas przestoju?", + "message": "Proszę sprawdzić ponownie za jakiś czas.", + "support_message": "Jeśli potrzebujesz pomocy, skontaktuj się z nami pod adresem", + "support_title": "Potrzebujesz wsparcia?", + "title": "Strona jest w trakcie konserwacji", + "why_down_message": "Przeprowadzamy zaplanowaną konserwację w celu poprawy jakości usług. Przepraszamy za wszelkie niedogodności.", + "why_down_title": "Dlaczego strona jest niedostępna?" + }, + "menu": { + "calls": "Zgłoszenia", + "calls_list": "Lista zgłoszeń", + "contacts": "Kontakty", + "home": "Strona główna", + "map": "Mapa", + "menu": "Menu", + "messages": "Wiadomości", + "new_call": "Nowe zgłoszenie", + "personnel": "Personel", + "protocols": "Protokoły", + "settings": "Ustawienia", + "units": "Jednostki", + "weatherAlerts": "Alerty pogodowe" + }, + "map": { + "call_set_as_current": "Zgłoszenie ustawione jako bieżące", + "failed_to_open_maps": "Nie udało się otworzyć aplikacji mapowej", + "failed_to_set_current_call": "Nie udało się ustawić zgłoszenia jako bieżące", + "layers": "Warstwy mapy", + "no_layers": "Brak dostępnych warstw", + "no_location_for_routing": "Brak danych lokalizacyjnych do wyznaczenia trasy", + "pin_color": "Kolor pinezki", + "recenter_map": "Wycentruj mapę", + "set_as_current_call": "Ustaw jako bieżące zgłoszenie", + "show_all": "Pokaż wszystko", + "hide_all": "Ukryj wszystko", + "view_call_details": "Zobacz szczegóły zgłoszenia" + }, + "notes": { + "actions": { + "add": "Dodaj notatkę", + "delete_confirm": "Czy na pewno chcesz usunąć tę notatkę?" + }, + "details": { + "close": "Zamknij", + "created": "Utworzono", + "delete": "Usuń", + "edit": "Edytuj", + "tags": "Tagi", + "title": "Szczegóły notatki", + "updated": "Zaktualizowano" + }, + "empty": "Nie znaleziono notatek", + "emptyDescription": "Dla Twojego wydziału nie utworzono jeszcze żadnych notatek.", + "search": "Szukaj notatek...", + "title": "Notatki" + }, + "personnel": { + "title": "Personel", + "search": "Szukaj personelu...", + "loading": "Ładowanie personelu...", + "empty": "Nie znaleziono personelu", + "empty_description": "Obecnie w Twoim dziale nie ma dostępnego personelu.", + "no_results": "Żaden personel nie pasuje do wyszukiwania", + "no_results_description": "Spróbuj zmienić kryteria wyszukiwania.", + "status": "Status", + "staffing": "Obsada", + "group": "Grupa", + "roles": "Role", + "email": "E-mail", + "phone": "Telefon", + "id_number": "Numer ID", + "responding_to": "Odpowiada na", + "status_updated": "Status zaktualizowany", + "staffing_updated": "Obsada zaktualizowana", + "details": "Szczegóły personelu", + "contact_info": "Dane kontaktowe", + "status_info": "Status i obsada", + "no_email": "Brak dostępnego e-maila", + "no_phone": "Brak dostępnego telefonu", + "no_group": "Nieprzypisany", + "no_roles": "Brak przypisanych ról", + "unknown_status": "Nieznany", + "on_duty": "Na służbie", + "off_duty": "Poza służbą", + "call_phone": "Zadzwoń", + "send_email": "E-mail", + "custom_fields": "Dodatkowe informacje" + }, + "onboarding": { + "screen1": { + "title": "Resgrid Dispatch", + "description": "Twórz, dysponuj i zarządzaj zgłoszeniami alarmowymi dzięki mobilnemu centrum dowodzenia na wyciągnięcie ręki" + }, + "screen2": { + "title": "Świadomość sytuacyjna w czasie rzeczywistym", + "description": "Śledź wszystkie jednostki, personel i zasoby na interaktywnej mapie z aktualizacjami statusów na żywo i AVL" + }, + "screen3": { + "title": "Płynna koordynacja", + "description": "Komunikuj się natychmiast z jednostkami w terenie, aktualizuj statusy zgłoszeń i koordynuj działania ratownicze z dowolnego miejsca" + }, + "skip": "Pomiń", + "next": "Dalej", + "getStarted": "Zaczynajmy" + }, + "protocols": { + "details": { + "close": "Zamknij", + "code": "Kod", + "created": "Utworzono", + "title": "Szczegóły protokołu", + "updated": "Zaktualizowano" + }, + "empty": "Nie znaleziono protokołów", + "emptyDescription": "Dla Twojego wydziału nie utworzono jeszcze żadnych protokołów.", + "search": "Szukaj protokołów...", + "title": "Protokoły" + }, + "push_notifications": { + "close": "Zamknij", + "message": "Wiadomość", + "new_notification": "Nowe powiadomienie", + "title": "Tytuł", + "types": { + "call": "Zgłoszenie", + "chat": "Czat", + "group_chat": "Czat grupowy", + "message": "Wiadomość", + "notification": "Powiadomienie" + }, + "unknown_type_warning": "Otrzymano powiadomienie nieznanego typu", + "view_call": "Zobacz zgłoszenie", + "view_message": "Zobacz wiadomość" + }, + "roles": { + "modal": { + "title": "Przypisanie ról jednostki" + }, + "selectUser": "Wybierz użytkownika", + "status": "{{active}} z {{total}} ról", + "tap_to_manage": "Dotknij, aby zarządzać rolami", + "unassigned": "Nieprzypisane" + }, + "settings": { + "about": "O aplikacji", + "account": "Konto", + "active_unit": "Aktywna jednostka", + "app_info": "Informacje o aplikacji", + "app_name": "Nazwa aplikacji", + "arabic": "Arabski", + "audio_device_selection": { + "bluetooth_device": "Urządzenie Bluetooth", + "current_selection": "Bieżący wybór", + "microphone": "Mikrofon", + "no_microphones_available": "Brak dostępnych mikrofonów", + "no_speakers_available": "Brak dostępnych głośników", + "none_selected": "Nie wybrano", + "speaker": "Głośnik", + "speaker_device": "Urządzenie głośnikowe", + "title": "Wybór urządzenia audio", + "unavailable": "Niedostępne", + "wired_device": "Urządzenie przewodowe" + }, + "background_geolocation": "Geolokalizacja w tle", + "background_geolocation_warning": "Ta funkcja pozwala aplikacji śledzić Twoją lokalizację w tle. Pomaga w koordynacji reagowania kryzysowego, ale może wpływać na żywotność baterii.", + "background_location": "Lokalizacja w tle", + "contact_us": "Skontaktuj się z nami", + "current_unit": "Bieżąca jednostka", + "english": "Angielski", + "french": "Francuski", + "german": "Niemiecki", + "italian": "Włoski", + "enter_password": "Wprowadź swoje hasło", + "enter_server_url": "Wprowadź adres URL API Resgrid (np. https://api.resgrid.com)", + "enter_username": "Wprowadź swoją nazwę użytkownika", + "environment": "Środowisko", + "general": "Ogólne", + "generale": "Ogólne", + "github": "Github", + "help_center": "Centrum pomocy", + "keep_alive": "Utrzymuj aktywność", + "keep_alive_warning": "Uwaga: Włączenie utrzymywania aktywności uniemożliwi urządzeniu przejście w tryb uśpienia i może znacząco zwiększyć zużycie baterii.", + "keep_screen_on": "Utrzymuj ekran włączony", + "language": "Język", + "links": "Linki", + "login_info": "Dane logowania", + "logout": "Wyloguj się", + "more": "Więcej", + "no_units_available": "Brak dostępnych jednostek", + "none_selected": "Nie wybrano", + "notifications": "Powiadomienia push", + "notifications_description": "Włącz powiadomienia, aby otrzymywać alerty i aktualizacje", + "notifications_enable": "Włącz powiadomienia", + "password": "Hasło", + "preferences": "Preferencje", + "privacy": "Polityka prywatności", + "privacy_policy": "Polityka prywatności", + "rate": "Oceń", + "select_unit": "Wybierz jednostkę", + "server": "Serwer", + "server_url": "Adres URL serwera", + "server_url_note": "Uwaga: To jest adres URL API Resgrid. Służy do połączenia z serwerem Resgrid. Nie dodawaj /api/v4 w adresie URL ani końcowego ukośnika.", + "set_active_unit": "Ustaw aktywną jednostkę", + "share": "Udostępnij", + "polish": "Polski", + "spanish": "Hiszpański", + "swedish": "Szwedzki", + "ukrainian": "Ukraiński", + "status_page": "Status systemu", + "support": "Wsparcie", + "support_us": "Wesprzyj nas", + "terms": "Regulamin", + "theme": { + "dark": "Ciemny", + "light": "Jasny", + "system": "Systemowy", + "title": "Motyw" + }, + "title": "Ustawienia", + "unit_selected_successfully": "Pomyślnie wybrano {{unitName}}", + "unit_selection": "Wybór jednostki", + "unit_selection_failed": "Nie udało się wybrać jednostki. Proszę spróbować ponownie.", + "username": "Nazwa użytkownika", + "version": "Wersja", + "website": "Strona internetowa" + }, + "status": { + "add_note": "Dodaj notatkę", + "both_destinations_enabled": "Może reagować na zgłoszenia lub stacje", + "call_destination_enabled": "Może reagować na zgłoszenia", + "calls_tab": "Zgłoszenia", + "failed_to_save_status": "Nie udało się zapisać statusu. Proszę spróbować ponownie.", + "general_status": "Status ogólny bez określonego celu", + "loading_stations": "Ładowanie stacji...", + "no_destination": "Brak celu", + "no_stations_available": "Brak dostępnych stacji", + "no_statuses_available": "Brak dostępnych statusów", + "note": "Notatka", + "note_optional": "Dodaj opcjonalną notatkę do tej aktualizacji statusu", + "note_required": "Proszę podać notatkę do tej aktualizacji statusu", + "select_destination": "Wybierz cel dla {{status}}", + "select_destination_type": "Dokąd chcesz reagować?", + "select_status": "Wybierz status", + "select_status_type": "Jaki status chcesz ustawić?", + "selected_destination": "Wybrany cel", + "selected_status": "Wybrany status", + "set_status": "Ustaw status", + "station_destination_enabled": "Może reagować na stacje", + "stations_tab": "Stacje", + "status_saved_successfully": "Status zapisany pomyślnie!" + }, + "dispatch": { + "active_calls": "Aktywne zgłoszenia", + "pending_calls": "Oczekujące", + "scheduled_calls": "Zaplanowane", + "units_available": "Dostępne", + "personnel_available": "Dostępni", + "personnel_on_duty": "Na służbie", + "units": "Jednostki", + "personnel": "Personel", + "map": "Mapa", + "notes": "Notatki", + "activity_log": "Dziennik aktywności", + "communications": "Komunikacja", + "no_active_calls": "Brak aktywnych zgłoszeń", + "no_units": "Brak dostępnych jednostek", + "no_personnel": "Brak dostępnego personelu", + "no_notes": "Brak dostępnych notatek", + "no_activity": "Brak ostatniej aktywności", + "current_channel": "Bieżący kanał", + "audio_stream": "Strumień audio", + "no_stream": "Brak aktywnego strumienia", + "ptt": "PTT", + "ptt_start": "PTT Start", + "ptt_end": "PTT Koniec", + "transmitting_on": "Transmisja na {{channel}}", + "transmission_ended": "Transmisja zakończona", + "voice_disabled": "Głos wyłączony", + "disconnected": "Rozłączono", + "select_channel": "Wybierz kanał", + "select_channel_description": "Wybierz kanał głosowy, aby się połączyć", + "change_channel_warning": "Wybranie nowego kanału spowoduje rozłączenie z bieżącym", + "default_channel": "Domyślny", + "no_channels_available": "Brak dostępnych kanałów głosowych", + "system_update": "Aktualizacja systemu", + "data_refreshed": "Dane odświeżone z serwera", + "call_selected": "Zgłoszenie wybrane", + "unit_selected": "Jednostka wybrana", + "unit_deselected": "Jednostka odznaczona", + "personnel_selected": "Personel wybrany", + "personnel_deselected": "Personel odznaczony", + "loading_map": "Ładowanie mapy...", + "map_not_available_web": "Mapa niedostępna na platformie webowej", + "filtering_by_call": "Filtrowanie wg zgłoszenia", + "clear_filter": "Wyczyść filtr", + "call_filter_active": "Filtr zgłoszenia aktywny", + "call_filter_cleared": "Filtr zgłoszenia wyczyszczony", + "showing_all_data": "Wyświetlanie wszystkich danych", + "call_notes": "Notatki zgłoszenia", + "no_call_notes": "Brak notatek zgłoszenia", + "add_call_note_placeholder": "Dodaj notatkę...", + "note_added": "Notatka dodana", + "note_added_to_console": "Nowa notatka została dodana do konsoli", + "add_note_title": "Dodaj nową notatkę", + "note_title_label": "Tytuł", + "note_title_placeholder": "Wprowadź tytuł notatki...", + "note_category_label": "Kategoria", + "note_category_placeholder": "Wybierz kategorię", + "note_no_category": "Bez kategorii", + "note_body_label": "Treść notatki", + "note_body_placeholder": "Wprowadź treść notatki...", + "note_save_error": "Nie udało się zapisać notatki: {{error}}", + "note_created": "Notatka utworzona", + "units_on_call": "Jednostki na zgłoszeniu", + "no_units_on_call": "Brak jednostek na zgłoszeniu", + "personnel_on_call": "Personel na zgłoszeniu", + "no_personnel_on_call": "Brak personelu na zgłoszeniu", + "call_activity": "Aktywność zgłoszenia", + "no_call_activity": "Brak aktywności zgłoszenia", + "on_call": "Na zgłoszeniu", + "filtered": "Przefiltrowane", + "active_filter": "Aktywny filtr", + "unit_status_change": "Zmiana statusu jednostki", + "personnel_status_change": "Zmiana statusu personelu", + "view_call_details": "Zobacz szczegóły zgłoszenia", + "dispatched_resources": "Zadysponowane", + "unassigned": "Nieprzypisane", + "available": "Dostępne", + "unknown": "Nieznane", + "search_personnel_placeholder": "Szukaj personelu...", + "search_calls_placeholder": "Szukaj zgłoszeń...", + "search_units_placeholder": "Szukaj jednostek...", + "search_notes_placeholder": "Szukaj notatek...", + "signalr_update": "Aktualizacja w czasie rzeczywistym", + "signalr_connected": "Połączono", + "realtime_updates_active": "Aktualizacje w czasie rzeczywistym są teraz aktywne", + "personnel_status_updated": "Status personelu zaktualizowany", + "personnel_staffing_updated": "Obsada personelu zaktualizowana", + "unit_status_updated": "Status jednostki zaktualizowany", + "calls_updated": "Zgłoszenia zaktualizowane", + "call_added": "Dodano nowe zgłoszenie", + "call_closed": "Zgłoszenie zamknięte", + "check_ins": "Meldunki", + "no_check_ins": "Brak zgłoszeń z licznikami meldunków", + "radio_log": "Dziennik radiowy", + "radio": "Radio", + "activity": "Aktywność", + "actions": "Akcje", + "no_radio_activity": "Brak transmisji radiowych", + "live": "NA ŻYWO", + "currently_transmitting": "Trwa transmisja...", + "duration": "Czas trwania", + "call_actions": "Akcje zgłoszenia", + "unit_actions": "Akcje jednostki", + "personnel_actions": { + "title": "Akcje personelu", + "status_tab": "Status", + "staffing_tab": "Obsada", + "select_status": "Wybierz status", + "select_staffing": "Wybierz poziom obsady", + "destination": "Cel", + "no_destination": "Brak celu", + "note": "Notatka", + "note_placeholder": "Dodaj opcjonalną notatkę...", + "update_status": "Zaktualizuj status", + "update_staffing": "Zaktualizuj obsadę", + "no_statuses_available": "Brak dostępnych statusów", + "no_staffings_available": "Brak dostępnych poziomów obsady" + }, + "unit_actions_panel": { + "status": "Status", + "select_status": "Wybierz status", + "destination": "Cel", + "no_destination": "Brak celu", + "note": "Notatka", + "note_placeholder": "Dodaj opcjonalną notatkę...", + "update_status": "Zaktualizuj status", + "no_statuses_available": "Brak dostępnych statusów", + "no_active_calls": "Brak aktywnych zgłoszeń", + "no_stations_available": "Brak dostępnych stacji", + "no_destinations_available": "Brak dostępnych celów" + }, + "call": "Zgłoszenie", + "station": "Stacja", + "calls": "Zgłoszenia", + "stations": "Stacje", + "no_stations_available": "Brak dostępnych stacji", + "new_call": "Nowe zgłoszenie", + "view_details": "Szczegóły", + "add_note": "Dodaj notatkę", + "close_call": "Zamknij", + "set_status": "Ustaw status", + "set_staffing": "Obsada", + "dispatch": "Dysponuj", + "select_items_for_actions": "Wybierz zgłoszenie, jednostkę lub personel, aby włączyć akcje kontekstowe", + "weather": { + "clear": "Bezchmurnie", + "mainly_clear": "Przeważnie bezchmurnie", + "partly_cloudy": "Częściowo zachmurzenie", + "overcast": "Pochmurno", + "fog": "Mgła", + "drizzle": "Mżawka", + "freezing_drizzle": "Marznąca mżawka", + "rain": "Deszcz", + "freezing_rain": "Marznący deszcz", + "snow": "Śnieg", + "rain_showers": "Przelotny deszcz", + "snow_showers": "Przelotny śnieg", + "thunderstorm": "Burza", + "thunderstorm_hail": "Burza z gradem", + "unknown": "Nieznane" + } + }, + "tabs": { + "calls": "Zgłoszenia", + "calendar": "Kalendarz", + "contacts": "Kontakty", + "home": "Strona główna", + "map": "Mapa", + "messages": "Wiadomości", + "notes": "Notatki", + "protocols": "Protokoły", + "settings": "Ustawienia", + "shifts": "Zmiany", + "personnel": "Personel" + }, + "check_in": { + "tab_title": "Meldunek", + "timer_status": "Status licznika", + "perform_check_in": "Zamelduj się", + "check_in_success": "Meldunek zarejestrowany pomyślnie", + "check_in_error": "Nie udało się zarejestrować meldunku", + "checked_in_by": "przez {{name}}", + "last_check_in": "Ostatni meldunek", + "elapsed": "Upłynęło", + "duration": "Czas trwania", + "status_ok": "OK", + "status_green": "OK", + "status_warning": "Ostrzeżenie", + "status_yellow": "Ostrzeżenie", + "status_overdue": "Zaległy", + "status_red": "Zaległy", + "status_critical": "Krytyczny", + "history": "Historia meldunków", + "no_timers": "Brak skonfigurowanych liczników meldunków", + "timers_disabled": "Liczniki meldunków są wyłączone dla tego zgłoszenia", + "type_personnel": "Personel", + "type_unit": "Jednostka", + "type_ic": "Kierujący działaniem ratowniczym", + "type_par": "PAR", + "type_hazmat": "Narażenie na materiały niebezpieczne", + "type_sector_rotation": "Rotacja sektora", + "type_rehab": "Rehabilitacja", + "add_note": "Dodaj notatkę (opcjonalnie)", + "confirm": "Potwierdź meldunek", + "minutes_ago": "{{count}} min temu", + "select_target": "Wybierz podmiot do meldunku", + "overdue_count": "{{count}} zaległych", + "warning_count": "{{count}} ostrzeżeń", + "enable_timers": "Włącz liczniki", + "disable_timers": "Wyłącz liczniki", + "summary": "{{overdue}} zaległych, {{warning}} ostrzeżeń, {{ok}} ok" + }, + "units": { + "title": "Jednostki" + }, + "weatherAlerts": { + "title": "Alerty pogodowe", + "activeAlerts": "Aktywne alerty", + "noActiveAlerts": "Brak aktywnych alertów pogodowych", + "moreAlerts": "+{{count}} więcej", + "severity": { + "extreme": "Ekstremalny", + "severe": "Poważny", + "moderate": "Umiarkowany", + "minor": "Niewielki", + "unknown": "Nieznany" + }, + "category": { + "met": "Meteorologiczny", + "fire": "Pożarowy", + "health": "Zdrowotny", + "env": "Środowiskowy", + "other": "Inny" + }, + "urgency": { + "immediate": "Natychmiastowa", + "expected": "Oczekiwana", + "future": "Przyszła", + "past": "Przeszła", + "unknown": "Nieznana" + }, + "certainty": { + "observed": "Zaobserwowane", + "likely": "Prawdopodobne", + "possible": "Możliwe", + "unlikely": "Mało prawdopodobne", + "unknown": "Nieznane" + }, + "status": { + "active": "Aktywny", + "updated": "Zaktualizowany", + "expired": "Wygasły", + "cancelled": "Anulowany" + }, + "detail": { + "headline": "Nagłówek", + "description": "Opis", + "instruction": "Instrukcje", + "area": "Obszar dotknięty", + "effective": "Obowiązuje od", + "onset": "Początek", + "expires": "Wygasa", + "sent": "Wysłano", + "sender": "Źródło", + "urgency": "Pilność", + "certainty": "Pewność" + }, + "filter": { + "all": "Wszystkie", + "nearby": "W pobliżu" + }, + "sort": { + "severity": "Ważność", + "expires": "Wkrótce wygasające", + "newest": "Najnowsze" + }, + "banner": { + "viewAll": "Zobacz wszystkie" + } + }, + "videoFeeds": { + "title": "Transmisje wideo", + "noFeeds": "Brak transmisji wideo dla tego zgłoszenia", + "addFeed": "Dodaj transmisję wideo", + "editFeed": "Edytuj transmisję wideo", + "deleteFeed": "Usuń transmisję wideo", + "deleteConfirm": "Czy na pewno chcesz usunąć tę transmisję wideo?", + "watch": "Oglądaj", + "goLive": "Rozpocznij transmisję", + "stopLive": "Zatrzymaj transmisję", + "flipCamera": "Odwróć kamerę", + "feedAdded": "Transmisja wideo dodana", + "feedUpdated": "Transmisja wideo zaktualizowana", + "feedDeleted": "Transmisja wideo usunięta", + "feedError": "Nie udało się załadować transmisji wideo", + "unsupportedFormat": "Ten format strumienia nie jest obsługiwany na urządzeniach mobilnych", + "copyUrl": "Kopiuj URL", + "form": { + "name": "Nazwa transmisji", + "namePlaceholder": "np. Dron Wóz 1", + "url": "URL strumienia", + "urlPlaceholder": "np. https://stream.example.com/live.m3u8", + "feedType": "Typ kamery", + "feedFormat": "Format strumienia", + "description": "Opis", + "descriptionPlaceholder": "Opcjonalny opis", + "status": "Status", + "sortOrder": "Kolejność sortowania", + "cameraLocation": "Lokalizacja kamery", + "useCurrentLocation": "Użyj bieżącej lokalizacji" + }, + "type": { + "drone": "Dron", + "fixedCamera": "Kamera stała", + "bodyCam": "Kamera osobista", + "trafficCam": "Kamera drogowa", + "weatherCam": "Kamera pogodowa", + "satelliteFeed": "Transmisja satelitarna", + "webCam": "Kamera internetowa", + "other": "Inna" + }, + "format": { + "rtsp": "RTSP", + "hls": "HLS", + "mjpeg": "MJPEG", + "youtubeLive": "YouTube Live", + "webrtc": "WebRTC", + "dash": "DASH", + "embed": "Osadzenie", + "other": "Inny" + }, + "status": { + "active": "Aktywna", + "inactive": "Nieaktywna", + "error": "Błąd" + } + }, + "welcome": "Witaj w aplikacji obytes" +} diff --git a/src/translations/sv.json b/src/translations/sv.json new file mode 100644 index 0000000..3bf6d82 --- /dev/null +++ b/src/translations/sv.json @@ -0,0 +1,1151 @@ +{ + "app": { + "title": "Resgrid Dispatch" + }, + "audio_streams": { + "buffering": "Buffrar", + "buffering_stream": "Buffrar ljudström...", + "close": "Stäng", + "currently_playing": "Spelar nu: {{streamName}}", + "loading": "Laddar", + "loading_stream": "Laddar ljudström...", + "loading_streams": "Laddar ljudströmmar...", + "name": "Namn", + "no_stream_playing": "Ingen ljudström spelas för närvarande", + "none": "Ingen", + "playing": "Spelar", + "refresh_streams": "Uppdatera strömmar", + "select_placeholder": "Välj en ljudström", + "select_stream": "Välj ljudström", + "status": "Status", + "stopped": "Stoppad", + "stream_info": "Ströminformation", + "stream_selected": "Vald: {{streamName}}", + "title": "Ljudströmmar", + "type": "Typ" + }, + "bluetooth": { + "applied": "Tillämpad", + "audio": "Ljud", + "audioActive": "Ljud aktivt", + "audio_device": "BT-handapparat", + "availableDevices": "Tillgängliga enheter", + "available_devices": "Tillgängliga enheter", + "bluetooth_not_ready": "Bluetooth är {{state}}. Vänligen aktivera Bluetooth.", + "buttonControlAvailable": "Knappstyrning tillgänglig", + "checking": "Kontrollerar Bluetooth-status...", + "clear": "Rensa", + "connect": "Anslut", + "connected": "Ansluten", + "connectionError": "Anslutningsfel", + "current_selection": "Aktuellt val", + "disconnect": "Koppla från", + "doublePress": "Dubbel ", + "liveKitActive": "LiveKit aktiv", + "longPress": "Lång ", + "micControl": "Mikrofonstyrning", + "mute": "Tysta", + "noDevicesFound": "Inga ljudenheter hittades", + "noDevicesFoundRetry": "Inga ljudenheter hittades. Försök skanna igen.", + "no_device_selected": "Ingen enhet vald", + "no_devices_found": "Inga Bluetooth-ljudenheter hittades", + "not_connected": "Ej ansluten", + "poweredOff": "Bluetooth är avstängt. Vänligen aktivera Bluetooth för att ansluta ljudenheter.", + "pttStart": "PTT start", + "pttStop": "PTT stopp", + "recentButtonEvents": "Senaste knapphändelser", + "scan": "Skanna", + "scanAgain": "Skanna igen", + "scan_again": "Skanna igen", + "scan_error_message": "Kan inte skanna efter Bluetooth-enheter", + "scan_error_title": "Skanningsfel", + "scanning": "Skannar...", + "select_device": "Välj Bluetooth-enhet", + "selected": "Vald", + "selection_error_message": "Kan inte spara föredragen enhet", + "selection_error_title": "Valfel", + "startScanning": "Börja skanna", + "stopScan": "Stoppa skanning", + "supports_mic_control": "Mikrofonstyrning", + "tap_scan_to_find_devices": "Tryck på 'Skanna' för att hitta Bluetooth-ljudenheter", + "title": "Bluetooth-ljud", + "unauthorized": "Bluetooth-behörighet nekad. Vänligen bevilja Bluetooth-behörigheter i Inställningar.", + "unknown": "Okänd", + "unknownDevice": "Okänd enhet", + "unknown_device": "Okänd enhet", + "unmute": "Slå på ljud", + "volumeDown": "Volym -", + "volumeUp": "Volym +" + }, + "callImages": { + "add": "Lägg till bild", + "add_new": "Lägg till ny bild", + "default_name": "Namnlös bild", + "error": "Fel vid hämtning av bilder", + "failed_to_load": "Kunde inte ladda bild", + "image_alt": "Ärendebild", + "image_name": "Bildnamn", + "image_note": "Bildanteckning", + "loading": "Laddar...", + "no_images": "Inga bilder tillgängliga", + "no_images_description": "Lägg till bilder till ditt ärende för att underlätta dokumentation och kommunikation", + "select_from_gallery": "Välj från galleri", + "take_photo": "Ta foto", + "title": "Ärendebilder", + "upload": "Ladda upp" + }, + "callNotes": { + "addNote": "Lägg till anteckning", + "addNotePlaceholder": "Lägg till en ny anteckning...", + "noNotes": "Inga anteckningar tillgängliga för detta ärende", + "noSearchResults": "Inga anteckningar matchar din sökning", + "searchPlaceholder": "Sök anteckningar...", + "title": "Ärendeanteckningar" + }, + "call_detail": { + "address": "Adress", + "call_location": "Ärendeplats", + "close_call": "Stäng ärende", + "close_call_confirmation": "Är du säker på att du vill stänga detta ärende?", + "close_call_error": "Kunde inte stänga ärende", + "close_call_note": "Stängningsanteckning", + "close_call_note_placeholder": "Ange en anteckning om stängningen av ärendet", + "close_call_success": "Ärende stängt", + "close_call_type": "Stängningstyp", + "close_call_type_placeholder": "Välj stängningstyp", + "close_call_type_required": "Vänligen välj en stängningstyp", + "close_call_types": { + "cancelled": "Avbrutet", + "closed": "Stängt", + "false_alarm": "Falsklarm", + "founded": "Grundat", + "minor": "Mindre", + "transferred": "Överfört", + "unfounded": "Ogrundat" + }, + "contact_email": "E-post", + "contact_info": "Kontaktinfo", + "contact_name": "Kontaktnamn", + "contact_phone": "Telefon", + "edit_call": "Redigera ärende", + "external_id": "Externt ID", + "failed_to_open_maps": "Kunde inte öppna kartapplikation", + "files": { + "add_file": "Lägg till fil", + "button": "Filer", + "empty": "Inga filer tillgängliga", + "empty_description": "Lägg till filer till ditt ärende för att underlätta dokumentation och kommunikation", + "error": "Fel vid hämtning av filer", + "file_name": "Filnamn", + "name_required": "Vänligen ange ett namn för filen", + "no_files": "Inga filer tillgängliga", + "no_files_description": "Lägg till filer till ditt ärende för att underlätta dokumentation och kommunikation", + "open_error": "Fel vid öppning av fil", + "select_error": "Fel vid val av fil", + "select_file": "Välj fil", + "share_error": "Fel vid delning av fil", + "title": "Ärendefiler", + "upload": "Ladda upp", + "upload_error": "Fel vid uppladdning av fil", + "uploading": "Laddar upp..." + }, + "group": "Grupp", + "images": "Bilder", + "loading": "Laddar ärendedetaljer...", + "nature": "Händelsetyp", + "no_additional_info": "Ingen ytterligare information tillgänglig", + "no_contact_info": "Ingen kontaktinformation tillgänglig", + "no_dispatched": "Inga enheter utlarmade till detta ärende", + "no_location": "Ingen platsdata tillgänglig", + "no_location_for_routing": "Ingen platsdata tillgänglig för vägbeskrivning", + "no_protocols": "Inga protokoll kopplade till detta ärende", + "no_timeline": "Inga tidslinjehändelser tillgängliga", + "not_available": "Ej tillgänglig", + "not_found": "Ärende hittades inte", + "missing_call_id": "Ärende-ID saknas", + "note": "Anteckning", + "notes": "Anteckningar", + "priority": "Prioritet", + "reference_id": "Referens-ID", + "set_active": "Ange som aktivt", + "set_active_error": "Kunde inte ange ärende som aktivt", + "set_active_success": "Ärende angivet som aktivt", + "setting_active": "Anger som aktivt...", + "status": "Status", + "tabs": { + "contact": "Kontakt", + "dispatched": "Utlarmade", + "info": "Info", + "protocols": "Protokoll", + "timeline": "Aktivitet", + "video": "Video" + }, + "timestamp": "Tidsstämpel", + "title": "Ärendedetaljer", + "type": "Typ", + "unit": "Enhet", + "update_call_error": "Kunde inte uppdatera ärende", + "update_call_success": "Ärende uppdaterat" + }, + "calls": { + "address": "Adress", + "address_found": "Adress hittad och plats uppdaterad", + "address_not_found": "Adress hittades inte, försök med en annan adress", + "address_placeholder": "Ange adressen för ärendet", + "address_required": "Vänligen ange en adress att söka efter", + "call_details": "Ärendedetaljer", + "call_location": "Ärendeplats", + "call_number": "Ärendenummer", + "call_priority": "Ärendeprioritet", + "confirm_deselect_message": "Är du säker på att du vill avmarkera det aktiva ärendet?", + "confirm_deselect_title": "Avmarkera aktivt ärende", + "contact_info": "Kontaktinfo", + "contact_info_placeholder": "Ange kontaktinformation", + "contact_name": "Kontaktnamn", + "contact_name_placeholder": "Ange kontaktens namn", + "contact_phone": "Kontakttelefon", + "contact_phone_placeholder": "Ange kontaktens telefonnummer", + "coordinates": "GPS-koordinater", + "coordinates_found": "Koordinater hittade och adress uppdaterad", + "coordinates_geocoding_error": "Kunde inte hämta adress för koordinater, men plats markerad på kartan", + "coordinates_invalid_format": "Ogiltigt koordinatformat. Använd formatet: latitud, longitud", + "coordinates_no_address": "Koordinater markerade på kartan, men ingen adress hittades", + "coordinates_out_of_range": "Koordinater utanför giltigt intervall. Latitud måste vara -90 till 90, longitud -180 till 180", + "coordinates_placeholder": "Ange GPS-koordinater (t.ex. 37.7749, -122.4194)", + "coordinates_required": "Vänligen ange koordinater att söka efter", + "create": "Skapa", + "create_error": "Fel vid skapande av ärende", + "create_new_call": "Skapa nytt ärende", + "create_success": "Ärende skapat", + "description": "Beskrivning", + "description_placeholder": "Ange beskrivning av ärendet", + "deselect": "Avmarkera", + "directions": "Vägbeskrivning", + "dispatch_to": "Larma till", + "dispatch_to_everyone": "Larma all tillgänglig personal", + "edit_call": "Redigera ärende", + "edit_call_description": "Uppdatera ärendeinformation", + "everyone": "Alla", + "files": { + "no_files": "Inga filer tillgängliga", + "no_files_description": "Inga filer har lagts till i detta ärende ännu", + "title": "Ärendefiler" + }, + "geocoding_error": "Kunde inte söka efter adress, försök igen", + "groups": "Grupper", + "invalid_priority": "Ogiltig prioritet vald. Vänligen välj en giltig prioritet.", + "invalid_type": "Ogiltig typ vald. Vänligen välj en giltig ärendetyp.", + "loading": "Laddar ärenden...", + "loading_calls": "Laddar ärenden...", + "name": "Namn", + "name_placeholder": "Ange ärendets namn", + "nature": "Händelsetyp", + "nature_placeholder": "Ange händelsetyp", + "new_call": "Nytt ärende", + "new_call_description": "Skapa ett nytt ärende för att starta en ny insats", + "no_call_selected": "Inget aktivt ärende", + "no_call_selected_info": "Denna enhet svarar för närvarande inte på något ärende", + "no_calls": "Inga aktiva ärenden", + "no_calls_available": "Inga ärenden tillgängliga", + "no_calls_description": "Inga aktiva ärenden hittades. Välj ett aktivt ärende för att visa detaljer.", + "no_location_message": "Detta ärende har ingen platsdata tillgänglig för navigering.", + "no_location_title": "Ingen plats tillgänglig", + "no_open_calls": "Inga öppna ärenden tillgängliga", + "note": "Anteckning", + "note_placeholder": "Ange anteckning för ärendet", + "plus_code": "Plus-kod", + "plus_code_found": "Plus-kod hittad och plats uppdaterad", + "plus_code_geocoding_error": "Kunde inte söka efter plus-kod, försök igen", + "plus_code_not_found": "Plus-kod hittades inte, försök med en annan plus-kod", + "plus_code_placeholder": "Ange plus-kod (t.ex. 849VCWC8+R9)", + "plus_code_required": "Vänligen ange en plus-kod att söka efter", + "priority": "Prioritet", + "priority_placeholder": "Välj prioritet för ärendet", + "roles": "Roller", + "search": "Sök ärenden...", + "select_active_call": "Välj aktivt ärende", + "select_address": "Välj adress", + "select_address_placeholder": "Välj adressen för ärendet", + "select_description": "Välj beskrivning", + "select_dispatch_recipients": "Välj larmmottagare", + "select_location": "Välj plats på kartan", + "select_name": "Välj namn", + "select_nature": "Välj händelsetyp", + "select_nature_placeholder": "Välj händelsetyp för ärendet", + "select_priority": "Välj prioritet", + "select_priority_placeholder": "Välj prioritet för ärendet", + "select_recipients": "Välj mottagare", + "select_type": "Välj typ", + "selected": "vald", + "title": "Ärenden", + "type": "Typ", + "units": "Enheter", + "users": "Användare", + "viewNotes": "Anteckningar", + "view_details": "Visa detaljer", + "what3words": "what3words", + "what3words_found": "what3words-adress hittad och plats uppdaterad", + "what3words_geocoding_error": "Kunde inte söka efter what3words-adress, försök igen", + "what3words_invalid_format": "Ogiltigt what3words-format. Använd formatet: ord.ord.ord", + "what3words_not_found": "what3words-adress hittades inte, försök med en annan adress", + "what3words_placeholder": "Ange what3words-adress (t.ex. filled.count.soap)", + "what3words_required": "Vänligen ange en what3words-adress att söka efter", + "expand_map": "Förstora karta", + "schedule_dispatch": "Schemalägg utlarmning", + "schedule_dispatch_description": "Ange ett framtida datum och tid för att schemalägga detta ärende för senare utlarmning", + "scheduled_on": "Schemalagt datum och tid", + "scheduled_on_helper": "Lämna tomt för att larma omedelbart", + "scheduled_on_past_error": "Schemalagd tid måste vara i framtiden", + "contact_information": "Kontaktinformation", + "new_call_web_hint": "Fyll i ärendeuppgifterna nedan. Tryck Ctrl+Enter för att skapa.", + "edit_call_web_hint": "Uppdatera ärendeuppgifterna nedan. Tryck Ctrl+S för att spara.", + "keyboard_shortcuts": "Tips: Tryck Ctrl+Enter för att skapa, Escape för att avbryta", + "edit_keyboard_shortcuts": "Tips: Tryck Ctrl+S för att spara, Escape för att avbryta", + "templates": { + "title": "Ärendemallar", + "select_template": "Välj mall", + "search_placeholder": "Sök mallar...", + "none": "Inga mallar tillgängliga", + "template_applied": "Mall tillämpad" + }, + "form": { + "title": "Ärendeformulär", + "no_form": "Inget ärendeformulär konfigurerat" + }, + "linked_calls": { + "title": "Länkat ärende", + "select": "Länka till befintligt ärende", + "change": "Ändra länkat ärende", + "none": "Inga aktiva ärenden tillgängliga", + "search_placeholder": "Sök ärenden...", + "linked": "Länkat" + }, + "contact_picker": { + "title": "Välj kontakt", + "search_placeholder": "Sök kontakter...", + "none": "Inga kontakter tillgängliga" + }, + "protocols": { + "title": "Protokoll", + "select": "Välj protokoll", + "none": "Inga protokoll tillgängliga", + "selected_count": "valda", + "expand_questions": "Visa frågor", + "collapse": "Dölj" + } + }, + "common": { + "add": "Lägg till", + "back": "Tillbaka", + "cancel": "Avbryt", + "creating": "Skapar...", + "saving": "Sparar...", + "unsaved_changes": "Osparade ändringar", + "close": "Stäng", + "confirm": "Bekräfta", + "confirm_location": "Bekräfta plats", + "delete": "Radera", + "dismiss": "Stäng", + "done": "Klar", + "edit": "Redigera", + "error": "Fel", + "errorOccurred": "Ett fel inträffade", + "get_my_location": "Hämta min plats", + "go_back": "Gå tillbaka", + "loading": "Laddar...", + "loading_address": "Laddar adress...", + "next": "Nästa", + "noActiveUnit": "Ingen aktiv enhet angiven", + "noActiveUnitDescription": "Vänligen ange en aktiv enhet från inställningssidan för att komma åt statuskontroller", + "noDataAvailable": "Ingen data tillgänglig", + "no_address_found": "Ingen adress hittades", + "no_location": "Ingen platsdata tillgänglig", + "no_results_found": "Inga resultat hittades", + "no_unit_selected": "Ingen enhet vald", + "nothingToDisplay": "Det finns inget att visa just nu", + "of": "av", + "ok": "Ok", + "optional": "valfritt", + "permission_denied": "Behörighet nekad", + "previous": "Föregående", + "remove": "Ta bort", + "retry": "Försök igen", + "route": "Rutt", + "save": "Spara", + "search": "Sök...", + "set_location": "Ange plats", + "share": "Dela", + "step": "Steg", + "submit": "Skicka", + "submitting": "Skickar...", + "tryAgainLater": "Vänligen försök igen senare", + "unknown": "Okänd", + "unknown_department": "Okänd avdelning", + "unknown_user": "Okänd användare", + "upload": "Ladda upp", + "uploading": "Laddar upp..." + }, + "contacts": { + "add": "Lägg till kontakt", + "addedBy": "Tillagd av", + "addedOn": "Tillagd den", + "additionalInformation": "Ytterligare information", + "address": "Adress", + "bluesky": "Bluesky", + "cancel": "Avbryt", + "cellPhone": "Mobiltelefon", + "city": "Stad", + "cityState": "Stad och län", + "cityStateZip": "Stad, län, postnummer", + "company": "Företag", + "contactInformation": "Kontaktinformation", + "contactNotes": "Kontaktanteckningar", + "contactNotesEmpty": "Inga anteckningar hittades för denna kontakt", + "contactNotesEmptyDescription": "Anteckningar som läggs till för denna kontakt visas här", + "contactNotesExpired": "Denna anteckning har gått ut", + "contactNotesLoading": "Laddar kontaktanteckningar...", + "contactType": "Kontakttyp", + "countryId": "Lands-ID", + "delete": "Radera", + "deleteConfirm": "Är du säker på att du vill radera denna kontakt?", + "deleteSuccess": "Kontakt raderad", + "description": "Lägg till och hantera dina kontakter", + "details": "Kontaktdetaljer", + "detailsTab": "Detaljer", + "edit": "Redigera kontakt", + "editedBy": "Redigerad av", + "editedOn": "Redigerad den", + "email": "E-post", + "empty": "Inga kontakter hittades", + "emptyDescription": "Lägg till kontakter för att hantera dina personliga och affärsmässiga förbindelser", + "entranceCoordinates": "Infartskoordinater", + "exitCoordinates": "Utfartskoordinater", + "expires": "Utgår", + "facebook": "Facebook", + "faxPhone": "Fax", + "formError": "Vänligen åtgärda felen i formuläret", + "homePhone": "Hemtelefon", + "identification": "Identifikation", + "important": "Markera som viktig", + "instagram": "Instagram", + "internal": "Intern", + "invalidEmail": "Ogiltig e-postadress", + "linkedin": "LinkedIn", + "locationCoordinates": "Platskoordinater", + "locationInformation": "Platsinformation", + "mastodon": "Mastodon", + "mobile": "Mobil", + "name": "Namn", + "noteAlert": "Varning", + "noteType": "Typ", + "notes": "Anteckningar", + "notesTab": "Anteckningar", + "officePhone": "Kontorstelefon", + "otherInfo": "Övrig information", + "person": "Person", + "phone": "Telefon", + "public": "Offentlig", + "required": "Obligatoriskt", + "save": "Spara kontakt", + "saveSuccess": "Kontakt sparad", + "search": "Sök kontakter...", + "shouldAlert": "Ska larma", + "socialMediaWeb": "Sociala medier och webb", + "state": "Län", + "stateId": "Läns-ID", + "systemInformation": "Systeminformation", + "tabs": { + "details": "Detaljer", + "notes": "Anteckningar" + }, + "threads": "Threads", + "title": "Kontakter", + "twitter": "Twitter", + "visibility": "Synlighet", + "website": "Webbplats", + "zip": "Postnummer" + }, + "form": { + "invalid_url": "Vänligen ange en giltig URL som börjar med http:// eller https://", + "required": "Detta fält är obligatoriskt" + }, + "livekit": { + "audio_devices": "Ljudenheter", + "audio_settings": "Ljudinställningar", + "connected_to_room": "Ansluten till kanal", + "connecting": "Ansluter...", + "disconnect": "Koppla från", + "join": "Anslut", + "microphone": "Mikrofon", + "mute": "Tysta", + "no_rooms_available": "Inga röstkanaler tillgängliga", + "speaker": "Högtalare", + "speaking": "Talar", + "title": "Röstkanaler", + "unmute": "Slå på ljud" + }, + "loading": { + "loading": "Laddar...", + "loadingData": "Laddar data...", + "pleaseWait": "Vänligen vänta", + "processingRequest": "Bearbetar din förfrågan..." + }, + "sso": { + "authenticating": "Autentiserar...", + "back_to_login": "Tillbaka till inloggning", + "back_to_lookup": "Byt användare", + "continue_button": "Fortsätt", + "department_id_label": "Avdelnings-ID", + "department_id_placeholder": "Ange avdelnings-ID", + "error_generic": "Inloggning misslyckades. Vänligen försök igen.", + "error_oidc_cancelled": "Inloggningen avbröts.", + "error_oidc_not_ready": "SSO-leverantören laddas, vänligen vänta.", + "error_sso_not_enabled": "Enkel inloggning är inte aktiverad för denna användare.", + "error_token_exchange": "Kunde inte slutföra inloggningen. Vänligen försök igen.", + "error_user_not_found": "Användare hittades inte. Vänligen kontrollera och försök igen.", + "looking_up": "Söker...", + "optional": "valfritt", + "page_subtitle": "Ange ditt användarnamn för att söka efter din organisations inloggningsalternativ.", + "page_title": "Enkel inloggning", + "provider_oidc": "OpenID Connect (OIDC)", + "provider_saml": "SAML 2.0", + "sign_in_button": "Logga in med SSO", + "sign_in_title": "Logga in", + "sso_button": "SSO-inloggning" + }, + "login": { + "branding_subtitle": "Kraftfull larmbehandlingsprogramvara för räddningstjänst, eftersök och räddning samt samhällsskydd.", + "branding_title": "Hantering av räddningsinsatser", + "errorModal": { + "confirmButton": "OK", + "message": "Vänligen kontrollera ditt användarnamn och lösenord och försök igen.", + "title": "Inloggning misslyckades" + }, + "feature_dispatch_desc": "Larma enheter omedelbart och hantera ärenden med realtidsuppdateringar på alla enheter.", + "feature_dispatch_title": "Realtidsutlarmning", + "feature_mapping_desc": "Spåra enheter i realtid med detaljerade kartor, vägbeskrivningar och platshantering.", + "feature_mapping_title": "Avancerade kartor", + "feature_personnel_desc": "Hantera ditt team med rollbaserad åtkomst, statusspårning och kommunikationsverktyg.", + "feature_personnel_title": "Personalhantering", + "footer_text": "Skapat med ❤️ i Lake Tahoe", + "login": "Logga in", + "login_button": "Logga in", + "login_button_description": "Logga in på ditt konto för att fortsätta", + "login_button_error": "Fel vid inloggning", + "login_button_loading": "Loggar in...", + "login_button_success": "Inloggning lyckades", + "no_account": "Har du inget konto?", + "page_subtitle": "Ange dina uppgifter för att logga in.", + "page_title": "Resgrid Dispatch", + "password": "Lösenord", + "password_incorrect": "Lösenordet var felaktigt", + "password_placeholder": "Ange ditt lösenord", + "register": "Registrera", + "title": "Logga in", + "username": "Användarnamn", + "username_placeholder": "Ange ditt användarnamn", + "welcome_title": "Välkommen tillbaka" + }, + "lockscreen": { + "message": "Ange ditt lösenord för att låsa upp skärmen", + "not_you": "Inte du? Tillbaka till inloggning", + "password": "Lösenord", + "password_placeholder": "Ange ditt lösenord", + "title": "Låsskärm", + "unlock_button": "Lås upp", + "unlock_failed": "Upplåsning misslyckades. Vänligen försök igen.", + "unlocking": "Låser upp...", + "welcome_back": "Välkommen tillbaka" + }, + "maintenance": { + "downtime_message": "Vi arbetar hårt för att slutföra underhållet så snabbt som möjligt. Vänligen kontrollera igen snart.", + "downtime_title": "Vad är driftstoppet?", + "message": "Vänligen kontrollera igen om en stund.", + "support_message": "Om du behöver hjälp, vänligen kontakta oss på", + "support_title": "Behöver du support?", + "title": "Webbplatsen är under underhåll", + "why_down_message": "Vi utför planerat underhåll för att förbättra din upplevelse. Vi ber om ursäkt för eventuella besvär.", + "why_down_title": "Varför är webbplatsen nere?" + }, + "menu": { + "calls": "Ärenden", + "calls_list": "Ärendelista", + "contacts": "Kontakter", + "home": "Hem", + "map": "Karta", + "menu": "Meny", + "messages": "Meddelanden", + "new_call": "Nytt ärende", + "personnel": "Personal", + "protocols": "Protokoll", + "settings": "Inställningar", + "units": "Enheter", + "weatherAlerts": "Vädervarningar" + }, + "map": { + "call_set_as_current": "Ärende angivet som aktuellt ärende", + "failed_to_open_maps": "Kunde inte öppna kartapplikation", + "failed_to_set_current_call": "Kunde inte ange ärende som aktuellt ärende", + "layers": "Kartlager", + "no_layers": "Inga lager tillgängliga", + "no_location_for_routing": "Ingen platsdata tillgänglig för vägbeskrivning", + "pin_color": "Nålfärg", + "recenter_map": "Centrera kartan", + "set_as_current_call": "Ange som aktuellt ärende", + "show_all": "Visa alla", + "hide_all": "Dölj alla", + "view_call_details": "Visa ärendedetaljer" + }, + "notes": { + "actions": { + "add": "Lägg till anteckning", + "delete_confirm": "Är du säker på att du vill radera denna anteckning?" + }, + "details": { + "close": "Stäng", + "created": "Skapad", + "delete": "Radera", + "edit": "Redigera", + "tags": "Taggar", + "title": "Anteckningsdetaljer", + "updated": "Uppdaterad" + }, + "empty": "Inga anteckningar hittades", + "emptyDescription": "Inga anteckningar har skapats för din avdelning ännu.", + "search": "Sök anteckningar...", + "title": "Anteckningar" + }, + "personnel": { + "title": "Personal", + "search": "Sök personal...", + "loading": "Laddar personal...", + "empty": "Ingen personal hittades", + "empty_description": "Ingen personal är för närvarande tillgänglig i din avdelning.", + "no_results": "Ingen personal matchar din sökning", + "no_results_description": "Försök justera dina söktermer.", + "status": "Status", + "staffing": "Bemanning", + "group": "Grupp", + "roles": "Roller", + "email": "E-post", + "phone": "Telefon", + "id_number": "ID-nummer", + "responding_to": "Svarar på", + "status_updated": "Status uppdaterad", + "staffing_updated": "Bemanning uppdaterad", + "details": "Personaldetaljer", + "contact_info": "Kontaktinformation", + "status_info": "Status och bemanning", + "no_email": "Ingen e-post tillgänglig", + "no_phone": "Inget telefonnummer tillgängligt", + "no_group": "Ej tilldelad", + "no_roles": "Inga roller tilldelade", + "unknown_status": "Okänd", + "on_duty": "I tjänst", + "off_duty": "Ej i tjänst", + "call_phone": "Ring", + "send_email": "E-post", + "custom_fields": "Ytterligare information" + }, + "onboarding": { + "screen1": { + "title": "Resgrid Dispatch", + "description": "Skapa, larma och hantera nödsamtal med ett kraftfullt mobilt ledningscentrum i din hand" + }, + "screen2": { + "title": "Realtidslägesbild", + "description": "Spåra alla enheter, personal och resurser på en interaktiv karta med realtidsuppdateringar och AVL" + }, + "screen3": { + "title": "Sömlös samordning", + "description": "Kommunicera direkt med fältenheter, uppdatera ärendestatus och samordna insatser var som helst" + }, + "skip": "Hoppa över", + "next": "Nästa", + "getStarted": "Kom igång" + }, + "protocols": { + "details": { + "close": "Stäng", + "code": "Kod", + "created": "Skapad", + "title": "Protokolldetaljer", + "updated": "Uppdaterad" + }, + "empty": "Inga protokoll hittades", + "emptyDescription": "Inga protokoll har skapats för din avdelning ännu.", + "search": "Sök protokoll...", + "title": "Protokoll" + }, + "push_notifications": { + "close": "Stäng", + "message": "Meddelande", + "new_notification": "Ny avisering", + "title": "Rubrik", + "types": { + "call": "Ärende", + "chat": "Chatt", + "group_chat": "Gruppchatt", + "message": "Meddelande", + "notification": "Avisering" + }, + "unknown_type_warning": "Okänd aviseringstyp mottagen", + "view_call": "Visa ärende", + "view_message": "Visa meddelande" + }, + "roles": { + "modal": { + "title": "Enhetsrolltilldelningar" + }, + "selectUser": "Välj användare", + "status": "{{active}} av {{total}} roller", + "tap_to_manage": "Tryck för att hantera roller", + "unassigned": "Ej tilldelad" + }, + "settings": { + "about": "Om", + "account": "Konto", + "active_unit": "Aktiv enhet", + "app_info": "Appinfo", + "app_name": "Appnamn", + "arabic": "Arabiska", + "audio_device_selection": { + "bluetooth_device": "Bluetooth-enhet", + "current_selection": "Aktuellt val", + "microphone": "Mikrofon", + "no_microphones_available": "Inga mikrofoner tillgängliga", + "no_speakers_available": "Inga högtalare tillgängliga", + "none_selected": "Ingen vald", + "speaker": "Högtalare", + "speaker_device": "Högtalarenhet", + "title": "Val av ljudenhet", + "unavailable": "Ej tillgänglig", + "wired_device": "Kabelansluten enhet" + }, + "background_geolocation": "Bakgrundspositionering", + "background_geolocation_warning": "Denna funktion tillåter appen att spåra din plats i bakgrunden. Det hjälper till med samordning av räddningsinsatser men kan påverka batteritiden.", + "background_location": "Bakgrundsplats", + "contact_us": "Kontakta oss", + "current_unit": "Aktuell enhet", + "english": "Engelska", + "french": "Franska", + "german": "Tyska", + "italian": "Italienska", + "enter_password": "Ange ditt lösenord", + "enter_server_url": "Ange Resgrid API-URL (t.ex. https://api.resgrid.com)", + "enter_username": "Ange ditt användarnamn", + "environment": "Miljö", + "general": "Allmänt", + "generale": "Allmänt", + "github": "Github", + "help_center": "Hjälpcenter", + "keep_alive": "Håll aktiv", + "keep_alive_warning": "Varning: Om du aktiverar håll aktiv förhindras din enhet från att gå i viloläge och kan öka batteriförbrukningen avsevärt.", + "keep_screen_on": "Håll skärmen på", + "language": "Språk", + "links": "Länkar", + "login_info": "Inloggningsuppgifter", + "logout": "Logga ut", + "more": "Mer", + "no_units_available": "Inga enheter tillgängliga", + "none_selected": "Ingen vald", + "notifications": "Push-aviseringar", + "notifications_description": "Aktivera aviseringar för att ta emot larm och uppdateringar", + "notifications_enable": "Aktivera aviseringar", + "password": "Lösenord", + "preferences": "Inställningar", + "privacy": "Integritetspolicy", + "privacy_policy": "Integritetspolicy", + "rate": "Betygsätt", + "select_unit": "Välj enhet", + "server": "Server", + "server_url": "Server-URL", + "server_url_note": "Observera: Detta är URL:en till Resgrid API. Den används för att ansluta till Resgrid-servern. Inkludera inte /api/v4 i URL:en eller ett avslutande snedstreck.", + "set_active_unit": "Ange aktiv enhet", + "share": "Dela", + "polish": "Polska", + "spanish": "Spanska", + "swedish": "Svenska", + "ukrainian": "Ukrainska", + "status_page": "Systemstatus", + "support": "Support", + "support_us": "Stöd oss", + "terms": "Användarvillkor", + "theme": { + "dark": "Mörkt", + "light": "Ljust", + "system": "System", + "title": "Tema" + }, + "title": "Inställningar", + "unit_selected_successfully": "{{unitName}} vald", + "unit_selection": "Enhetsval", + "unit_selection_failed": "Kunde inte välja enhet. Vänligen försök igen.", + "username": "Användarnamn", + "version": "Version", + "website": "Webbplats" + }, + "status": { + "add_note": "Lägg till anteckning", + "both_destinations_enabled": "Kan svara på ärenden eller stationer", + "call_destination_enabled": "Kan svara på ärenden", + "calls_tab": "Ärenden", + "failed_to_save_status": "Kunde inte spara status. Vänligen försök igen.", + "general_status": "Allmän status utan specifik destination", + "loading_stations": "Laddar stationer...", + "no_destination": "Ingen destination", + "no_stations_available": "Inga stationer tillgängliga", + "no_statuses_available": "Inga statusar tillgängliga", + "note": "Anteckning", + "note_optional": "Lägg till en valfri anteckning för denna statusuppdatering", + "note_required": "Vänligen ange en anteckning för denna statusuppdatering", + "select_destination": "Välj destination för {{status}}", + "select_destination_type": "Vart vill du svara?", + "select_status": "Välj status", + "select_status_type": "Vilken status vill du ange?", + "selected_destination": "Vald destination", + "selected_status": "Vald status", + "set_status": "Ange status", + "station_destination_enabled": "Kan svara på stationer", + "stations_tab": "Stationer", + "status_saved_successfully": "Status sparad!" + }, + "dispatch": { + "active_calls": "Aktiva ärenden", + "pending_calls": "Väntande", + "scheduled_calls": "Schemalagda", + "units_available": "Tillgängliga", + "personnel_available": "Tillgängliga", + "personnel_on_duty": "I tjänst", + "units": "Enheter", + "personnel": "Personal", + "map": "Karta", + "notes": "Anteckningar", + "activity_log": "Aktivitetslogg", + "communications": "Kommunikation", + "no_active_calls": "Inga aktiva ärenden", + "no_units": "Inga enheter tillgängliga", + "no_personnel": "Ingen personal tillgänglig", + "no_notes": "Inga anteckningar tillgängliga", + "no_activity": "Ingen senaste aktivitet", + "current_channel": "Aktuell kanal", + "audio_stream": "Ljudström", + "no_stream": "Ingen ström aktiv", + "ptt": "PTT", + "ptt_start": "PTT start", + "ptt_end": "PTT slut", + "transmitting_on": "Sänder på {{channel}}", + "transmission_ended": "Sändning avslutad", + "voice_disabled": "Röst inaktiverad", + "disconnected": "Frånkopplad", + "select_channel": "Välj kanal", + "select_channel_description": "Välj en röstkanal att ansluta till", + "change_channel_warning": "Att välja en ny kanal kopplar från den nuvarande", + "default_channel": "Standard", + "no_channels_available": "Inga röstkanaler tillgängliga", + "system_update": "Systemuppdatering", + "data_refreshed": "Data uppdaterad från servern", + "call_selected": "Ärende valt", + "unit_selected": "Enhet vald", + "unit_deselected": "Enhet avmarkerad", + "personnel_selected": "Personal vald", + "personnel_deselected": "Personal avmarkerad", + "loading_map": "Laddar karta...", + "map_not_available_web": "Karta ej tillgänglig på webbplattform", + "filtering_by_call": "Filtrerar efter ärende", + "clear_filter": "Rensa filter", + "call_filter_active": "Ärendefilter aktivt", + "call_filter_cleared": "Ärendefilter rensat", + "showing_all_data": "Visar all data", + "call_notes": "Ärendeanteckningar", + "no_call_notes": "Inga ärendeanteckningar", + "add_call_note_placeholder": "Lägg till en anteckning...", + "note_added": "Anteckning tillagd", + "note_added_to_console": "En ny anteckning har lagts till i konsolen", + "add_note_title": "Lägg till ny anteckning", + "note_title_label": "Rubrik", + "note_title_placeholder": "Ange anteckningsrubrik...", + "note_category_label": "Kategori", + "note_category_placeholder": "Välj en kategori", + "note_no_category": "Ingen kategori", + "note_body_label": "Anteckningsinnehåll", + "note_body_placeholder": "Ange anteckningsinnehåll...", + "note_save_error": "Kunde inte spara anteckning: {{error}}", + "note_created": "Anteckning skapad", + "units_on_call": "Enheter på ärende", + "no_units_on_call": "Inga enheter på ärende", + "personnel_on_call": "Personal på ärende", + "no_personnel_on_call": "Ingen personal på ärende", + "call_activity": "Ärendeaktivitet", + "no_call_activity": "Ingen ärendeaktivitet", + "on_call": "På ärende", + "filtered": "Filtrerad", + "active_filter": "Aktivt filter", + "unit_status_change": "Enhetsstatusändring", + "personnel_status_change": "Personalstatusändring", + "view_call_details": "Visa ärendedetaljer", + "dispatched_resources": "Utlarmade", + "unassigned": "Ej tilldelad", + "available": "Tillgänglig", + "unknown": "Okänd", + "search_personnel_placeholder": "Sök personal...", + "search_calls_placeholder": "Sök ärenden...", + "search_units_placeholder": "Sök enheter...", + "search_notes_placeholder": "Sök anteckningar...", + "signalr_update": "Realtidsuppdatering", + "signalr_connected": "Ansluten", + "realtime_updates_active": "Realtidsuppdateringar är nu aktiva", + "personnel_status_updated": "Personalstatus uppdaterad", + "personnel_staffing_updated": "Personalbemanning uppdaterad", + "unit_status_updated": "Enhetsstatus uppdaterad", + "calls_updated": "Ärenden uppdaterade", + "call_added": "Nytt ärende tillagt", + "call_closed": "Ärende stängt", + "check_ins": "Incheckningar", + "no_check_ins": "Inga ärenden med incheckningstimer", + "radio_log": "Radiologg", + "radio": "Radio", + "activity": "Aktivitet", + "actions": "Åtgärder", + "no_radio_activity": "Inga radiosändningar", + "live": "LIVE", + "currently_transmitting": "Sänder för närvarande...", + "duration": "Varaktighet", + "call_actions": "Ärendeåtgärder", + "unit_actions": "Enhetsåtgärder", + "personnel_actions": { + "title": "Personalåtgärder", + "status_tab": "Status", + "staffing_tab": "Bemanning", + "select_status": "Välj status", + "select_staffing": "Välj bemanningsnivå", + "destination": "Destination", + "no_destination": "Ingen destination", + "note": "Anteckning", + "note_placeholder": "Lägg till en valfri anteckning...", + "update_status": "Uppdatera status", + "update_staffing": "Uppdatera bemanning", + "no_statuses_available": "Inga statusar tillgängliga", + "no_staffings_available": "Inga bemanningsnivåer tillgängliga" + }, + "unit_actions_panel": { + "status": "Status", + "select_status": "Välj status", + "destination": "Destination", + "no_destination": "Ingen destination", + "note": "Anteckning", + "note_placeholder": "Lägg till en valfri anteckning...", + "update_status": "Uppdatera status", + "no_statuses_available": "Inga statusar tillgängliga", + "no_active_calls": "Inga aktiva ärenden", + "no_stations_available": "Inga stationer tillgängliga", + "no_destinations_available": "Inga destinationer tillgängliga" + }, + "call": "Ärende", + "station": "Station", + "calls": "Ärenden", + "stations": "Stationer", + "no_stations_available": "Inga stationer tillgängliga", + "new_call": "Nytt ärende", + "view_details": "Detaljer", + "add_note": "Lägg till anteckning", + "close_call": "Stäng", + "set_status": "Ange status", + "set_staffing": "Bemanning", + "dispatch": "Larma", + "select_items_for_actions": "Välj ett ärende, enhet eller personal för att aktivera kontextmedvetna åtgärder", + "weather": { + "clear": "Klart", + "mainly_clear": "Mestadels klart", + "partly_cloudy": "Delvis molnigt", + "overcast": "Mulet", + "fog": "Dimma", + "drizzle": "Duggregn", + "freezing_drizzle": "Underkylt duggregn", + "rain": "Regn", + "freezing_rain": "Underkylt regn", + "snow": "Snö", + "rain_showers": "Regnskurar", + "snow_showers": "Snöbyar", + "thunderstorm": "Åskväder", + "thunderstorm_hail": "Åskväder med hagel", + "unknown": "Okänt" + } + }, + "tabs": { + "calls": "Ärenden", + "calendar": "Kalender", + "contacts": "Kontakter", + "home": "Hem", + "map": "Karta", + "messages": "Meddelanden", + "notes": "Anteckningar", + "protocols": "Protokoll", + "settings": "Inställningar", + "shifts": "Skift", + "personnel": "Personal" + }, + "check_in": { + "tab_title": "Incheckning", + "timer_status": "Timerstatus", + "perform_check_in": "Checka in", + "check_in_success": "Incheckning registrerad", + "check_in_error": "Kunde inte registrera incheckning", + "checked_in_by": "av {{name}}", + "last_check_in": "Senaste incheckning", + "elapsed": "Förfluten tid", + "duration": "Varaktighet", + "status_ok": "OK", + "status_green": "OK", + "status_warning": "Varning", + "status_yellow": "Varning", + "status_overdue": "Försenad", + "status_red": "Försenad", + "status_critical": "Kritisk", + "history": "Incheckningshistorik", + "no_timers": "Inga incheckningstimrar konfigurerade", + "timers_disabled": "Incheckningstimrar är inaktiverade för detta ärende", + "type_personnel": "Personal", + "type_unit": "Enhet", + "type_ic": "Insatsledare", + "type_par": "PAR", + "type_hazmat": "Farligt gods-exponering", + "type_sector_rotation": "Sektorsrotation", + "type_rehab": "Rehab", + "add_note": "Lägg till anteckning (valfritt)", + "confirm": "Bekräfta incheckning", + "minutes_ago": "{{count}} min sedan", + "select_target": "Välj enhet att checka in", + "overdue_count": "{{count}} försenade", + "warning_count": "{{count}} varning", + "enable_timers": "Aktivera timrar", + "disable_timers": "Inaktivera timrar", + "summary": "{{overdue}} försenade, {{warning}} varning, {{ok}} ok" + }, + "units": { + "title": "Enheter" + }, + "weatherAlerts": { + "title": "Vädervarningar", + "activeAlerts": "Aktiva varningar", + "noActiveAlerts": "Inga aktiva vädervarningar", + "moreAlerts": "+{{count}} fler", + "severity": { + "extreme": "Extrem", + "severe": "Allvarlig", + "moderate": "Måttlig", + "minor": "Mindre", + "unknown": "Okänd" + }, + "category": { + "met": "Meteorologisk", + "fire": "Brand", + "health": "Hälsa", + "env": "Miljö", + "other": "Övrigt" + }, + "urgency": { + "immediate": "Omedelbar", + "expected": "Förväntad", + "future": "Framtida", + "past": "Passerad", + "unknown": "Okänd" + }, + "certainty": { + "observed": "Observerad", + "likely": "Trolig", + "possible": "Möjlig", + "unlikely": "Osannolik", + "unknown": "Okänd" + }, + "status": { + "active": "Aktiv", + "updated": "Uppdaterad", + "expired": "Utgången", + "cancelled": "Avbruten" + }, + "detail": { + "headline": "Rubrik", + "description": "Beskrivning", + "instruction": "Instruktioner", + "area": "Drabbat område", + "effective": "Gällande", + "onset": "Början", + "expires": "Utgår", + "sent": "Skickad", + "sender": "Källa", + "urgency": "Brådskande", + "certainty": "Säkerhet" + }, + "filter": { + "all": "Alla", + "nearby": "Nära" + }, + "sort": { + "severity": "Allvarlighetsgrad", + "expires": "Utgår snart", + "newest": "Nyaste" + }, + "banner": { + "viewAll": "Visa alla" + } + }, + "videoFeeds": { + "title": "Videoflöden", + "noFeeds": "Inga videoflöden för detta ärende", + "addFeed": "Lägg till videoflöde", + "editFeed": "Redigera videoflöde", + "deleteFeed": "Radera videoflöde", + "deleteConfirm": "Är du säker på att du vill ta bort detta videoflöde?", + "watch": "Titta", + "goLive": "Gå live", + "stopLive": "Stoppa live", + "flipCamera": "Vänd kamera", + "feedAdded": "Videoflöde tillagt", + "feedUpdated": "Videoflöde uppdaterat", + "feedDeleted": "Videoflöde borttaget", + "feedError": "Kunde inte ladda videoflöde", + "unsupportedFormat": "Detta strömformat stöds inte på mobil", + "copyUrl": "Kopiera URL", + "form": { + "name": "Flödesnamn", + "namePlaceholder": "t.ex. Släckbil 1 Drönare", + "url": "Ström-URL", + "urlPlaceholder": "t.ex. https://stream.example.com/live.m3u8", + "feedType": "Kameratyp", + "feedFormat": "Strömformat", + "description": "Beskrivning", + "descriptionPlaceholder": "Valfri beskrivning", + "status": "Status", + "sortOrder": "Sorteringsordning", + "cameraLocation": "Kameraplats", + "useCurrentLocation": "Använd nuvarande plats" + }, + "type": { + "drone": "Drönare", + "fixedCamera": "Fast kamera", + "bodyCam": "Kroppskamera", + "trafficCam": "Trafikkamera", + "weatherCam": "Väderkamera", + "satelliteFeed": "Satellitflöde", + "webCam": "Webbkamera", + "other": "Övrigt" + }, + "format": { + "rtsp": "RTSP", + "hls": "HLS", + "mjpeg": "MJPEG", + "youtubeLive": "YouTube Live", + "webrtc": "WebRTC", + "dash": "DASH", + "embed": "Inbäddad", + "other": "Övrigt" + }, + "status": { + "active": "Aktiv", + "inactive": "Inaktiv", + "error": "Fel" + } + }, + "welcome": "Välkommen till obytes app-webbplats" +} diff --git a/src/translations/uk.json b/src/translations/uk.json new file mode 100644 index 0000000..edddceb --- /dev/null +++ b/src/translations/uk.json @@ -0,0 +1,1151 @@ +{ + "app": { + "title": "Resgrid Dispatch" + }, + "audio_streams": { + "buffering": "Буферизація", + "buffering_stream": "Буферизація аудіопотоку...", + "close": "Закрити", + "currently_playing": "Зараз відтворюється: {{streamName}}", + "loading": "Завантаження", + "loading_stream": "Завантаження аудіопотоку...", + "loading_streams": "Завантаження аудіопотоків...", + "name": "Назва", + "no_stream_playing": "Наразі жоден аудіопотік не відтворюється", + "none": "Немає", + "playing": "Відтворення", + "refresh_streams": "Оновити потоки", + "select_placeholder": "Виберіть аудіопотік", + "select_stream": "Вибрати аудіопотік", + "status": "Статус", + "stopped": "Зупинено", + "stream_info": "Інформація про потік", + "stream_selected": "Вибрано: {{streamName}}", + "title": "Аудіопотоки", + "type": "Тип" + }, + "bluetooth": { + "applied": "Застосовано", + "audio": "Аудіо", + "audioActive": "Аудіо активне", + "audio_device": "BT-гарнітура", + "availableDevices": "Доступні пристрої", + "available_devices": "Доступні пристрої", + "bluetooth_not_ready": "Bluetooth у стані {{state}}. Будь ласка, увімкніть Bluetooth.", + "buttonControlAvailable": "Керування кнопками доступне", + "checking": "Перевірка стану Bluetooth...", + "clear": "Очистити", + "connect": "Підключити", + "connected": "Підключено", + "connectionError": "Помилка підключення", + "current_selection": "Поточний вибір", + "disconnect": "Відключити", + "doublePress": "Подвійне ", + "liveKitActive": "LiveKit активний", + "longPress": "Довге ", + "micControl": "Керування мікрофоном", + "mute": "Вимкнути звук", + "noDevicesFound": "Аудіопристрої не знайдено", + "noDevicesFoundRetry": "Аудіопристрої не знайдено. Спробуйте сканувати знову.", + "no_device_selected": "Пристрій не вибрано", + "no_devices_found": "Bluetooth-аудіопристрої не знайдено", + "not_connected": "Не підключено", + "poweredOff": "Bluetooth вимкнено. Будь ласка, увімкніть Bluetooth для підключення аудіопристроїв.", + "pttStart": "PTT Старт", + "pttStop": "PTT Стоп", + "recentButtonEvents": "Останні події кнопок", + "scan": "Сканувати", + "scanAgain": "Сканувати знову", + "scan_again": "Сканувати знову", + "scan_error_message": "Неможливо сканувати Bluetooth-пристрої", + "scan_error_title": "Помилка сканування", + "scanning": "Сканування...", + "select_device": "Вибрати Bluetooth-пристрій", + "selected": "Вибрано", + "selection_error_message": "Неможливо зберегти бажаний пристрій", + "selection_error_title": "Помилка вибору", + "startScanning": "Почати сканування", + "stopScan": "Зупинити сканування", + "supports_mic_control": "Керування мікрофоном", + "tap_scan_to_find_devices": "Натисніть «Сканувати» для пошуку Bluetooth-аудіопристроїв", + "title": "Bluetooth-аудіо", + "unauthorized": "Дозвіл на Bluetooth відхилено. Будь ласка, надайте дозвіл на Bluetooth у Налаштуваннях.", + "unknown": "Невідомо", + "unknownDevice": "Невідомий пристрій", + "unknown_device": "Невідомий пристрій", + "unmute": "Увімкнути звук", + "volumeDown": "Гучність -", + "volumeUp": "Гучність +" + }, + "callImages": { + "add": "Додати зображення", + "add_new": "Додати нове зображення", + "default_name": "Зображення без назви", + "error": "Помилка отримання зображень", + "failed_to_load": "Не вдалося завантажити зображення", + "image_alt": "Зображення виклику", + "image_name": "Назва зображення", + "image_note": "Примітка до зображення", + "loading": "Завантаження...", + "no_images": "Зображення відсутні", + "no_images_description": "Додайте зображення до виклику для покращення документації та комунікації", + "select_from_gallery": "Вибрати з галереї", + "take_photo": "Зробити фото", + "title": "Зображення виклику", + "upload": "Завантажити" + }, + "callNotes": { + "addNote": "Додати нотатку", + "addNotePlaceholder": "Додати нову нотатку...", + "noNotes": "Нотатки для цього виклику відсутні", + "noSearchResults": "Нотатки не відповідають вашому пошуку", + "searchPlaceholder": "Пошук нотаток...", + "title": "Нотатки виклику" + }, + "call_detail": { + "address": "Адреса", + "call_location": "Місце виклику", + "close_call": "Закрити виклик", + "close_call_confirmation": "Ви впевнені, що хочете закрити цей виклик?", + "close_call_error": "Не вдалося закрити виклик", + "close_call_note": "Примітка закриття", + "close_call_note_placeholder": "Введіть примітку про закриття виклику", + "close_call_success": "Виклик успішно закрито", + "close_call_type": "Тип закриття", + "close_call_type_placeholder": "Виберіть тип закриття", + "close_call_type_required": "Будь ласка, виберіть тип закриття", + "close_call_types": { + "cancelled": "Скасовано", + "closed": "Закрито", + "false_alarm": "Хибна тривога", + "founded": "Підтверджено", + "minor": "Незначний", + "transferred": "Передано", + "unfounded": "Непідтверджено" + }, + "contact_email": "Електронна пошта", + "contact_info": "Контактна інформація", + "contact_name": "Контактна особа", + "contact_phone": "Телефон", + "edit_call": "Редагувати виклик", + "external_id": "Зовнішній ID", + "failed_to_open_maps": "Не вдалося відкрити картографічний додаток", + "files": { + "add_file": "Додати файл", + "button": "Файли", + "empty": "Файли відсутні", + "empty_description": "Додайте файли до виклику для покращення документації та комунікації", + "error": "Помилка отримання файлів", + "file_name": "Назва файлу", + "name_required": "Будь ласка, введіть назву файлу", + "no_files": "Файли відсутні", + "no_files_description": "Додайте файли до виклику для покращення документації та комунікації", + "open_error": "Помилка відкриття файлу", + "select_error": "Помилка вибору файлу", + "select_file": "Вибрати файл", + "share_error": "Помилка при спільному доступі до файлу", + "title": "Файли виклику", + "upload": "Завантажити", + "upload_error": "Помилка завантаження файлу", + "uploading": "Завантаження..." + }, + "group": "Група", + "images": "Зображення", + "loading": "Завантаження деталей виклику...", + "nature": "Характер", + "no_additional_info": "Додаткова інформація відсутня", + "no_contact_info": "Контактна інформація відсутня", + "no_dispatched": "До цього виклику не диспетчеризовано жодного підрозділу", + "no_location": "Дані про місцезнаходження відсутні", + "no_location_for_routing": "Дані про місцезнаходження для маршрутизації відсутні", + "no_protocols": "До цього виклику не прикріплено протоколів", + "no_timeline": "Події хронології відсутні", + "not_available": "Н/Д", + "not_found": "Виклик не знайдено", + "missing_call_id": "Відсутній ID виклику", + "note": "Нотатка", + "notes": "Нотатки", + "priority": "Пріоритет", + "reference_id": "Референсний ID", + "set_active": "Зробити активним", + "set_active_error": "Не вдалося зробити виклик активним", + "set_active_success": "Виклик встановлено як активний", + "setting_active": "Встановлення активним...", + "status": "Статус", + "tabs": { + "contact": "Контакт", + "dispatched": "Диспетчеризовано", + "info": "Інфо", + "protocols": "Протоколи", + "timeline": "Активність", + "video": "Відео" + }, + "timestamp": "Часова мітка", + "title": "Деталі виклику", + "type": "Тип", + "unit": "Підрозділ", + "update_call_error": "Не вдалося оновити виклик", + "update_call_success": "Виклик успішно оновлено" + }, + "calls": { + "address": "Адреса", + "address_found": "Адресу знайдено та місцезнаходження оновлено", + "address_not_found": "Адресу не знайдено, спробуйте іншу адресу", + "address_placeholder": "Введіть адресу виклику", + "address_required": "Будь ласка, введіть адресу для пошуку", + "call_details": "Деталі виклику", + "call_location": "Місце виклику", + "call_number": "Номер виклику", + "call_priority": "Пріоритет виклику", + "confirm_deselect_message": "Ви впевнені, що хочете скасувати вибір поточного активного виклику?", + "confirm_deselect_title": "Скасувати вибір активного виклику", + "contact_info": "Контактна інформація", + "contact_info_placeholder": "Введіть інформацію контактної особи", + "contact_name": "Контактна особа", + "contact_name_placeholder": "Введіть ім'я контактної особи", + "contact_phone": "Телефон контактної особи", + "contact_phone_placeholder": "Введіть телефон контактної особи", + "coordinates": "GPS-координати", + "coordinates_found": "Координати знайдено та адресу оновлено", + "coordinates_geocoding_error": "Не вдалося отримати адресу за координатами, але місцезнаходження встановлено на карті", + "coordinates_invalid_format": "Невірний формат координат. Використовуйте формат: широта, довгота", + "coordinates_no_address": "Координати встановлено на карті, але адресу не знайдено", + "coordinates_out_of_range": "Координати поза діапазоном. Широта має бути від -90 до 90, довгота від -180 до 180", + "coordinates_placeholder": "Введіть GPS-координати (напр., 37.7749, -122.4194)", + "coordinates_required": "Будь ласка, введіть координати для пошуку", + "create": "Створити", + "create_error": "Помилка створення виклику", + "create_new_call": "Створити новий виклик", + "create_success": "Виклик успішно створено", + "description": "Опис", + "description_placeholder": "Введіть опис виклику", + "deselect": "Скасувати вибір", + "directions": "Маршрут", + "dispatch_to": "Диспетчеризувати до", + "dispatch_to_everyone": "Диспетчеризувати всьому наявному персоналу", + "edit_call": "Редагувати виклик", + "edit_call_description": "Оновити інформацію про виклик", + "everyone": "Усі", + "files": { + "no_files": "Файли відсутні", + "no_files_description": "До цього виклику ще не додано файлів", + "title": "Файли виклику" + }, + "geocoding_error": "Не вдалося здійснити пошук адреси, спробуйте ще раз", + "groups": "Групи", + "invalid_priority": "Обрано невірний пріоритет. Будь ласка, виберіть дійсний пріоритет.", + "invalid_type": "Обрано невірний тип. Будь ласка, виберіть дійсний тип виклику.", + "loading": "Завантаження викликів...", + "loading_calls": "Завантаження викликів...", + "name": "Назва", + "name_placeholder": "Введіть назву виклику", + "nature": "Характер", + "nature_placeholder": "Введіть характер виклику", + "new_call": "Новий виклик", + "new_call_description": "Створіть новий виклик для початку нового інциденту", + "no_call_selected": "Немає активного виклику", + "no_call_selected_info": "Цей підрозділ наразі не реагує на жодний виклик", + "no_calls": "Немає активних викликів", + "no_calls_available": "Виклики відсутні", + "no_calls_description": "Активних викликів не знайдено. Виберіть активний виклик для перегляду деталей.", + "no_location_message": "Цей виклик не має даних про місцезнаходження для навігації.", + "no_location_title": "Місцезнаходження недоступне", + "no_open_calls": "Немає відкритих викликів", + "note": "Нотатка", + "note_placeholder": "Введіть нотатку до виклику", + "plus_code": "Plus Code", + "plus_code_found": "Plus Code знайдено та місцезнаходження оновлено", + "plus_code_geocoding_error": "Не вдалося здійснити пошук за Plus Code, спробуйте ще раз", + "plus_code_not_found": "Plus Code не знайдено, спробуйте інший Plus Code", + "plus_code_placeholder": "Введіть Plus Code (напр., 849VCWC8+R9)", + "plus_code_required": "Будь ласка, введіть Plus Code для пошуку", + "priority": "Пріоритет", + "priority_placeholder": "Виберіть пріоритет виклику", + "roles": "Ролі", + "search": "Пошук викликів...", + "select_active_call": "Вибрати активний виклик", + "select_address": "Вибрати адресу", + "select_address_placeholder": "Виберіть адресу виклику", + "select_description": "Вибрати опис", + "select_dispatch_recipients": "Вибрати отримувачів диспетчеризації", + "select_location": "Вибрати місце на карті", + "select_name": "Вибрати назву", + "select_nature": "Вибрати характер", + "select_nature_placeholder": "Виберіть характер виклику", + "select_priority": "Вибрати пріоритет", + "select_priority_placeholder": "Виберіть пріоритет виклику", + "select_recipients": "Вибрати отримувачів", + "select_type": "Вибрати тип", + "selected": "вибрано", + "title": "Виклики", + "type": "Тип", + "units": "Підрозділи", + "users": "Користувачі", + "viewNotes": "Нотатки", + "view_details": "Переглянути деталі", + "what3words": "what3words", + "what3words_found": "Адресу what3words знайдено та місцезнаходження оновлено", + "what3words_geocoding_error": "Не вдалося здійснити пошук за адресою what3words, спробуйте ще раз", + "what3words_invalid_format": "Невірний формат what3words. Використовуйте формат: слово.слово.слово", + "what3words_not_found": "Адресу what3words не знайдено, спробуйте іншу адресу", + "what3words_placeholder": "Введіть адресу what3words (напр., filled.count.soap)", + "what3words_required": "Будь ласка, введіть адресу what3words для пошуку", + "expand_map": "Розгорнути карту", + "schedule_dispatch": "Запланувати диспетчеризацію", + "schedule_dispatch_description": "Встановіть майбутню дату та час для планування цього виклику на пізніше", + "scheduled_on": "Запланована дата та час", + "scheduled_on_helper": "Залиште порожнім для негайної диспетчеризації", + "scheduled_on_past_error": "Запланований час має бути в майбутньому", + "contact_information": "Контактна інформація", + "new_call_web_hint": "Заповніть деталі виклику нижче. Натисніть Ctrl+Enter для створення.", + "edit_call_web_hint": "Оновіть деталі виклику нижче. Натисніть Ctrl+S для збереження.", + "keyboard_shortcuts": "Підказка: Натисніть Ctrl+Enter для створення, Escape для скасування", + "edit_keyboard_shortcuts": "Підказка: Натисніть Ctrl+S для збереження, Escape для скасування", + "templates": { + "title": "Шаблони викликів", + "select_template": "Вибрати шаблон", + "search_placeholder": "Пошук шаблонів...", + "none": "Шаблони відсутні", + "template_applied": "Шаблон застосовано" + }, + "form": { + "title": "Форма виклику", + "no_form": "Форма виклику не налаштована" + }, + "linked_calls": { + "title": "Пов'язаний виклик", + "select": "Зв'язати з існуючим викликом", + "change": "Змінити пов'язаний виклик", + "none": "Немає доступних активних викликів", + "search_placeholder": "Пошук викликів...", + "linked": "Пов'язано" + }, + "contact_picker": { + "title": "Вибрати контакт", + "search_placeholder": "Пошук контактів...", + "none": "Контакти відсутні" + }, + "protocols": { + "title": "Протоколи", + "select": "Вибрати протоколи", + "none": "Протоколи відсутні", + "selected_count": "вибрано", + "expand_questions": "Переглянути запитання", + "collapse": "Згорнути" + } + }, + "common": { + "add": "Додати", + "back": "Назад", + "cancel": "Скасувати", + "creating": "Створення...", + "saving": "Збереження...", + "unsaved_changes": "Незбережені зміни", + "close": "Закрити", + "confirm": "Підтвердити", + "confirm_location": "Підтвердити місцезнаходження", + "delete": "Видалити", + "dismiss": "Закрити", + "done": "Готово", + "edit": "Редагувати", + "error": "Помилка", + "errorOccurred": "Сталася помилка", + "get_my_location": "Визначити моє місцезнаходження", + "go_back": "Повернутися", + "loading": "Завантаження...", + "loading_address": "Завантаження адреси...", + "next": "Далі", + "noActiveUnit": "Активний підрозділ не встановлено", + "noActiveUnitDescription": "Будь ласка, встановіть активний підрозділ на сторінці налаштувань для доступу до керування статусами", + "noDataAvailable": "Дані відсутні", + "no_address_found": "Адресу не знайдено", + "no_location": "Дані про місцезнаходження відсутні", + "no_results_found": "Результатів не знайдено", + "no_unit_selected": "Підрозділ не вибрано", + "nothingToDisplay": "Наразі немає що відображати", + "of": "з", + "ok": "Ок", + "optional": "необов'язково", + "permission_denied": "Доступ заборонено", + "previous": "Попередній", + "remove": "Видалити", + "retry": "Повторити", + "route": "Маршрут", + "save": "Зберегти", + "search": "Пошук...", + "set_location": "Встановити місцезнаходження", + "share": "Поділитися", + "step": "Крок", + "submit": "Надіслати", + "submitting": "Надсилання...", + "tryAgainLater": "Будь ласка, спробуйте пізніше", + "unknown": "Невідомо", + "unknown_department": "Невідомий підрозділ", + "unknown_user": "Невідомий користувач", + "upload": "Завантажити", + "uploading": "Завантаження..." + }, + "contacts": { + "add": "Додати контакт", + "addedBy": "Додав", + "addedOn": "Додано", + "additionalInformation": "Додаткова інформація", + "address": "Адреса", + "bluesky": "Bluesky", + "cancel": "Скасувати", + "cellPhone": "Мобільний телефон", + "city": "Місто", + "cityState": "Місто та область", + "cityStateZip": "Місто, область, поштовий індекс", + "company": "Компанія", + "contactInformation": "Контактна інформація", + "contactNotes": "Нотатки контакту", + "contactNotesEmpty": "Нотатки для цього контакту не знайдено", + "contactNotesEmptyDescription": "Нотатки, додані до цього контакту, з'являться тут", + "contactNotesExpired": "Термін дії цієї нотатки закінчився", + "contactNotesLoading": "Завантаження нотаток контакту...", + "contactType": "Тип контакту", + "countryId": "ID країни", + "delete": "Видалити", + "deleteConfirm": "Ви впевнені, що хочете видалити цей контакт?", + "deleteSuccess": "Контакт успішно видалено", + "description": "Додавайте та керуйте вашими контактами", + "details": "Деталі контакту", + "detailsTab": "Деталі", + "edit": "Редагувати контакт", + "editedBy": "Відредагував", + "editedOn": "Відредаговано", + "email": "Електронна пошта", + "empty": "Контакти не знайдено", + "emptyDescription": "Додайте контакти для керування особистими та діловими зв'язками", + "entranceCoordinates": "Координати входу", + "exitCoordinates": "Координати виходу", + "expires": "Закінчується", + "facebook": "Facebook", + "faxPhone": "Факс", + "formError": "Будь ласка, виправте помилки у формі", + "homePhone": "Домашній телефон", + "identification": "Ідентифікація", + "important": "Позначити як важливий", + "instagram": "Instagram", + "internal": "Внутрішній", + "invalidEmail": "Невірна електронна адреса", + "linkedin": "LinkedIn", + "locationCoordinates": "Координати місцезнаходження", + "locationInformation": "Інформація про місцезнаходження", + "mastodon": "Mastodon", + "mobile": "Мобільний", + "name": "Ім'я", + "noteAlert": "Сповіщення", + "noteType": "Тип", + "notes": "Нотатки", + "notesTab": "Нотатки", + "officePhone": "Робочий телефон", + "otherInfo": "Інша інформація", + "person": "Особа", + "phone": "Телефон", + "public": "Публічний", + "required": "Обов'язково", + "save": "Зберегти контакт", + "saveSuccess": "Контакт успішно збережено", + "search": "Пошук контактів...", + "shouldAlert": "Сповіщати", + "socialMediaWeb": "Соціальні мережі та веб", + "state": "Область", + "stateId": "ID області", + "systemInformation": "Системна інформація", + "tabs": { + "details": "Деталі", + "notes": "Нотатки" + }, + "threads": "Threads", + "title": "Контакти", + "twitter": "Twitter", + "visibility": "Видимість", + "website": "Вебсайт", + "zip": "Поштовий індекс" + }, + "form": { + "invalid_url": "Будь ласка, введіть дійсну URL-адресу, що починається з http:// або https://", + "required": "Це поле є обов'язковим" + }, + "livekit": { + "audio_devices": "Аудіопристрої", + "audio_settings": "Налаштування аудіо", + "connected_to_room": "Підключено до каналу", + "connecting": "Підключення...", + "disconnect": "Відключити", + "join": "Приєднатися", + "microphone": "Мікрофон", + "mute": "Вимкнути звук", + "no_rooms_available": "Голосові канали недоступні", + "speaker": "Динамік", + "speaking": "Говорить", + "title": "Голосові канали", + "unmute": "Увімкнути звук" + }, + "loading": { + "loading": "Завантаження...", + "loadingData": "Завантаження даних...", + "pleaseWait": "Будь ласка, зачекайте", + "processingRequest": "Обробка вашого запиту..." + }, + "sso": { + "authenticating": "Автентифікація...", + "back_to_login": "Повернутися до входу", + "back_to_lookup": "Змінити користувача", + "continue_button": "Продовжити", + "department_id_label": "ID підрозділу", + "department_id_placeholder": "Введіть ID підрозділу", + "error_generic": "Не вдалося увійти. Будь ласка, спробуйте ще раз.", + "error_oidc_cancelled": "Вхід було скасовано.", + "error_oidc_not_ready": "SSO-провайдер завантажується, будь ласка, зачекайте.", + "error_sso_not_enabled": "Єдиний вхід не увімкнено для цього користувача.", + "error_token_exchange": "Не вдалося завершити вхід. Будь ласка, спробуйте ще раз.", + "error_user_not_found": "Користувача не знайдено. Будь ласка, перевірте та спробуйте ще раз.", + "looking_up": "Пошук...", + "optional": "необов'язково", + "page_subtitle": "Введіть ваше ім'я користувача для пошуку параметрів входу вашої організації.", + "page_title": "Єдиний вхід (SSO)", + "provider_oidc": "OpenID Connect (OIDC)", + "provider_saml": "SAML 2.0", + "sign_in_button": "Увійти через SSO", + "sign_in_title": "Вхід", + "sso_button": "SSO Вхід" + }, + "login": { + "branding_subtitle": "Потужне диспетчерське програмне забезпечення для служб екстреної допомоги, пошуково-рятувальних служб та організацій громадської безпеки.", + "branding_title": "Управління екстреним реагуванням", + "errorModal": { + "confirmButton": "OK", + "message": "Будь ласка, перевірте ваше ім'я користувача та пароль і спробуйте ще раз.", + "title": "Помилка входу" + }, + "feature_dispatch_desc": "Миттєво диспетчеризуйте підрозділи та керуйте викликами з оновленнями в реальному часі на всіх пристроях.", + "feature_dispatch_title": "Диспетчеризація в реальному часі", + "feature_mapping_desc": "Відстежуйте підрозділи в реальному часі з детальними картами, маршрутизацією та керуванням місцезнаходженням.", + "feature_mapping_title": "Розширена картографія", + "feature_personnel_desc": "Керуйте вашою командою з рольовим доступом, відстеженням статусів та засобами зв'язку.", + "feature_personnel_title": "Управління персоналом", + "footer_text": "Створено з ❤️ на озері Тахо", + "login": "Вхід", + "login_button": "Увійти", + "login_button_description": "Увійдіть до свого облікового запису для продовження", + "login_button_error": "Помилка входу", + "login_button_loading": "Вхід...", + "login_button_success": "Успішний вхід", + "no_account": "Немає облікового запису?", + "page_subtitle": "Введіть ваші облікові дані для входу.", + "page_title": "Resgrid Dispatch", + "password": "Пароль", + "password_incorrect": "Невірний пароль", + "password_placeholder": "Введіть ваш пароль", + "register": "Реєстрація", + "title": "Вхід", + "username": "Ім'я користувача", + "username_placeholder": "Введіть ваше ім'я користувача", + "welcome_title": "З поверненням" + }, + "lockscreen": { + "message": "Введіть ваш пароль для розблокування екрана", + "not_you": "Це не ви? Повернутися до входу", + "password": "Пароль", + "password_placeholder": "Введіть ваш пароль", + "title": "Екран блокування", + "unlock_button": "Розблокувати", + "unlock_failed": "Не вдалося розблокувати. Будь ласка, спробуйте ще раз.", + "unlocking": "Розблокування...", + "welcome_back": "З поверненням" + }, + "maintenance": { + "downtime_message": "Ми працюємо над якнайшвидшим завершенням технічних робіт. Будь ласка, перевірте пізніше.", + "downtime_title": "Який час простою?", + "message": "Будь ласка, перевірте пізніше.", + "support_message": "Якщо вам потрібна допомога, зв'яжіться з нами за адресою", + "support_title": "Потрібна підтримка?", + "title": "Сайт на технічному обслуговуванні", + "why_down_message": "Ми проводимо планове технічне обслуговування для покращення вашого досвіду. Просимо вибачення за незручності.", + "why_down_title": "Чому сайт недоступний?" + }, + "menu": { + "calls": "Виклики", + "calls_list": "Список викликів", + "contacts": "Контакти", + "home": "Головна", + "map": "Карта", + "menu": "Меню", + "messages": "Повідомлення", + "new_call": "Новий виклик", + "personnel": "Персонал", + "protocols": "Протоколи", + "settings": "Налаштування", + "units": "Підрозділи", + "weatherAlerts": "Погодні попередження" + }, + "map": { + "call_set_as_current": "Виклик встановлено як поточний", + "failed_to_open_maps": "Не вдалося відкрити картографічний додаток", + "failed_to_set_current_call": "Не вдалося встановити виклик як поточний", + "layers": "Шари карти", + "no_layers": "Шари недоступні", + "no_location_for_routing": "Дані про місцезнаходження для маршрутизації відсутні", + "pin_color": "Колір маркера", + "recenter_map": "Перецентрувати карту", + "set_as_current_call": "Встановити як поточний виклик", + "show_all": "Показати все", + "hide_all": "Сховати все", + "view_call_details": "Переглянути деталі виклику" + }, + "notes": { + "actions": { + "add": "Додати нотатку", + "delete_confirm": "Ви впевнені, що хочете видалити цю нотатку?" + }, + "details": { + "close": "Закрити", + "created": "Створено", + "delete": "Видалити", + "edit": "Редагувати", + "tags": "Теги", + "title": "Деталі нотатки", + "updated": "Оновлено" + }, + "empty": "Нотатки не знайдено", + "emptyDescription": "Для вашого підрозділу ще не створено жодної нотатки.", + "search": "Пошук нотаток...", + "title": "Нотатки" + }, + "personnel": { + "title": "Персонал", + "search": "Пошук персоналу...", + "loading": "Завантаження персоналу...", + "empty": "Персонал не знайдено", + "empty_description": "У вашому відділі наразі немає доступного персоналу.", + "no_results": "Жоден персонал не відповідає вашому пошуку", + "no_results_description": "Спробуйте змінити пошукові терміни.", + "status": "Статус", + "staffing": "Укомплектування", + "group": "Група", + "roles": "Ролі", + "email": "Електронна пошта", + "phone": "Телефон", + "id_number": "Ідентифікаційний номер", + "responding_to": "Відповідає на", + "status_updated": "Статус оновлено", + "staffing_updated": "Укомплектування оновлено", + "details": "Деталі персоналу", + "contact_info": "Контактна інформація", + "status_info": "Статус та укомплектування", + "no_email": "Електронна пошта недоступна", + "no_phone": "Телефон недоступний", + "no_group": "Не призначено", + "no_roles": "Ролі не призначені", + "unknown_status": "Невідомо", + "on_duty": "На службі", + "off_duty": "Не на службі", + "call_phone": "Зателефонувати", + "send_email": "Електронна пошта", + "custom_fields": "Додаткова інформація" + }, + "onboarding": { + "screen1": { + "title": "Resgrid Dispatch", + "description": "Створюйте, диспетчеризуйте та керуйте екстреними викликами за допомогою потужного мобільного командного центру" + }, + "screen2": { + "title": "Ситуаційна обізнаність у реальному часі", + "description": "Відстежуйте всі підрозділи, персонал та ресурси на інтерактивній карті з оновленнями статусу в реальному часі та AVL" + }, + "screen3": { + "title": "Безперебійна координація", + "description": "Миттєво зв'язуйтеся з польовими підрозділами, оновлюйте статуси викликів та координуйте реагування з будь-якого місця" + }, + "skip": "Пропустити", + "next": "Далі", + "getStarted": "Розпочати" + }, + "protocols": { + "details": { + "close": "Закрити", + "code": "Код", + "created": "Створено", + "title": "Деталі протоколу", + "updated": "Оновлено" + }, + "empty": "Протоколи не знайдено", + "emptyDescription": "Для вашого підрозділу ще не створено жодного протоколу.", + "search": "Пошук протоколів...", + "title": "Протоколи" + }, + "push_notifications": { + "close": "Закрити", + "message": "Повідомлення", + "new_notification": "Нове сповіщення", + "title": "Заголовок", + "types": { + "call": "Виклик", + "chat": "Чат", + "group_chat": "Груповий чат", + "message": "Повідомлення", + "notification": "Сповіщення" + }, + "unknown_type_warning": "Отримано сповіщення невідомого типу", + "view_call": "Переглянути виклик", + "view_message": "Переглянути повідомлення" + }, + "roles": { + "modal": { + "title": "Призначення ролей підрозділу" + }, + "selectUser": "Вибрати користувача", + "status": "{{active}} з {{total}} ролей", + "tap_to_manage": "Натисніть для керування ролями", + "unassigned": "Не призначено" + }, + "settings": { + "about": "Про додаток", + "account": "Обліковий запис", + "active_unit": "Активний підрозділ", + "app_info": "Інформація про додаток", + "app_name": "Назва додатку", + "arabic": "Арабська", + "audio_device_selection": { + "bluetooth_device": "Bluetooth-пристрій", + "current_selection": "Поточний вибір", + "microphone": "Мікрофон", + "no_microphones_available": "Мікрофони недоступні", + "no_speakers_available": "Динаміки недоступні", + "none_selected": "Не вибрано", + "speaker": "Динамік", + "speaker_device": "Пристрій динаміка", + "title": "Вибір аудіопристрою", + "unavailable": "Недоступно", + "wired_device": "Дротовий пристрій" + }, + "background_geolocation": "Фонова геолокація", + "background_geolocation_warning": "Ця функція дозволяє додатку відстежувати ваше місцезнаходження у фоновому режимі. Це допомагає з координацією екстреного реагування, але може вплинути на час роботи батареї.", + "background_location": "Фонове місцезнаходження", + "contact_us": "Зв'язатися з нами", + "current_unit": "Поточний підрозділ", + "english": "Англійська", + "french": "Французька", + "german": "Німецька", + "italian": "Італійська", + "enter_password": "Введіть ваш пароль", + "enter_server_url": "Введіть URL API Resgrid (напр., https://api.resgrid.com)", + "enter_username": "Введіть ваше ім'я користувача", + "environment": "Середовище", + "general": "Загальне", + "generale": "Загальне", + "github": "Github", + "help_center": "Центр допомоги", + "keep_alive": "Підтримка активності", + "keep_alive_warning": "Увага: увімкнення підтримки активності запобігає переходу пристрою в режим сну та може значно збільшити споживання батареї.", + "keep_screen_on": "Тримати екран увімкненим", + "language": "Мова", + "links": "Посилання", + "login_info": "Інформація для входу", + "logout": "Вийти", + "more": "Більше", + "no_units_available": "Підрозділи недоступні", + "none_selected": "Не вибрано", + "notifications": "Push-сповіщення", + "notifications_description": "Увімкніть сповіщення для отримання оповіщень та оновлень", + "notifications_enable": "Увімкнути сповіщення", + "password": "Пароль", + "preferences": "Налаштування", + "privacy": "Політика конфіденційності", + "privacy_policy": "Політика конфіденційності", + "rate": "Оцінити", + "select_unit": "Вибрати підрозділ", + "server": "Сервер", + "server_url": "URL сервера", + "server_url_note": "Примітка: Це URL API Resgrid. Він використовується для підключення до сервера Resgrid. Не додавайте /api/v4 до URL або косу риску в кінці.", + "set_active_unit": "Встановити активний підрозділ", + "share": "Поділитися", + "polish": "Польська", + "spanish": "Іспанська", + "swedish": "Шведська", + "ukrainian": "Українська", + "status_page": "Стан системи", + "support": "Підтримка", + "support_us": "Підтримайте нас", + "terms": "Умови використання", + "theme": { + "dark": "Темна", + "light": "Світла", + "system": "Системна", + "title": "Тема" + }, + "title": "Налаштування", + "unit_selected_successfully": "{{unitName}} успішно вибрано", + "unit_selection": "Вибір підрозділу", + "unit_selection_failed": "Не вдалося вибрати підрозділ. Будь ласка, спробуйте ще раз.", + "username": "Ім'я користувача", + "version": "Версія", + "website": "Вебсайт" + }, + "status": { + "add_note": "Додати нотатку", + "both_destinations_enabled": "Може реагувати на виклики або станції", + "call_destination_enabled": "Може реагувати на виклики", + "calls_tab": "Виклики", + "failed_to_save_status": "Не вдалося зберегти статус. Будь ласка, спробуйте ще раз.", + "general_status": "Загальний статус без конкретного призначення", + "loading_stations": "Завантаження станцій...", + "no_destination": "Без призначення", + "no_stations_available": "Станції недоступні", + "no_statuses_available": "Статуси недоступні", + "note": "Нотатка", + "note_optional": "Додайте необов'язкову нотатку для цього оновлення статусу", + "note_required": "Будь ласка, введіть нотатку для цього оновлення статусу", + "select_destination": "Виберіть призначення для {{status}}", + "select_destination_type": "Куди ви хочете прибути?", + "select_status": "Вибрати статус", + "select_status_type": "Який статус ви хочете встановити?", + "selected_destination": "Вибране призначення", + "selected_status": "Вибраний статус", + "set_status": "Встановити статус", + "station_destination_enabled": "Може реагувати на станції", + "stations_tab": "Станції", + "status_saved_successfully": "Статус успішно збережено!" + }, + "dispatch": { + "active_calls": "Активні виклики", + "pending_calls": "В очікуванні", + "scheduled_calls": "Заплановані", + "units_available": "Доступні", + "personnel_available": "Доступні", + "personnel_on_duty": "На чергуванні", + "units": "Підрозділи", + "personnel": "Персонал", + "map": "Карта", + "notes": "Нотатки", + "activity_log": "Журнал активності", + "communications": "Зв'язок", + "no_active_calls": "Немає активних викликів", + "no_units": "Підрозділи недоступні", + "no_personnel": "Персонал недоступний", + "no_notes": "Нотатки відсутні", + "no_activity": "Немає останньої активності", + "current_channel": "Поточний канал", + "audio_stream": "Аудіопотік", + "no_stream": "Немає активного потоку", + "ptt": "PTT", + "ptt_start": "PTT Старт", + "ptt_end": "PTT Кінець", + "transmitting_on": "Передача на {{channel}}", + "transmission_ended": "Передачу завершено", + "voice_disabled": "Голос вимкнено", + "disconnected": "Відключено", + "select_channel": "Вибрати канал", + "select_channel_description": "Виберіть голосовий канал для підключення", + "change_channel_warning": "Вибір нового каналу відключить від поточного", + "default_channel": "За замовчуванням", + "no_channels_available": "Голосові канали недоступні", + "system_update": "Системне оновлення", + "data_refreshed": "Дані оновлено з сервера", + "call_selected": "Виклик вибрано", + "unit_selected": "Підрозділ вибрано", + "unit_deselected": "Вибір підрозділу скасовано", + "personnel_selected": "Персонал вибрано", + "personnel_deselected": "Вибір персоналу скасовано", + "loading_map": "Завантаження карти...", + "map_not_available_web": "Карта недоступна на веб-платформі", + "filtering_by_call": "Фільтрація за викликом", + "clear_filter": "Очистити фільтр", + "call_filter_active": "Фільтр виклику активний", + "call_filter_cleared": "Фільтр виклику очищено", + "showing_all_data": "Показано всі дані", + "call_notes": "Нотатки виклику", + "no_call_notes": "Немає нотаток виклику", + "add_call_note_placeholder": "Додати нотатку...", + "note_added": "Нотатку додано", + "note_added_to_console": "Нову нотатку додано до консолі", + "add_note_title": "Додати нову нотатку", + "note_title_label": "Заголовок", + "note_title_placeholder": "Введіть заголовок нотатки...", + "note_category_label": "Категорія", + "note_category_placeholder": "Виберіть категорію", + "note_no_category": "Без категорії", + "note_body_label": "Зміст нотатки", + "note_body_placeholder": "Введіть зміст нотатки...", + "note_save_error": "Не вдалося зберегти нотатку: {{error}}", + "note_created": "Нотатку створено", + "units_on_call": "Підрозділи на виклику", + "no_units_on_call": "Немає підрозділів на виклику", + "personnel_on_call": "Персонал на виклику", + "no_personnel_on_call": "Немає персоналу на виклику", + "call_activity": "Активність виклику", + "no_call_activity": "Немає активності виклику", + "on_call": "На виклику", + "filtered": "Відфільтровано", + "active_filter": "Активний фільтр", + "unit_status_change": "Зміна статусу підрозділу", + "personnel_status_change": "Зміна статусу персоналу", + "view_call_details": "Переглянути деталі виклику", + "dispatched_resources": "Диспетчеризовано", + "unassigned": "Не призначено", + "available": "Доступний", + "unknown": "Невідомо", + "search_personnel_placeholder": "Пошук персоналу...", + "search_calls_placeholder": "Пошук викликів...", + "search_units_placeholder": "Пошук підрозділів...", + "search_notes_placeholder": "Пошук нотаток...", + "signalr_update": "Оновлення в реальному часі", + "signalr_connected": "Підключено", + "realtime_updates_active": "Оновлення в реальному часі тепер активні", + "personnel_status_updated": "Статус персоналу оновлено", + "personnel_staffing_updated": "Укомплектованість персоналу оновлено", + "unit_status_updated": "Статус підрозділу оновлено", + "calls_updated": "Виклики оновлено", + "call_added": "Новий виклик додано", + "call_closed": "Виклик закрито", + "check_ins": "Перевірки", + "no_check_ins": "Немає викликів з таймерами перевірки", + "radio_log": "Радіожурнал", + "radio": "Радіо", + "activity": "Активність", + "actions": "Дії", + "no_radio_activity": "Немає радіопередач", + "live": "НАЖИВО", + "currently_transmitting": "Зараз передає...", + "duration": "Тривалість", + "call_actions": "Дії з викликом", + "unit_actions": "Дії з підрозділом", + "personnel_actions": { + "title": "Дії з персоналом", + "status_tab": "Статус", + "staffing_tab": "Укомплектованість", + "select_status": "Вибрати статус", + "select_staffing": "Вибрати рівень укомплектованості", + "destination": "Призначення", + "no_destination": "Без призначення", + "note": "Нотатка", + "note_placeholder": "Додайте необов'язкову нотатку...", + "update_status": "Оновити статус", + "update_staffing": "Оновити укомплектованість", + "no_statuses_available": "Статуси недоступні", + "no_staffings_available": "Рівні укомплектованості недоступні" + }, + "unit_actions_panel": { + "status": "Статус", + "select_status": "Вибрати статус", + "destination": "Призначення", + "no_destination": "Без призначення", + "note": "Нотатка", + "note_placeholder": "Додайте необов'язкову нотатку...", + "update_status": "Оновити статус", + "no_statuses_available": "Статуси недоступні", + "no_active_calls": "Немає активних викликів", + "no_stations_available": "Станції недоступні", + "no_destinations_available": "Призначення недоступні" + }, + "call": "Виклик", + "station": "Станція", + "calls": "Виклики", + "stations": "Станції", + "no_stations_available": "Станції недоступні", + "new_call": "Новий виклик", + "view_details": "Деталі", + "add_note": "Додати нотатку", + "close_call": "Закрити", + "set_status": "Встановити статус", + "set_staffing": "Укомплектованість", + "dispatch": "Диспетчеризувати", + "select_items_for_actions": "Виберіть виклик, підрозділ або персонал для увімкнення контекстних дій", + "weather": { + "clear": "Ясно", + "mainly_clear": "Переважно ясно", + "partly_cloudy": "Мінлива хмарність", + "overcast": "Хмарно", + "fog": "Туман", + "drizzle": "Мряка", + "freezing_drizzle": "Крижана мряка", + "rain": "Дощ", + "freezing_rain": "Крижаний дощ", + "snow": "Сніг", + "rain_showers": "Зливи", + "snow_showers": "Снігопади", + "thunderstorm": "Гроза", + "thunderstorm_hail": "Гроза з градом", + "unknown": "Невідомо" + } + }, + "tabs": { + "calls": "Виклики", + "calendar": "Календар", + "contacts": "Контакти", + "home": "Головна", + "map": "Карта", + "messages": "Повідомлення", + "notes": "Нотатки", + "protocols": "Протоколи", + "settings": "Налаштування", + "shifts": "Зміни", + "personnel": "Персонал" + }, + "check_in": { + "tab_title": "Перевірка", + "timer_status": "Статус таймера", + "perform_check_in": "Відмітитися", + "check_in_success": "Перевірку успішно зафіксовано", + "check_in_error": "Не вдалося зафіксувати перевірку", + "checked_in_by": "від {{name}}", + "last_check_in": "Остання перевірка", + "elapsed": "Минуло", + "duration": "Тривалість", + "status_ok": "OK", + "status_green": "OK", + "status_warning": "Попередження", + "status_yellow": "Попередження", + "status_overdue": "Прострочено", + "status_red": "Прострочено", + "status_critical": "Критично", + "history": "Історія перевірок", + "no_timers": "Таймери перевірки не налаштовано", + "timers_disabled": "Таймери перевірки вимкнено для цього виклику", + "type_personnel": "Персонал", + "type_unit": "Підрозділ", + "type_ic": "Керівник інциденту", + "type_par": "PAR", + "type_hazmat": "Хімічне ураження", + "type_sector_rotation": "Ротація секторів", + "type_rehab": "Реабілітація", + "add_note": "Додати нотатку (необов'язково)", + "confirm": "Підтвердити перевірку", + "minutes_ago": "{{count}} хв тому", + "select_target": "Виберіть об'єкт для перевірки", + "overdue_count": "{{count}} прострочено", + "warning_count": "{{count}} попередження", + "enable_timers": "Увімкнути таймери", + "disable_timers": "Вимкнути таймери", + "summary": "{{overdue}} прострочено, {{warning}} попередження, {{ok}} ок" + }, + "units": { + "title": "Підрозділи" + }, + "weatherAlerts": { + "title": "Погодні попередження", + "activeAlerts": "Активні попередження", + "noActiveAlerts": "Немає активних погодних попереджень", + "moreAlerts": "+{{count}} ще", + "severity": { + "extreme": "Екстремальний", + "severe": "Серйозний", + "moderate": "Помірний", + "minor": "Незначний", + "unknown": "Невідомо" + }, + "category": { + "met": "Метеорологічний", + "fire": "Пожежа", + "health": "Здоров'я", + "env": "Екологічний", + "other": "Інше" + }, + "urgency": { + "immediate": "Негайно", + "expected": "Очікуваний", + "future": "Майбутній", + "past": "Минулий", + "unknown": "Невідомо" + }, + "certainty": { + "observed": "Спостережено", + "likely": "Ймовірно", + "possible": "Можливо", + "unlikely": "Малоймовірно", + "unknown": "Невідомо" + }, + "status": { + "active": "Активний", + "updated": "Оновлено", + "expired": "Закінчився", + "cancelled": "Скасовано" + }, + "detail": { + "headline": "Заголовок", + "description": "Опис", + "instruction": "Інструкції", + "area": "Зона ураження", + "effective": "Набирає чинності", + "onset": "Початок", + "expires": "Закінчується", + "sent": "Надіслано", + "sender": "Джерело", + "urgency": "Терміновість", + "certainty": "Достовірність" + }, + "filter": { + "all": "Усі", + "nearby": "Поблизу" + }, + "sort": { + "severity": "Серйозність", + "expires": "Скоро закінчується", + "newest": "Найновіші" + }, + "banner": { + "viewAll": "Переглянути все" + } + }, + "videoFeeds": { + "title": "Відеопотоки", + "noFeeds": "Немає відеопотоків для цього виклику", + "addFeed": "Додати відеопотік", + "editFeed": "Редагувати відеопотік", + "deleteFeed": "Видалити відеопотік", + "deleteConfirm": "Ви впевнені, що хочете видалити цей відеопотік?", + "watch": "Дивитися", + "goLive": "Почати трансляцію", + "stopLive": "Зупинити трансляцію", + "flipCamera": "Перемкнути камеру", + "feedAdded": "Відеопотік додано", + "feedUpdated": "Відеопотік оновлено", + "feedDeleted": "Відеопотік видалено", + "feedError": "Не вдалося завантажити відеопотік", + "unsupportedFormat": "Цей формат потоку не підтримується на мобільному пристрої", + "copyUrl": "Копіювати URL", + "form": { + "name": "Назва потоку", + "namePlaceholder": "напр., Дрон Пожежна машина 1", + "url": "URL потоку", + "urlPlaceholder": "напр., https://stream.example.com/live.m3u8", + "feedType": "Тип камери", + "feedFormat": "Формат потоку", + "description": "Опис", + "descriptionPlaceholder": "Необов'язковий опис", + "status": "Статус", + "sortOrder": "Порядок сортування", + "cameraLocation": "Розташування камери", + "useCurrentLocation": "Використати поточне місцезнаходження" + }, + "type": { + "drone": "Дрон", + "fixedCamera": "Стаціонарна камера", + "bodyCam": "Натільна камера", + "trafficCam": "Камера дорожнього руху", + "weatherCam": "Метеокамера", + "satelliteFeed": "Супутниковий потік", + "webCam": "Вебкамера", + "other": "Інше" + }, + "format": { + "rtsp": "RTSP", + "hls": "HLS", + "mjpeg": "MJPEG", + "youtubeLive": "YouTube Live", + "webrtc": "WebRTC", + "dash": "DASH", + "embed": "Вбудований", + "other": "Інше" + }, + "status": { + "active": "Активний", + "inactive": "Неактивний", + "error": "Помилка" + } + }, + "welcome": "Ласкаво просимо до додатку obytes" +} diff --git a/yarn.lock b/yarn.lock index 9abbf3d..faa32f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -46,11 +46,25 @@ js-tokens "^4.0.0" picocolors "^1.1.1" +"@babel/code-frame@^7.20.0", "@babel/code-frame@^7.28.6", "@babel/code-frame@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c" + integrity sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw== + dependencies: + "@babel/helper-validator-identifier" "^7.28.5" + js-tokens "^4.0.0" + picocolors "^1.1.1" + "@babel/compat-data@^7.27.2", "@babel/compat-data@^7.27.7": version "7.28.4" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.4.tgz#96fdf1af1b8859c8474ab39c295312bfb7c24b04" integrity sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw== +"@babel/compat-data@^7.28.6": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.0.tgz#00d03e8c0ac24dd9be942c5370990cbe1f17d88d" + integrity sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg== + "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.20.0", "@babel/core@^7.21.3", "@babel/core@^7.23.9", "@babel/core@^7.24.4", "@babel/core@^7.25.2", "@babel/core@^7.27.4": version "7.28.4" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.4.tgz#12a550b8794452df4c8b084f95003bce1742d496" @@ -104,6 +118,17 @@ "@jridgewell/trace-mapping" "^0.3.28" jsesc "^3.0.2" +"@babel/generator@^7.29.0", "@babel/generator@^7.29.1": + version "7.29.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.1.tgz#d09876290111abbb00ef962a7b83a5307fba0d50" + integrity sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw== + dependencies: + "@babel/parser" "^7.29.0" + "@babel/types" "^7.29.0" + "@jridgewell/gen-mapping" "^0.3.12" + "@jridgewell/trace-mapping" "^0.3.28" + jsesc "^3.0.2" + "@babel/helper-annotate-as-pure@^7.27.1", "@babel/helper-annotate-as-pure@^7.27.3": version "7.27.3" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz#f31fd86b915fc4daf1f3ac6976c59be7084ed9c5" @@ -122,6 +147,17 @@ lru-cache "^5.1.1" semver "^6.3.1" +"@babel/helper-compilation-targets@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz#32c4a3f41f12ed1532179b108a4d746e105c2b25" + integrity sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA== + dependencies: + "@babel/compat-data" "^7.28.6" + "@babel/helper-validator-option" "^7.27.1" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.27.1": version "7.28.3" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz#3e747434ea007910c320c4d39a6b46f20f371d46" @@ -135,6 +171,19 @@ "@babel/traverse" "^7.28.3" semver "^6.3.1" +"@babel/helper-create-class-features-plugin@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz#611ff5482da9ef0db6291bcd24303400bca170fb" + integrity sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow== + dependencies: + "@babel/helper-annotate-as-pure" "^7.27.3" + "@babel/helper-member-expression-to-functions" "^7.28.5" + "@babel/helper-optimise-call-expression" "^7.27.1" + "@babel/helper-replace-supers" "^7.28.6" + "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" + "@babel/traverse" "^7.28.6" + semver "^6.3.1" + "@babel/helper-create-regexp-features-plugin@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz#05b0882d97ba1d4d03519e4bce615d70afa18c53" @@ -168,6 +217,14 @@ "@babel/traverse" "^7.27.1" "@babel/types" "^7.27.1" +"@babel/helper-member-expression-to-functions@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz#f3e07a10be37ed7a63461c63e6929575945a6150" + integrity sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg== + dependencies: + "@babel/traverse" "^7.28.5" + "@babel/types" "^7.28.5" + "@babel/helper-module-imports@^7.22.15", "@babel/helper-module-imports@^7.25.9", "@babel/helper-module-imports@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" @@ -197,6 +254,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c" integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== +"@babel/helper-plugin-utils@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz#6f13ea251b68c8532e985fd532f28741a8af9ac8" + integrity sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug== + "@babel/helper-remap-async-to-generator@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz#4601d5c7ce2eb2aea58328d43725523fcd362ce6" @@ -215,6 +277,15 @@ "@babel/helper-optimise-call-expression" "^7.27.1" "@babel/traverse" "^7.27.1" +"@babel/helper-replace-supers@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz#94aa9a1d7423a00aead3f204f78834ce7d53fe44" + integrity sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.28.5" + "@babel/helper-optimise-call-expression" "^7.27.1" + "@babel/traverse" "^7.28.6" + "@babel/helper-skip-transparent-expression-wrappers@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz#62bb91b3abba8c7f1fec0252d9dbea11b3ee7a56" @@ -233,6 +304,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== +"@babel/helper-validator-identifier@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" + integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== + "@babel/helper-validator-option@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" @@ -265,13 +341,20 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.4", "@babel/parser@^7.25.3", "@babel/parser@^7.26.10", "@babel/parser@^7.27.2", "@babel/parser@^7.28.3", "@babel/parser@^7.28.4": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.4", "@babel/parser@^7.25.3", "@babel/parser@^7.26.10", "@babel/parser@^7.27.2", "@babel/parser@^7.28.3", "@babel/parser@^7.28.4": version "7.28.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.4.tgz#da25d4643532890932cc03f7705fe19637e03fa8" integrity sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg== dependencies: "@babel/types" "^7.28.4" +"@babel/parser@^7.28.6", "@babel/parser@^7.29.0": + version "7.29.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.2.tgz#58bd50b9a7951d134988a1ae177a35ef9a703ba1" + integrity sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA== + dependencies: + "@babel/types" "^7.29.0" + "@babel/plugin-proposal-decorators@^7.12.9": version "7.28.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.28.0.tgz#419c8acc31088e05a774344c021800f7ddc39bf0" @@ -443,6 +526,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" +"@babel/plugin-syntax-typescript@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz#c7b2ddf1d0a811145b1de800d1abd146af92e3a2" + integrity sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A== + dependencies: + "@babel/helper-plugin-utils" "^7.28.6" + "@babel/plugin-transform-arrow-functions@^7.0.0-0", "@babel/plugin-transform-arrow-functions@^7.24.7": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz#6e2061067ba3ab0266d834a9f94811196f2aba9a" @@ -475,7 +565,15 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-class-properties@^7.0.0-0", "@babel/plugin-transform-class-properties@^7.25.4": +"@babel/plugin-transform-class-properties@^7.0.0-0": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz#d274a4478b6e782d9ea987fda09bdb6d28d66b72" + integrity sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" + +"@babel/plugin-transform-class-properties@^7.25.4": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz#dd40a6a370dfd49d32362ae206ddaf2bb082a925" integrity sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA== @@ -483,7 +581,27 @@ "@babel/helper-create-class-features-plugin" "^7.27.1" "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-classes@^7.0.0-0", "@babel/plugin-transform-classes@^7.25.4": +"@babel/plugin-transform-class-static-block@^7.27.1": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz#1257491e8259c6d125ac4d9a6f39f9d2bf3dba70" + integrity sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" + +"@babel/plugin-transform-classes@^7.0.0-0": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz#8f6fb79ba3703978e701ce2a97e373aae7dda4b7" + integrity sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q== + dependencies: + "@babel/helper-annotate-as-pure" "^7.27.3" + "@babel/helper-compilation-targets" "^7.28.6" + "@babel/helper-globals" "^7.28.0" + "@babel/helper-plugin-utils" "^7.28.6" + "@babel/helper-replace-supers" "^7.28.6" + "@babel/traverse" "^7.28.6" + +"@babel/plugin-transform-classes@^7.25.4": version "7.28.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz#75d66175486788c56728a73424d67cbc7473495c" integrity sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA== @@ -573,7 +691,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.27.1" "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-nullish-coalescing-operator@^7.0.0-0", "@babel/plugin-transform-nullish-coalescing-operator@^7.24.7": +"@babel/plugin-transform-nullish-coalescing-operator@^7.0.0-0": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz#9bc62096e90ab7a887f3ca9c469f6adec5679757" + integrity sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg== + dependencies: + "@babel/helper-plugin-utils" "^7.28.6" + +"@babel/plugin-transform-nullish-coalescing-operator@^7.24.7": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz#4f9d3153bf6782d73dd42785a9d22d03197bc91d" integrity sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA== @@ -605,7 +730,15 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-optional-chaining@^7.0.0-0", "@babel/plugin-transform-optional-chaining@^7.24.8": +"@babel/plugin-transform-optional-chaining@^7.0.0-0": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz#926cf150bd421fc8362753e911b4a1b1ce4356cd" + integrity sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w== + dependencies: + "@babel/helper-plugin-utils" "^7.28.6" + "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" + +"@babel/plugin-transform-optional-chaining@^7.24.8": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz#874ce3c4f06b7780592e946026eb76a32830454f" integrity sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg== @@ -743,6 +876,17 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" "@babel/plugin-syntax-typescript" "^7.27.1" +"@babel/plugin-transform-typescript@^7.28.5": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz#1e93d96da8adbefdfdade1d4956f73afa201a158" + integrity sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.27.3" + "@babel/helper-create-class-features-plugin" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" + "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" + "@babel/plugin-syntax-typescript" "^7.28.6" + "@babel/plugin-transform-unicode-regex@^7.0.0-0", "@babel/plugin-transform-unicode-regex@^7.24.7": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz#25948f5c395db15f609028e370667ed8bae9af97" @@ -763,7 +907,18 @@ "@babel/plugin-transform-react-jsx-development" "^7.27.1" "@babel/plugin-transform-react-pure-annotations" "^7.27.1" -"@babel/preset-typescript@^7.16.7", "@babel/preset-typescript@^7.23.0": +"@babel/preset-typescript@^7.16.7": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz#540359efa3028236958466342967522fd8f2a60c" + integrity sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-validator-option" "^7.27.1" + "@babel/plugin-syntax-jsx" "^7.27.1" + "@babel/plugin-transform-modules-commonjs" "^7.27.1" + "@babel/plugin-transform-typescript" "^7.28.5" + +"@babel/preset-typescript@^7.23.0": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz#190742a6428d282306648a55b0529b561484f912" integrity sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ== @@ -788,6 +943,15 @@ "@babel/parser" "^7.27.2" "@babel/types" "^7.27.1" +"@babel/template@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57" + integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ== + dependencies: + "@babel/code-frame" "^7.28.6" + "@babel/parser" "^7.28.6" + "@babel/types" "^7.28.6" + "@babel/traverse--for-generate-function-map@npm:@babel/traverse@^7.25.3": version "7.28.4" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.4.tgz#8d456101b96ab175d487249f60680221692b958b" @@ -814,7 +978,20 @@ "@babel/types" "^7.28.4" debug "^4.3.1" -"@babel/types@^7.0.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.21.3", "@babel/types@^7.23.0", "@babel/types@^7.25.2", "@babel/types@^7.26.10", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.4", "@babel/types@^7.3.3": +"@babel/traverse@^7.28.5", "@babel/traverse@^7.28.6", "@babel/traverse@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.0.tgz#f323d05001440253eead3c9c858adbe00b90310a" + integrity sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA== + dependencies: + "@babel/code-frame" "^7.29.0" + "@babel/generator" "^7.29.0" + "@babel/helper-globals" "^7.28.0" + "@babel/parser" "^7.29.0" + "@babel/template" "^7.28.6" + "@babel/types" "^7.29.0" + debug "^4.3.1" + +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.21.3", "@babel/types@^7.23.0", "@babel/types@^7.25.2", "@babel/types@^7.26.10", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.4", "@babel/types@^7.3.3": version "7.28.4" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.4.tgz#0a4e618f4c60a7cd6c11cb2d48060e4dbe38ac3a" integrity sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q== @@ -822,6 +999,14 @@ "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.27.1" +"@babel/types@^7.26.0", "@babel/types@^7.28.5", "@babel/types@^7.28.6", "@babel/types@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7" + integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -1193,30 +1378,30 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== -"@expo/cli@0.24.22": - version "0.24.22" - resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-0.24.22.tgz#75bef7ce8df0e239b489b70a396e9ddf9833bad1" - integrity sha512-cEg6/F8ZWjoVkEwm0rXKReWbsCUROFbLFBYht+d5RzHnDwJoTX4QWJKx4m+TGNDPamRUIGw36U4z41Fvev0XmA== +"@expo/cli@54.0.23": + version "54.0.23" + resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-54.0.23.tgz#e8a7dc4e1f2a8a5361afd80bcc352014b57a87ac" + integrity sha512-km0h72SFfQCmVycH/JtPFTVy69w6Lx1cHNDmfLfQqgKFYeeHTjx7LVDP4POHCtNxFP2UeRazrygJhlh4zz498g== dependencies: "@0no-co/graphql.web" "^1.0.8" - "@babel/runtime" "^7.20.0" - "@expo/code-signing-certificates" "^0.0.5" - "@expo/config" "~11.0.13" - "@expo/config-plugins" "~10.1.2" - "@expo/devcert" "^1.1.2" - "@expo/env" "~1.0.7" - "@expo/image-utils" "^0.7.6" - "@expo/json-file" "^9.1.5" - "@expo/metro-config" "~0.20.17" - "@expo/osascript" "^2.2.5" - "@expo/package-manager" "^1.8.6" - "@expo/plist" "^0.3.5" - "@expo/prebuild-config" "^9.0.12" - "@expo/schema-utils" "^0.1.0" + "@expo/code-signing-certificates" "^0.0.6" + "@expo/config" "~12.0.13" + "@expo/config-plugins" "~54.0.4" + "@expo/devcert" "^1.2.1" + "@expo/env" "~2.0.8" + "@expo/image-utils" "^0.8.8" + "@expo/json-file" "^10.0.8" + "@expo/metro" "~54.2.0" + "@expo/metro-config" "~54.0.14" + "@expo/osascript" "^2.3.8" + "@expo/package-manager" "^1.9.10" + "@expo/plist" "^0.4.8" + "@expo/prebuild-config" "^54.0.8" + "@expo/schema-utils" "^0.1.8" "@expo/spawn-async" "^1.7.2" "@expo/ws-tunnel" "^1.0.1" "@expo/xcpretty" "^4.3.0" - "@react-native/dev-middleware" "0.79.6" + "@react-native/dev-middleware" "0.81.5" "@urql/core" "^5.0.6" "@urql/exchange-retry" "^1.3.0" accepts "^1.3.8" @@ -1230,12 +1415,13 @@ connect "^3.7.0" debug "^4.3.4" env-editor "^0.4.1" + expo-server "^1.0.5" freeport-async "^2.0.0" getenv "^2.0.0" - glob "^10.4.2" + glob "^13.0.0" lan-network "^0.1.6" minimatch "^9.0.0" - node-forge "^1.3.1" + node-forge "^1.3.3" npm-package-arg "^11.0.0" ora "^3.4.0" picomatch "^3.0.1" @@ -1255,33 +1441,32 @@ source-map-support "~0.5.21" stacktrace-parser "^0.1.10" structured-headers "^0.4.1" - tar "^7.4.3" + tar "^7.5.2" terminal-link "^2.1.1" undici "^6.18.2" wrap-ansi "^7.0.0" ws "^8.12.1" -"@expo/code-signing-certificates@^0.0.5": - version "0.0.5" - resolved "https://registry.yarnpkg.com/@expo/code-signing-certificates/-/code-signing-certificates-0.0.5.tgz#a693ff684fb20c4725dade4b88a6a9f96b02496c" - integrity sha512-BNhXkY1bblxKZpltzAx98G2Egj9g1Q+JRcvR7E99DOj862FTCX+ZPsAUtPTr7aHxwtrL7+fL3r0JSmM9kBm+Bw== +"@expo/code-signing-certificates@^0.0.6": + version "0.0.6" + resolved "https://registry.yarnpkg.com/@expo/code-signing-certificates/-/code-signing-certificates-0.0.6.tgz#6b7b22830cb69c77a45e357c2f3aa7ab436ac772" + integrity sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w== dependencies: - node-forge "^1.2.1" - nullthrows "^1.1.1" + node-forge "^1.3.3" -"@expo/config-plugins@~10.1.1", "@expo/config-plugins@~10.1.2": - version "10.1.2" - resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-10.1.2.tgz#6efa256a3fa2fca116eeb5bef8b22b089e287282" - integrity sha512-IMYCxBOcnuFStuK0Ay+FzEIBKrwW8OVUMc65+v0+i7YFIIe8aL342l7T4F8lR4oCfhXn7d6M5QPgXvjtc/gAcw== +"@expo/config-plugins@~54.0.4": + version "54.0.4" + resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-54.0.4.tgz#b31cb16f6651342abcdafba600118245ecd9fb00" + integrity sha512-g2yXGICdoOw5i3LkQSDxl2Q5AlQCrG7oniu0pCPPO+UxGb7He4AFqSvPSy8HpRUj55io17hT62FTjYRD+d6j3Q== dependencies: - "@expo/config-types" "^53.0.5" - "@expo/json-file" "~9.1.5" - "@expo/plist" "^0.3.5" + "@expo/config-types" "^54.0.10" + "@expo/json-file" "~10.0.8" + "@expo/plist" "^0.4.8" "@expo/sdk-runtime-versions" "^1.0.0" chalk "^4.1.2" debug "^4.3.5" getenv "^2.0.0" - glob "^10.4.2" + glob "^13.0.0" resolve-from "^5.0.0" semver "^7.5.4" slash "^3.0.0" @@ -1289,43 +1474,49 @@ xcode "^3.0.1" xml2js "0.6.0" -"@expo/config-types@^53.0.5": - version "53.0.5" - resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-53.0.5.tgz#bba7e0712c2c5b1d8963348d68ea96339f858db4" - integrity sha512-kqZ0w44E+HEGBjy+Lpyn0BVL5UANg/tmNixxaRMLS6nf37YsDrLk2VMAmeKMMk5CKG0NmOdVv3ngeUjRQMsy9g== +"@expo/config-types@^54.0.10": + version "54.0.10" + resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-54.0.10.tgz#688f4338255d2fdea970f44e2dfd8e8d37dec292" + integrity sha512-/J16SC2an1LdtCZ67xhSkGXpALYUVUNyZws7v+PVsFZxClYehDSoKLqyRaGkpHlYrCc08bS0RF5E0JV6g50psA== -"@expo/config@^11.0.0", "@expo/config@~11.0.12", "@expo/config@~11.0.13": - version "11.0.13" - resolved "https://registry.yarnpkg.com/@expo/config/-/config-11.0.13.tgz#1cc490a5f667e0129db5f98755f6bc4d8921edb2" - integrity sha512-TnGb4u/zUZetpav9sx/3fWK71oCPaOjZHoVED9NaEncktAd0Eonhq5NUghiJmkUGt3gGSjRAEBXiBbbY9/B1LA== +"@expo/config@~12.0.11", "@expo/config@~12.0.12", "@expo/config@~12.0.13": + version "12.0.13" + resolved "https://registry.yarnpkg.com/@expo/config/-/config-12.0.13.tgz#8e696e6121c3c364e1dd527f595cf0a1d9386828" + integrity sha512-Cu52arBa4vSaupIWsF0h7F/Cg//N374nYb7HAxV0I4KceKA7x2UXpYaHOL7EEYYvp7tZdThBjvGpVmr8ScIvaQ== dependencies: "@babel/code-frame" "~7.10.4" - "@expo/config-plugins" "~10.1.2" - "@expo/config-types" "^53.0.5" - "@expo/json-file" "^9.1.5" + "@expo/config-plugins" "~54.0.4" + "@expo/config-types" "^54.0.10" + "@expo/json-file" "^10.0.8" deepmerge "^4.3.1" getenv "^2.0.0" - glob "^10.4.2" + glob "^13.0.0" require-from-string "^2.0.2" resolve-from "^5.0.0" resolve-workspace-root "^2.0.0" semver "^7.6.0" slugify "^1.3.4" - sucrase "3.35.0" + sucrase "~3.35.1" -"@expo/devcert@^1.1.2": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@expo/devcert/-/devcert-1.2.0.tgz#7b32c2d959e36baaa0649433395e5170c808b44f" - integrity sha512-Uilcv3xGELD5t/b0eM4cxBFEKQRIivB3v7i+VhWLV/gL98aw810unLKKJbGAxAIhY6Ipyz8ChWibFsKFXYwstA== +"@expo/devcert@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@expo/devcert/-/devcert-1.2.1.tgz#1a687985bea1670866e54d5ba7c0ced963c354f4" + integrity sha512-qC4eaxmKMTmJC2ahwyui6ud8f3W60Ss7pMkpBq40Hu3zyiAaugPXnZ24145U7K36qO9UHdZUVxsCvIpz2RYYCA== dependencies: "@expo/sudo-prompt" "^9.3.1" debug "^3.1.0" - glob "^10.4.2" -"@expo/env@~1.0.7": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@expo/env/-/env-1.0.7.tgz#6ee604e158d0f140fc2be711b9a7cb3adc341889" - integrity sha512-qSTEnwvuYJ3umapO9XJtrb1fAqiPlmUUg78N0IZXXGwQRt+bkp0OBls+Y5Mxw/Owj8waAM0Z3huKKskRADR5ow== +"@expo/devtools@0.1.8": + version "0.1.8" + resolved "https://registry.yarnpkg.com/@expo/devtools/-/devtools-0.1.8.tgz#bc5b297698f78b3b67037f04593a31e688330a7a" + integrity sha512-SVLxbuanDjJPgc0sy3EfXUMLb/tXzp6XIHkhtPVmTWJAp+FOr6+5SeiCfJrCzZFet0Ifyke2vX3sFcKwEvCXwQ== + dependencies: + chalk "^4.1.2" + +"@expo/env@~2.0.8": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@expo/env/-/env-2.0.11.tgz#3a10d9142b1833566bdfb39de1c062f7a8b8ac38" + integrity sha512-xV+ps6YCW7XIPVUwFVCRN2nox09dnRwy8uIjwHWTODu0zFw4kp4omnVkl0OOjuu2XOe7tdgAHxikrkJt9xB/7Q== dependencies: chalk "^4.0.0" debug "^4.3.4" @@ -1333,18 +1524,17 @@ dotenv-expand "~11.0.6" getenv "^2.0.0" -"@expo/fingerprint@0.13.4": - version "0.13.4" - resolved "https://registry.yarnpkg.com/@expo/fingerprint/-/fingerprint-0.13.4.tgz#380762d68e3d55718331ede813e24b8760ebb2b5" - integrity sha512-MYfPYBTMfrrNr07DALuLhG6EaLVNVrY/PXjEzsjWdWE4ZFn0yqI0IdHNkJG7t1gePT8iztHc7qnsx+oo/rDo6w== +"@expo/fingerprint@0.15.4": + version "0.15.4" + resolved "https://registry.yarnpkg.com/@expo/fingerprint/-/fingerprint-0.15.4.tgz#578bd1e1179a13313f7de682ebaaacb703b2b7ac" + integrity sha512-eYlxcrGdR2/j2M6pEDXo9zU9KXXF1vhP+V+Tl+lyY+bU8lnzrN6c637mz6Ye3em2ANy8hhUR03Raf8VsT9Ogng== dependencies: "@expo/spawn-async" "^1.7.2" arg "^5.0.2" chalk "^4.1.2" debug "^4.3.4" - find-up "^5.0.0" getenv "^2.0.0" - glob "^10.4.2" + glob "^13.0.0" ignore "^5.3.1" minimatch "^9.0.0" p-limit "^3.1.0" @@ -1356,132 +1546,147 @@ resolved "https://registry.yarnpkg.com/@expo/html-elements/-/html-elements-0.10.1.tgz#ec2625370cf1d4cb78efa954df45d422532d5ab6" integrity sha512-3PTmtkV15D7+lykXVtvkH1jQ5Y6JE+e3zCaoMMux7z2cSLGQUNwDEUwG37gew3OEB1/E4/SEWgjvg8m7E6/e2Q== -"@expo/image-utils@^0.7.6": - version "0.7.6" - resolved "https://registry.yarnpkg.com/@expo/image-utils/-/image-utils-0.7.6.tgz#b8442bef770e1c7b39997d57f666bffeeced0a7a" - integrity sha512-GKnMqC79+mo/1AFrmAcUcGfbsXXTRqOMNS1umebuevl3aaw+ztsYEFEiuNhHZW7PQ3Xs3URNT513ZxKhznDscw== +"@expo/image-utils@^0.8.8": + version "0.8.13" + resolved "https://registry.yarnpkg.com/@expo/image-utils/-/image-utils-0.8.13.tgz#c7476352af9f576440e5ec8201c2f75f090a4804" + integrity sha512-1I//yBQeTY6p0u1ihqGNDAr35EbSG8uFEupFrIF0jd++h9EWH33521yZJU1yE+mwGlzCb61g3ehu78siMhXBlA== dependencies: + "@expo/require-utils" "^55.0.4" "@expo/spawn-async" "^1.7.2" chalk "^4.0.0" getenv "^2.0.0" jimp-compact "0.16.1" parse-png "^2.1.0" - resolve-from "^5.0.0" semver "^7.6.0" - temp-dir "~2.0.0" - unique-string "~2.0.0" - -"@expo/json-file@^10.0.7": - version "10.0.7" - resolved "https://registry.yarnpkg.com/@expo/json-file/-/json-file-10.0.7.tgz#e4f58fdc03fc62f13610eeafe086d84e6e44fe01" - integrity sha512-z2OTC0XNO6riZu98EjdNHC05l51ySeTto6GP7oSQrCvQgG9ARBwD1YvMQaVZ9wU7p/4LzSf1O7tckL3B45fPpw== - dependencies: - "@babel/code-frame" "~7.10.4" - json5 "^2.2.3" -"@expo/json-file@^9.1.5", "@expo/json-file@~9.1.5": - version "9.1.5" - resolved "https://registry.yarnpkg.com/@expo/json-file/-/json-file-9.1.5.tgz#7d7b2dc4990dc2c2de69a571191aba984b7fb7ed" - integrity sha512-prWBhLUlmcQtvN6Y7BpW2k9zXGd3ySa3R6rAguMJkp1z22nunLN64KYTUWfijFlprFoxm9r2VNnGkcbndAlgKA== +"@expo/json-file@^10.0.13", "@expo/json-file@^10.0.8", "@expo/json-file@~10.0.8": + version "10.0.13" + resolved "https://registry.yarnpkg.com/@expo/json-file/-/json-file-10.0.13.tgz#1a9ac56333786e8672181b0b95aab08f8255a548" + integrity sha512-pX/XjQn7tgNw6zuuV2ikmegmwe/S7uiwhrs2wXrANMkq7ozrA+JcZwgW9Q/8WZgciBzfAhNp5hnackHcrmapQA== dependencies: - "@babel/code-frame" "~7.10.4" + "@babel/code-frame" "^7.20.0" json5 "^2.2.3" -"@expo/metro-config@0.20.17", "@expo/metro-config@~0.20.17": - version "0.20.17" - resolved "https://registry.yarnpkg.com/@expo/metro-config/-/metro-config-0.20.17.tgz#3be75fd6b93081c8a9f0022dcfa9e5b767334902" - integrity sha512-lpntF2UZn5bTwrPK6guUv00Xv3X9mkN3YYla+IhEHiYXWyG7WKOtDU0U4KR8h3ubkZ6SPH3snDyRyAzMsWtZFA== +"@expo/metro-config@54.0.14", "@expo/metro-config@~54.0.14": + version "54.0.14" + resolved "https://registry.yarnpkg.com/@expo/metro-config/-/metro-config-54.0.14.tgz#e455dfb2bae9473ec665bc830d651baa709c1e8a" + integrity sha512-hxpLyDfOR4L23tJ9W1IbJJsG7k4lv2sotohBm/kTYyiG+pe1SYCAWsRmgk+H42o/wWf/HQjE5k45S5TomGLxNA== dependencies: + "@babel/code-frame" "^7.20.0" "@babel/core" "^7.20.0" "@babel/generator" "^7.20.5" - "@babel/parser" "^7.20.0" - "@babel/types" "^7.20.0" - "@expo/config" "~11.0.12" - "@expo/env" "~1.0.7" - "@expo/json-file" "~9.1.5" + "@expo/config" "~12.0.13" + "@expo/env" "~2.0.8" + "@expo/json-file" "~10.0.8" + "@expo/metro" "~54.2.0" "@expo/spawn-async" "^1.7.2" + browserslist "^4.25.0" chalk "^4.1.0" debug "^4.3.2" dotenv "~16.4.5" dotenv-expand "~11.0.6" getenv "^2.0.0" - glob "^10.4.2" + glob "^13.0.0" + hermes-parser "^0.29.1" jsc-safe-url "^0.2.4" - lightningcss "~1.27.0" + lightningcss "^1.30.1" minimatch "^9.0.0" postcss "~8.4.32" resolve-from "^5.0.0" -"@expo/metro-runtime@5.0.5", "@expo/metro-runtime@~5.0.5": - version "5.0.5" - resolved "https://registry.yarnpkg.com/@expo/metro-runtime/-/metro-runtime-5.0.5.tgz#0b6d365e87034e3dde96fb2f7373fcb0de40af1e" - integrity sha512-P8UFTi+YsmiD1BmdTdiIQITzDMcZgronsA3RTQ4QKJjHM3bas11oGzLQOnFaIZnlEV8Rrr3m1m+RHxvnpL+t/A== +"@expo/metro-runtime@^6.1.2", "@expo/metro-runtime@~6.1.2": + version "6.1.2" + resolved "https://registry.yarnpkg.com/@expo/metro-runtime/-/metro-runtime-6.1.2.tgz#5a4ff117df6115f9c9d4dcc561065e16d69c078b" + integrity sha512-nvM+Qv45QH7pmYvP8JB1G8JpScrWND3KrMA6ZKe62cwwNiX/BjHU28Ear0v/4bQWXlOY0mv6B8CDIm8JxXde9g== + dependencies: + anser "^1.4.9" + pretty-format "^29.7.0" + stacktrace-parser "^0.1.10" + whatwg-fetch "^3.0.0" -"@expo/osascript@^2.2.5": - version "2.3.7" - resolved "https://registry.yarnpkg.com/@expo/osascript/-/osascript-2.3.7.tgz#2d53ef06733593405c83767de7420510736e0fa9" - integrity sha512-IClSOXxR0YUFxIriUJVqyYki7lLMIHrrzOaP01yxAL1G8pj2DWV5eW1y5jSzIcIfSCNhtGsshGd1tU/AYup5iQ== +"@expo/metro@~54.2.0": + version "54.2.0" + resolved "https://registry.yarnpkg.com/@expo/metro/-/metro-54.2.0.tgz#6ecf4a77ae7553b73daca4206854728de76c854d" + integrity sha512-h68TNZPGsk6swMmLm9nRSnE2UXm48rWwgcbtAHVMikXvbxdS41NDHHeqg1rcQ9AbznDRp6SQVC2MVpDnsRKU1w== + dependencies: + metro "0.83.3" + metro-babel-transformer "0.83.3" + metro-cache "0.83.3" + metro-cache-key "0.83.3" + metro-config "0.83.3" + metro-core "0.83.3" + metro-file-map "0.83.3" + metro-minify-terser "0.83.3" + metro-resolver "0.83.3" + metro-runtime "0.83.3" + metro-source-map "0.83.3" + metro-symbolicate "0.83.3" + metro-transform-plugins "0.83.3" + metro-transform-worker "0.83.3" + +"@expo/osascript@^2.3.8": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@expo/osascript/-/osascript-2.4.2.tgz#fe341cff1eb2c939da43cf58ade5504c8a5d77ca" + integrity sha512-/XP7PSYF2hzOZzqfjgkoWtllyeTN8dW3aM4P6YgKcmmPikKL5FdoyQhti4eh6RK5a5VrUXJTOlTNIpIHsfB5Iw== dependencies: "@expo/spawn-async" "^1.7.2" - exec-async "^2.2.0" -"@expo/package-manager@^1.8.6": - version "1.9.8" - resolved "https://registry.yarnpkg.com/@expo/package-manager/-/package-manager-1.9.8.tgz#8f6b46a2f5f4bf4f2c78507b1a7a368e0c2e2126" - integrity sha512-4/I6OWquKXYnzo38pkISHCOCOXxfeEmu4uDoERq1Ei/9Ur/s9y3kLbAamEkitUkDC7gHk1INxRWEfFNzGbmOrA== +"@expo/package-manager@^1.9.10": + version "1.10.4" + resolved "https://registry.yarnpkg.com/@expo/package-manager/-/package-manager-1.10.4.tgz#1a16bd2ccf85a23865dd98392c11b9f75f9bbf7a" + integrity sha512-y9Mr4Kmpk4abAVZrNNPCdzOZr8nLLyi18p1SXr0RCVA8IfzqZX/eY4H+50a0HTmXqIsPZrQdcdb4I3ekMS9GvQ== dependencies: - "@expo/json-file" "^10.0.7" + "@expo/json-file" "^10.0.13" "@expo/spawn-async" "^1.7.2" chalk "^4.0.0" npm-package-arg "^11.0.0" ora "^3.4.0" resolve-workspace-root "^2.0.0" -"@expo/plist@^0.3.5": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@expo/plist/-/plist-0.3.5.tgz#11913c64951936101529cb26d7260ef16970fc31" - integrity sha512-9RYVU1iGyCJ7vWfg3e7c/NVyMFs8wbl+dMWZphtFtsqyN9zppGREU3ctlD3i8KUE0sCUTVnLjCWr+VeUIDep2g== +"@expo/plist@^0.4.8": + version "0.4.8" + resolved "https://registry.yarnpkg.com/@expo/plist/-/plist-0.4.8.tgz#e014511a4a5008cf2b832b91caa8e9f2704127cc" + integrity sha512-pfNtErGGzzRwHP+5+RqswzPDKkZrx+Cli0mzjQaus1ZWFsog5ibL+nVT3NcporW51o8ggnt7x813vtRbPiyOrQ== dependencies: "@xmldom/xmldom" "^0.8.8" base64-js "^1.2.3" xmlbuilder "^15.1.1" -"@expo/prebuild-config@^9.0.10", "@expo/prebuild-config@^9.0.12": - version "9.0.12" - resolved "https://registry.yarnpkg.com/@expo/prebuild-config/-/prebuild-config-9.0.12.tgz#ee009b6b4e01ce93f90726f58b084016d2e820a3" - integrity sha512-AKH5Scf+gEMgGxZZaimrJI2wlUJlRoqzDNn7/rkhZa5gUTnO4l6slKak2YdaH+nXlOWCNfAQWa76NnpQIfmv6Q== - dependencies: - "@expo/config" "~11.0.13" - "@expo/config-plugins" "~10.1.2" - "@expo/config-types" "^53.0.5" - "@expo/image-utils" "^0.7.6" - "@expo/json-file" "^9.1.5" - "@react-native/normalize-colors" "0.79.6" +"@expo/prebuild-config@^54.0.8": + version "54.0.8" + resolved "https://registry.yarnpkg.com/@expo/prebuild-config/-/prebuild-config-54.0.8.tgz#509410345489cc52d1e6ece52742384efe7ad7c6" + integrity sha512-EA7N4dloty2t5Rde+HP0IEE+nkAQiu4A/+QGZGT9mFnZ5KKjPPkqSyYcRvP5bhQE10D+tvz6X0ngZpulbMdbsg== + dependencies: + "@expo/config" "~12.0.13" + "@expo/config-plugins" "~54.0.4" + "@expo/config-types" "^54.0.10" + "@expo/image-utils" "^0.8.8" + "@expo/json-file" "^10.0.8" + "@react-native/normalize-colors" "0.81.5" debug "^4.3.1" resolve-from "^5.0.0" semver "^7.6.0" xml2js "0.6.0" -"@expo/schema-utils@^0.1.0": - version "0.1.7" - resolved "https://registry.yarnpkg.com/@expo/schema-utils/-/schema-utils-0.1.7.tgz#38baa0effa0823cd4eca3f05e5eee3bde311da12" - integrity sha512-jWHoSuwRb5ZczjahrychMJ3GWZu54jK9ulNdh1d4OzAEq672K9E5yOlnlBsfIHWHGzUAT+0CL7Yt1INiXTz68g== +"@expo/require-utils@^55.0.4": + version "55.0.4" + resolved "https://registry.yarnpkg.com/@expo/require-utils/-/require-utils-55.0.4.tgz#cd474a8997ba6ecfa43d084a7f17bde0cb854179" + integrity sha512-JAANvXqV7MOysWeVWgaiDzikoyDjJWOV/ulOW60Zb3kXJfrx2oZOtGtDXDFKD1mXuahQgoM5QOjuZhF7gFRNjA== + dependencies: + "@babel/code-frame" "^7.20.0" + "@babel/core" "^7.25.2" + "@babel/plugin-transform-modules-commonjs" "^7.24.8" + +"@expo/schema-utils@^0.1.8": + version "0.1.8" + resolved "https://registry.yarnpkg.com/@expo/schema-utils/-/schema-utils-0.1.8.tgz#8b9543d77fc4ac4954196e3fa00f8fcedd71426a" + integrity sha512-9I6ZqvnAvKKDiO+ZF8BpQQFYWXOJvTAL5L/227RUbWG1OVZDInFifzCBiqAZ3b67NRfeAgpgvbA7rejsqhY62A== "@expo/sdk-runtime-versions@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@expo/sdk-runtime-versions/-/sdk-runtime-versions-1.0.0.tgz#d7ebd21b19f1c6b0395e50d78da4416941c57f7c" integrity sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ== -"@expo/server@^0.6.3": - version "0.6.3" - resolved "https://registry.yarnpkg.com/@expo/server/-/server-0.6.3.tgz#f5c1b52c8841527a242c656a763e280af8accc1a" - integrity sha512-Ea7NJn9Xk1fe4YeJ86rObHSv/bm3u/6WiQPXEqXJ2GrfYpVab2Swoh9/PnSM3KjR64JAgKjArDn1HiPjITCfHA== - dependencies: - abort-controller "^3.0.0" - debug "^4.3.4" - source-map-support "~0.5.21" - undici "^6.18.2 || ^7.0.0" - "@expo/spawn-async@^1.7.2": version "1.7.2" resolved "https://registry.yarnpkg.com/@expo/spawn-async/-/spawn-async-1.7.2.tgz#fcfe66c3e387245e72154b1a7eae8cada6a47f58" @@ -1494,10 +1699,10 @@ resolved "https://registry.yarnpkg.com/@expo/sudo-prompt/-/sudo-prompt-9.3.2.tgz#0fd2813402a42988e49145cab220e25bea74b308" integrity sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw== -"@expo/vector-icons@^14.0.0": - version "14.1.0" - resolved "https://registry.yarnpkg.com/@expo/vector-icons/-/vector-icons-14.1.0.tgz#d3dddad8b6ea60502e0fe5485b86751827606ce4" - integrity sha512-7T09UE9h8QDTsUeMGymB4i+iqvtEeaO5VvUjryFB4tugDTG/bkzViWA74hm5pfjjDEhYMXWaX112mcvhccmIwQ== +"@expo/vector-icons@^15.0.3": + version "15.1.1" + resolved "https://registry.yarnpkg.com/@expo/vector-icons/-/vector-icons-15.1.1.tgz#4b1d2c60493c0b0536972f0a5babd5f5c85b48f4" + integrity sha512-Iu2VkcoI5vygbtYngm7jb4ifxElNVXQYdDrYkT7UCEIiKLeWnQY0wf2ZhHZ+Wro6Sc5TaumpKUOqDRpLi5rkvw== "@expo/ws-tunnel@^1.0.1": version "1.0.6" @@ -2962,11 +3167,126 @@ "@pnpm/network.ca-file" "^1.0.1" config-chain "^1.1.11" +"@radix-ui/primitive@1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.3.tgz#e2dbc13bdc5e4168f4334f75832d7bdd3e2de5ba" + integrity sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg== + +"@radix-ui/react-collection@1.1.7": + version "1.1.7" + resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.1.7.tgz#d05c25ca9ac4695cc19ba91f42f686e3ea2d9aec" + integrity sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw== + dependencies: + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-slot" "1.2.3" + "@radix-ui/react-compose-refs@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz#a2c4c47af6337048ee78ff6dc0d090b390d2bb30" integrity sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg== +"@radix-ui/react-context@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.2.tgz#61628ef269a433382c364f6f1e3788a6dc213a36" + integrity sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA== + +"@radix-ui/react-dialog@^1.1.1": + version "1.1.15" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz#1de3d7a7e9a17a9874d29c07f5940a18a119b632" + integrity sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw== + dependencies: + "@radix-ui/primitive" "1.1.3" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-dismissable-layer" "1.1.11" + "@radix-ui/react-focus-guards" "1.1.3" + "@radix-ui/react-focus-scope" "1.1.7" + "@radix-ui/react-id" "1.1.1" + "@radix-ui/react-portal" "1.1.9" + "@radix-ui/react-presence" "1.1.5" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-slot" "1.2.3" + "@radix-ui/react-use-controllable-state" "1.2.2" + aria-hidden "^1.2.4" + react-remove-scroll "^2.6.3" + +"@radix-ui/react-direction@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.1.1.tgz#39e5a5769e676c753204b792fbe6cf508e550a14" + integrity sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw== + +"@radix-ui/react-dismissable-layer@1.1.11": + version "1.1.11" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz#e33ab6f6bdaa00f8f7327c408d9f631376b88b37" + integrity sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg== + dependencies: + "@radix-ui/primitive" "1.1.3" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-use-callback-ref" "1.1.1" + "@radix-ui/react-use-escape-keydown" "1.1.1" + +"@radix-ui/react-focus-guards@1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz#2a5669e464ad5fde9f86d22f7fdc17781a4dfa7f" + integrity sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw== + +"@radix-ui/react-focus-scope@1.1.7": + version "1.1.7" + resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz#dfe76fc103537d80bf42723a183773fd07bfb58d" + integrity sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw== + dependencies: + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-use-callback-ref" "1.1.1" + +"@radix-ui/react-id@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.1.tgz#1404002e79a03fe062b7e3864aa01e24bd1471f7" + integrity sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg== + dependencies: + "@radix-ui/react-use-layout-effect" "1.1.1" + +"@radix-ui/react-portal@1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.9.tgz#14c3649fe48ec474ac51ed9f2b9f5da4d91c4472" + integrity sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ== + dependencies: + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-use-layout-effect" "1.1.1" + +"@radix-ui/react-presence@1.1.5": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.5.tgz#5d8f28ac316c32f078afce2996839250c10693db" + integrity sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ== + dependencies: + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-use-layout-effect" "1.1.1" + +"@radix-ui/react-primitive@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz#db9b8bcff49e01be510ad79893fb0e4cda50f1bc" + integrity sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ== + dependencies: + "@radix-ui/react-slot" "1.2.3" + +"@radix-ui/react-roving-focus@1.1.11": + version "1.1.11" + resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz#ef54384b7361afc6480dcf9907ef2fedb5080fd9" + integrity sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA== + dependencies: + "@radix-ui/primitive" "1.1.3" + "@radix-ui/react-collection" "1.1.7" + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-direction" "1.1.1" + "@radix-ui/react-id" "1.1.1" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-use-callback-ref" "1.1.1" + "@radix-ui/react-use-controllable-state" "1.2.2" + "@radix-ui/react-slot@1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.2.0.tgz#57727fc186ddb40724ccfbe294e1a351d92462ba" @@ -2974,6 +3294,59 @@ dependencies: "@radix-ui/react-compose-refs" "1.1.2" +"@radix-ui/react-slot@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.2.3.tgz#502d6e354fc847d4169c3bc5f189de777f68cfe1" + integrity sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A== + dependencies: + "@radix-ui/react-compose-refs" "1.1.2" + +"@radix-ui/react-tabs@^1.1.12": + version "1.1.13" + resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz#3537ce379d7e7ff4eeb6b67a0973e139c2ac1f15" + integrity sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A== + dependencies: + "@radix-ui/primitive" "1.1.3" + "@radix-ui/react-context" "1.1.2" + "@radix-ui/react-direction" "1.1.1" + "@radix-ui/react-id" "1.1.1" + "@radix-ui/react-presence" "1.1.5" + "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-roving-focus" "1.1.11" + "@radix-ui/react-use-controllable-state" "1.2.2" + +"@radix-ui/react-use-callback-ref@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz#62a4dba8b3255fdc5cc7787faeac1c6e4cc58d40" + integrity sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg== + +"@radix-ui/react-use-controllable-state@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz#905793405de57d61a439f4afebbb17d0645f3190" + integrity sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg== + dependencies: + "@radix-ui/react-use-effect-event" "0.0.2" + "@radix-ui/react-use-layout-effect" "1.1.1" + +"@radix-ui/react-use-effect-event@0.0.2": + version "0.0.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz#090cf30d00a4c7632a15548512e9152217593907" + integrity sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA== + dependencies: + "@radix-ui/react-use-layout-effect" "1.1.1" + +"@radix-ui/react-use-escape-keydown@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz#b3fed9bbea366a118f40427ac40500aa1423cc29" + integrity sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g== + dependencies: + "@radix-ui/react-use-callback-ref" "1.1.1" + +"@radix-ui/react-use-layout-effect@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz#0c4230a9eed49d4589c967e2d9c0d9d60a23971e" + integrity sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ== + "@react-aria/checkbox@3.2.1": version "3.2.1" resolved "https://registry.yarnpkg.com/@react-aria/checkbox/-/checkbox-3.2.1.tgz#493d9d584b4db474645a4565c4f899ee3a579f07" @@ -3331,23 +3704,23 @@ resolved "https://registry.yarnpkg.com/@react-native-community/netinfo/-/netinfo-11.4.1.tgz#a3c247aceab35f75dd0aa4bfa85d2be5a4508688" integrity sha512-B0BYAkghz3Q2V09BF88RA601XursIEA111tnc2JOaN7axJWmNefmfjZqw/KdSxKZp7CZUuPpjBmz/WCR9uaHYg== -"@react-native/assets-registry@0.79.6": - version "0.79.6" - resolved "https://registry.yarnpkg.com/@react-native/assets-registry/-/assets-registry-0.79.6.tgz#cecc2a1140a9584d590000b951a08a0611ec30c3" - integrity sha512-UVSP1224PWg0X+mRlZNftV5xQwZGfawhivuW8fGgxNK9MS/U84xZ+16lkqcPh1ank6MOt239lIWHQ1S33CHgqA== +"@react-native/assets-registry@0.81.5": + version "0.81.5" + resolved "https://registry.yarnpkg.com/@react-native/assets-registry/-/assets-registry-0.81.5.tgz#d22c924fa6f6d4a463c5af34ce91f38756c0fa7d" + integrity sha512-705B6x/5Kxm1RKRvSv0ADYWm5JOnoiQ1ufW7h8uu2E6G9Of/eE6hP/Ivw3U5jI16ERqZxiKQwk34VJbB0niX9w== -"@react-native/babel-plugin-codegen@0.79.6": - version "0.79.6" - resolved "https://registry.yarnpkg.com/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.79.6.tgz#2e86024a649072268b03b28da8555f9c81bdb51b" - integrity sha512-CS5OrgcMPixOyUJ/Sk/HSsKsKgyKT5P7y3CojimOQzWqRZBmoQfxdST4ugj7n1H+ebM2IKqbgovApFbqXsoX0g== +"@react-native/babel-plugin-codegen@0.81.5": + version "0.81.5" + resolved "https://registry.yarnpkg.com/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.81.5.tgz#328d03f42c32b5a8cc2dee1aa84a7c48dddc5f18" + integrity sha512-oF71cIH6je3fSLi6VPjjC3Sgyyn57JLHXs+mHWc9MoCiJJcM4nqsS5J38zv1XQ8d3zOW2JtHro+LF0tagj2bfQ== dependencies: "@babel/traverse" "^7.25.3" - "@react-native/codegen" "0.79.6" + "@react-native/codegen" "0.81.5" -"@react-native/babel-preset@0.79.6": - version "0.79.6" - resolved "https://registry.yarnpkg.com/@react-native/babel-preset/-/babel-preset-0.79.6.tgz#bc0e94a0b3403d237a60902161587ff90205835c" - integrity sha512-H+FRO+r2Ql6b5IwfE0E7D52JhkxjeGSBSUpCXAI5zQ60zSBJ54Hwh2bBJOohXWl4J+C7gKYSAd2JHMUETu+c/A== +"@react-native/babel-preset@0.81.5": + version "0.81.5" + resolved "https://registry.yarnpkg.com/@react-native/babel-preset/-/babel-preset-0.81.5.tgz#e8b7969d21f87ef4e41e603248e8a70c44b4a5bb" + integrity sha512-UoI/x/5tCmi+pZ3c1+Ypr1DaRMDLI3y+Q70pVLLVgrnC3DHsHRIbHcCHIeG/IJvoeFqFM2sTdhSOLJrf8lOPrA== dependencies: "@babel/core" "^7.25.2" "@babel/plugin-proposal-export-default-from" "^7.24.7" @@ -3390,141 +3763,144 @@ "@babel/plugin-transform-typescript" "^7.25.2" "@babel/plugin-transform-unicode-regex" "^7.24.7" "@babel/template" "^7.25.0" - "@react-native/babel-plugin-codegen" "0.79.6" - babel-plugin-syntax-hermes-parser "0.25.1" + "@react-native/babel-plugin-codegen" "0.81.5" + babel-plugin-syntax-hermes-parser "0.29.1" babel-plugin-transform-flow-enums "^0.0.2" react-refresh "^0.14.0" -"@react-native/codegen@0.79.6": - version "0.79.6" - resolved "https://registry.yarnpkg.com/@react-native/codegen/-/codegen-0.79.6.tgz#25e9bb68ce02afcdb01b9b2b0bf8a3a7fd99bf8b" - integrity sha512-iRBX8Lgbqypwnfba7s6opeUwVyaR23mowh9ILw7EcT2oLz3RqMmjJdrbVpWhGSMGq2qkPfqAH7bhO8C7O+xfjQ== +"@react-native/codegen@0.81.5": + version "0.81.5" + resolved "https://registry.yarnpkg.com/@react-native/codegen/-/codegen-0.81.5.tgz#d4dec668c94b9d58a5c2dbdbf026db331e1b6b27" + integrity sha512-a2TDA03Up8lpSa9sh5VRGCQDXgCTOyDOFH+aqyinxp1HChG8uk89/G+nkJ9FPd0rqgi25eCTR16TWdS3b+fA6g== dependencies: "@babel/core" "^7.25.2" "@babel/parser" "^7.25.3" glob "^7.1.1" - hermes-parser "0.25.1" + hermes-parser "0.29.1" invariant "^2.2.4" nullthrows "^1.1.1" yargs "^17.6.2" -"@react-native/community-cli-plugin@0.79.6": - version "0.79.6" - resolved "https://registry.yarnpkg.com/@react-native/community-cli-plugin/-/community-cli-plugin-0.79.6.tgz#6d95bc10b0dff0150f8e971b4b0f0867b8c0c06c" - integrity sha512-ZHVst9vByGsegeaddkD2YbZ6NvYb4n3pD9H7Pit94u+NlByq2uBJghoOjT6EKqg+UVl8tLRdi88cU2pDPwdHqA== +"@react-native/community-cli-plugin@0.81.5": + version "0.81.5" + resolved "https://registry.yarnpkg.com/@react-native/community-cli-plugin/-/community-cli-plugin-0.81.5.tgz#617789cda4da419d03dda00e2a78c36188b4391e" + integrity sha512-yWRlmEOtcyvSZ4+OvqPabt+NS36vg0K/WADTQLhrYrm9qdZSuXmq8PmdJWz/68wAqKQ+4KTILiq2kjRQwnyhQw== dependencies: - "@react-native/dev-middleware" "0.79.6" - chalk "^4.0.0" - debug "^2.2.0" + "@react-native/dev-middleware" "0.81.5" + debug "^4.4.0" invariant "^2.2.4" - metro "^0.82.0" - metro-config "^0.82.0" - metro-core "^0.82.0" + metro "^0.83.1" + metro-config "^0.83.1" + metro-core "^0.83.1" semver "^7.1.3" -"@react-native/debugger-frontend@0.79.6": - version "0.79.6" - resolved "https://registry.yarnpkg.com/@react-native/debugger-frontend/-/debugger-frontend-0.79.6.tgz#ec0ea9c2f140a564d26789a18dc097519f1b9c48" - integrity sha512-lIK/KkaH7ueM22bLO0YNaQwZbT/oeqhaghOvmZacaNVbJR1Cdh/XAqjT8FgCS+7PUnbxA8B55NYNKGZG3O2pYw== +"@react-native/debugger-frontend@0.81.5": + version "0.81.5" + resolved "https://registry.yarnpkg.com/@react-native/debugger-frontend/-/debugger-frontend-0.81.5.tgz#82ece0181e9a7a3dcbdfa86cf9decd654e13f81f" + integrity sha512-bnd9FSdWKx2ncklOetCgrlwqSGhMHP2zOxObJbOWXoj7GHEmih4MKarBo5/a8gX8EfA1EwRATdfNBQ81DY+h+w== -"@react-native/dev-middleware@0.79.6": - version "0.79.6" - resolved "https://registry.yarnpkg.com/@react-native/dev-middleware/-/dev-middleware-0.79.6.tgz#62a4c0b987e5d100eae3e8c95c58ae1c8abe377a" - integrity sha512-BK3GZBa9c7XSNR27EDRtxrgyyA3/mf1j3/y+mPk7Ac0Myu85YNrXnC9g3mL5Ytwo0g58TKrAIgs1fF2Q5Mn6mQ== +"@react-native/dev-middleware@0.81.5": + version "0.81.5" + resolved "https://registry.yarnpkg.com/@react-native/dev-middleware/-/dev-middleware-0.81.5.tgz#81e8ac545d7736ef6ebb2e59fdbaebc5cf9aedec" + integrity sha512-WfPfZzboYgo/TUtysuD5xyANzzfka8Ebni6RIb2wDxhb56ERi7qDrE4xGhtPsjCL4pQBXSVxyIlCy0d8I6EgGA== dependencies: "@isaacs/ttlcache" "^1.4.1" - "@react-native/debugger-frontend" "0.79.6" + "@react-native/debugger-frontend" "0.81.5" chrome-launcher "^0.15.2" chromium-edge-launcher "^0.2.0" connect "^3.6.5" - debug "^2.2.0" + debug "^4.4.0" invariant "^2.2.4" nullthrows "^1.1.1" open "^7.0.3" serve-static "^1.16.2" ws "^6.2.3" -"@react-native/gradle-plugin@0.79.6": - version "0.79.6" - resolved "https://registry.yarnpkg.com/@react-native/gradle-plugin/-/gradle-plugin-0.79.6.tgz#02d996aae3df87512c2a56e1f5fefffc883c8a18" - integrity sha512-C5odetI6py3CSELeZEVz+i00M+OJuFZXYnjVD4JyvpLn462GesHRh+Se8mSkU5QSaz9cnpMnyFLJAx05dokWbA== +"@react-native/gradle-plugin@0.81.5": + version "0.81.5" + resolved "https://registry.yarnpkg.com/@react-native/gradle-plugin/-/gradle-plugin-0.81.5.tgz#a58830f38789f6254b64449a17fe57455b589d00" + integrity sha512-hORRlNBj+ReNMLo9jme3yQ6JQf4GZpVEBLxmTXGGlIL78MAezDZr5/uq9dwElSbcGmLEgeiax6e174Fie6qPLg== -"@react-native/js-polyfills@0.79.6": - version "0.79.6" - resolved "https://registry.yarnpkg.com/@react-native/js-polyfills/-/js-polyfills-0.79.6.tgz#11dab284ace2708f0483833cfff0c9aee81274df" - integrity sha512-6wOaBh1namYj9JlCNgX2ILeGUIwc6OP6MWe3Y5jge7Xz9fVpRqWQk88Q5Y9VrAtTMTcxoX3CvhrfRr3tGtSfQw== +"@react-native/js-polyfills@0.81.5": + version "0.81.5" + resolved "https://registry.yarnpkg.com/@react-native/js-polyfills/-/js-polyfills-0.81.5.tgz#2ca68188c8fff9b951f507b1dec7efe928848274" + integrity sha512-fB7M1CMOCIUudTRuj7kzxIBTVw2KXnsgbQ6+4cbqSxo8NmRRhA0Ul4ZUzZj3rFd3VznTL4Brmocv1oiN0bWZ8w== -"@react-native/normalize-colors@0.79.6": - version "0.79.6" - resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.79.6.tgz#e076519b6dba9150dad7f935c1b0a64ea0a90033" - integrity sha512-0v2/ruY7eeKun4BeKu+GcfO+SHBdl0LJn4ZFzTzjHdWES0Cn+ONqKljYaIv8p9MV2Hx/kcdEvbY4lWI34jC/mQ== +"@react-native/normalize-colors@0.81.5": + version "0.81.5" + resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.81.5.tgz#1ca6cb6772bb7324df2b11aab35227eacd6bdfe7" + integrity sha512-0HuJ8YtqlTVRXGZuGeBejLE04wSQsibpTI+RGOyVqxZvgtlLLC/Ssw0UmbHhT4lYMp2fhdtvKZSs5emWB1zR/g== "@react-native/normalize-colors@^0.74.1": version "0.74.89" resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.74.89.tgz#b8ac17d1bbccd3ef9a1f921665d04d42cff85976" integrity sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg== -"@react-native/virtualized-lists@0.79.6": - version "0.79.6" - resolved "https://registry.yarnpkg.com/@react-native/virtualized-lists/-/virtualized-lists-0.79.6.tgz#ab395e3a1edba1c8c564d3a85961f213cc164a99" - integrity sha512-khA/Hrbb+rB68YUHrLubfLgMOD9up0glJhw25UE3Kntj32YDyuO0Tqc81ryNTcCekFKJ8XrAaEjcfPg81zBGPw== +"@react-native/virtualized-lists@0.81.5": + version "0.81.5" + resolved "https://registry.yarnpkg.com/@react-native/virtualized-lists/-/virtualized-lists-0.81.5.tgz#24123fded16992d7e46ecc4ccd473be939ea8c1b" + integrity sha512-UVXgV/db25OPIvwZySeToXD/9sKKhOdkcWmmf4Jh8iBZuyfML+/5CasaZ1E7Lqg6g3uqVQq75NqIwkYmORJMPw== dependencies: invariant "^2.2.4" nullthrows "^1.1.1" -"@react-navigation/bottom-tabs@^7.3.10": - version "7.4.7" - resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-7.4.7.tgz#c6fb80bfe25f47db27491918a764e01877f7efeb" - integrity sha512-SQ4KuYV9yr3SV/thefpLWhAD0CU2CrBMG1l0w/QKl3GYuGWdN5OQmdQdmaPZGtsjjVOb+N9Qo7Tf6210P4TlpA== +"@react-navigation/bottom-tabs@^7.4.0": + version "7.15.9" + resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-7.15.9.tgz#f9789b73d23f4e79f0a4a0cb5b61c38054d3c71d" + integrity sha512-Ou28A1aZLj5wiFQ3F93aIsrI4NCwn3IJzkkjNo9KLFXsc0Yks+UqrVaFlffHFLsrbajuGRG/OQpnMA1ljayY5Q== dependencies: - "@react-navigation/elements" "^2.6.4" + "@react-navigation/elements" "^2.9.14" color "^4.2.3" + sf-symbols-typescript "^2.1.0" -"@react-navigation/core@^7.12.4": - version "7.12.4" - resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-7.12.4.tgz#73cc4c0989455c93bf21d7aeecc89d3a7006ccde" - integrity sha512-xLFho76FA7v500XID5z/8YfGTvjQPw7/fXsq4BIrVSqetNe/o/v+KAocEw4ots6kyv3XvSTyiWKh2g3pN6xZ9Q== +"@react-navigation/core@^7.17.2": + version "7.17.2" + resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-7.17.2.tgz#8a17b73faf7c0688a4749dcac8c7350d8f93e943" + integrity sha512-Rt2OZwcgOmjv401uLGAKaRM6xo0fiBce/A7LfRHI1oe5FV+KooWcgAoZ2XOtgKj6UzVMuQWt3b2e6rxo/mDJRA== dependencies: - "@react-navigation/routers" "^7.5.1" + "@react-navigation/routers" "^7.5.3" escape-string-regexp "^4.0.0" + fast-deep-equal "^3.1.3" nanoid "^3.3.11" query-string "^7.1.3" react-is "^19.1.0" use-latest-callback "^0.2.4" use-sync-external-store "^1.5.0" -"@react-navigation/elements@^2.6.4": - version "2.6.4" - resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-2.6.4.tgz#f1dc8548b1289588fabcd2f0342c1391c689a49f" - integrity sha512-O3X9vWXOEhAO56zkQS7KaDzL8BvjlwZ0LGSteKpt1/k6w6HONG+2Wkblrb057iKmehTkEkQMzMLkXiuLmN5x9Q== +"@react-navigation/elements@^2.9.14": + version "2.9.14" + resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-2.9.14.tgz#48b9e3cf16e38818df1e633a13a17c0fa96e9c43" + integrity sha512-lKqzu+su2pI/YIZmR7L7xdOs4UL+rVXKJAMpRMBrwInEy96SjIFst6QDGpE89Dunnu3VjVpjWfByo9f2GWBHDQ== dependencies: color "^4.2.3" use-latest-callback "^0.2.4" use-sync-external-store "^1.5.0" -"@react-navigation/native-stack@^7.3.10": - version "7.3.26" - resolved "https://registry.yarnpkg.com/@react-navigation/native-stack/-/native-stack-7.3.26.tgz#a08ee0626e49428a808da9d810f24db5b08deae9" - integrity sha512-EjaBWzLZ76HJGOOcWCFf+h/M+Zg7M1RalYioDOb6ZdXHz7AwYNidruT3OUAQgSzg3gVLqvu5OYO0jFsNDPCZxQ== +"@react-navigation/native-stack@^7.3.16": + version "7.14.10" + resolved "https://registry.yarnpkg.com/@react-navigation/native-stack/-/native-stack-7.14.10.tgz#f83ff598bbadcaec57412301f2258183af244085" + integrity sha512-mCbYbYhi7Em2R2nEgwYGdLU38smy+KK+HMMVcwuzllWsF3Qb+jOUEYbB6Or7LvE7SS77BZ6sHdx4HptCEv50hQ== dependencies: - "@react-navigation/elements" "^2.6.4" + "@react-navigation/elements" "^2.9.14" + color "^4.2.3" + sf-symbols-typescript "^2.1.0" warn-once "^0.1.1" -"@react-navigation/native@^7.1.6": - version "7.1.17" - resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-7.1.17.tgz#88d557c0f5000aa2741e4368c59719526f1394c4" - integrity sha512-uEcYWi1NV+2Qe1oELfp9b5hTYekqWATv2cuwcOAg5EvsIsUPtzFrKIasgUXLBRGb9P7yR5ifoJ+ug4u6jdqSTQ== +"@react-navigation/native@^7.1.8": + version "7.2.2" + resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-7.2.2.tgz#c9438fe8393454d74fdb7f959ac9abede52b1f8e" + integrity sha512-kem1Ko2BcbAjmbQIv66dNmr6EtfDut3QU0qjsVhMnLLhktwyXb6FzZYp8gTrUb6AvkAbaJoi+BF5Pl55pAUa5w== dependencies: - "@react-navigation/core" "^7.12.4" + "@react-navigation/core" "^7.17.2" escape-string-regexp "^4.0.0" fast-deep-equal "^3.1.3" nanoid "^3.3.11" use-latest-callback "^0.2.4" -"@react-navigation/routers@^7.5.1": - version "7.5.1" - resolved "https://registry.yarnpkg.com/@react-navigation/routers/-/routers-7.5.1.tgz#b8f6e9b491fdc1bc7164fdac4fa4faa82f397daf" - integrity sha512-pxipMW/iEBSUrjxz2cDD7fNwkqR4xoi0E/PcfTQGCcdJwLoaxzab5kSadBLj1MTJyT0YRrOXL9umHpXtp+Dv4w== +"@react-navigation/routers@^7.5.3": + version "7.5.3" + resolved "https://registry.yarnpkg.com/@react-navigation/routers/-/routers-7.5.3.tgz#8002930ef5f62351be2475d0dffde3ffaee174d7" + integrity sha512-1tJHg4KKRJuQ1/EvJxatrMef3NZXEPzwUIUZ3n1yJ2t7Q97siwRtbynRpQG9/69ebbtiZ8W3ScOZF/OmhvM4Rg== dependencies: nanoid "^3.3.11" @@ -4009,96 +4385,96 @@ micromatch "^4.0.0" p-reduce "^2.0.0" -"@sentry-internal/browser-utils@8.54.0": - version "8.54.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.54.0.tgz#2d68c7fa843db867ed98059faf1a750be3eca95a" - integrity sha512-DKWCqb4YQosKn6aD45fhKyzhkdG7N6goGFDeyTaJFREJDFVDXiNDsYZu30nJ6BxMM7uQIaARhPAC5BXfoED3pQ== +"@sentry-internal/browser-utils@10.12.0": + version "10.12.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-10.12.0.tgz#aa3a05653e530d2693e307c0131571ee8a97b60d" + integrity sha512-dozbx389jhKynj0d657FsgbBVOar7pX3mb6GjqCxslXF0VKpZH2Xks0U32RgDY/nK27O+o095IWz7YvjVmPkDw== dependencies: - "@sentry/core" "8.54.0" + "@sentry/core" "10.12.0" -"@sentry-internal/feedback@8.54.0": - version "8.54.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.54.0.tgz#52c3a63aa5b520eca7acfa1376621e8441984126" - integrity sha512-nQqRacOXoElpE0L0ADxUUII0I3A94niqG9Z4Fmsw6057QvyrV/LvTiMQBop6r5qLjwMqK+T33iR4/NQI5RhsXQ== +"@sentry-internal/feedback@10.12.0": + version "10.12.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-10.12.0.tgz#a48039507f37fe62e19566128a894661a724ef0d" + integrity sha512-0+7ceO6yQPPqfxRc9ue/xoPHKcnB917ezPaehGQNfAFNQB9PNTG1y55+8mRu0Fw+ANbZeCt/HyoCmXuRdxmkpg== dependencies: - "@sentry/core" "8.54.0" + "@sentry/core" "10.12.0" -"@sentry-internal/replay-canvas@8.54.0": - version "8.54.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.54.0.tgz#e57a3893db2bb0ea7ad9dc2a804bb035142fe3ba" - integrity sha512-K/On3OAUBeq/TV2n+1EvObKC+WMV9npVXpVyJqCCyn8HYMm8FUGzuxeajzm0mlW4wDTPCQor6mK9/IgOquUzCw== +"@sentry-internal/replay-canvas@10.12.0": + version "10.12.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-10.12.0.tgz#f79dde92bcba67b4f706db6c217467e14d6348c5" + integrity sha512-W/z1/+69i3INNfPjD1KuinSNaRQaApjzwb37IFmiyF440F93hxmEYgXHk3poOlYYaigl2JMYbysGPWOiXnqUXA== dependencies: - "@sentry-internal/replay" "8.54.0" - "@sentry/core" "8.54.0" + "@sentry-internal/replay" "10.12.0" + "@sentry/core" "10.12.0" -"@sentry-internal/replay@8.54.0": - version "8.54.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.54.0.tgz#b92990a51ffbe8d92998ff8188db9e3a6f9d1e18" - integrity sha512-8xuBe06IaYIGJec53wUC12tY2q4z2Z0RPS2s1sLtbA00EvK1YDGuXp96IDD+HB9mnDMrQ/jW5f97g9TvPsPQUg== +"@sentry-internal/replay@10.12.0": + version "10.12.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-10.12.0.tgz#47ca89acdc621217991c7ed1d133fd37915a512d" + integrity sha512-/1093gSNGN5KlOBsuyAl33JkzGiG38kCnxswQLZWpPpR6LBbR1Ddb18HjhDpoQNNEZybJBgJC3a5NKl43C2TSQ== dependencies: - "@sentry-internal/browser-utils" "8.54.0" - "@sentry/core" "8.54.0" + "@sentry-internal/browser-utils" "10.12.0" + "@sentry/core" "10.12.0" -"@sentry/babel-plugin-component-annotate@3.4.0": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-3.4.0.tgz#f47a7652e16f84556df82cbc38f0004bca1335d1" - integrity sha512-tSzfc3aE7m0PM0Aj7HBDet5llH9AB9oc+tBQ8AvOqUSnWodLrNCuWeQszJ7mIBovD3figgCU3h0cvI6U5cDtsg== - -"@sentry/browser@8.54.0": - version "8.54.0" - resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.54.0.tgz#5487075908aac564892e689e1b6d233fdb314f5b" - integrity sha512-BgUtvxFHin0fS0CmJVKTLXXZcke0Av729IVfi+2fJ4COX8HO7/HAP02RKaSQGmL2HmvWYTfNZ7529AnUtrM4Rg== - dependencies: - "@sentry-internal/browser-utils" "8.54.0" - "@sentry-internal/feedback" "8.54.0" - "@sentry-internal/replay" "8.54.0" - "@sentry-internal/replay-canvas" "8.54.0" - "@sentry/core" "8.54.0" - -"@sentry/cli-darwin@2.45.0": - version "2.45.0" - resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.45.0.tgz#e3d6feae4fadcfdf91db9c7b9c4689a66d3d8d19" - integrity sha512-p4Uxfv/L2fQdP3/wYnKVVz9gzZJf/1Xp9D+6raax/3Bu5y87yHYUqcdt98y/VAXQD4ofp2QgmhGUVPofvQNZmg== - -"@sentry/cli-linux-arm64@2.45.0": - version "2.45.0" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.45.0.tgz#384c8e17f7e7dc007d164033d0e7c75aa83a2e9b" - integrity sha512-gUcLoEjzg7AIc4QQGEZwRHri+EHf3Gcms9zAR1VHiNF3/C/jL4WeDPJF2YiWAQt6EtH84tHiyhw1Ab/R8XFClg== - -"@sentry/cli-linux-arm@2.45.0": - version "2.45.0" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.45.0.tgz#b9d6f86f3934b4d9ced5b45a8158ff2ac2bdd25d" - integrity sha512-6sEskFLlFKJ+e0MOYgIclBTUX5jYMyYhHIxXahEkI/4vx6JO0uvpyRAkUJRpJkRh/lPog0FM+tbP3so+VxB2qQ== - -"@sentry/cli-linux-i686@2.45.0": - version "2.45.0" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.45.0.tgz#39e22beb84cfa26e11bdc198364315fdfb4da4d5" - integrity sha512-VmmOaEAzSW23YdGNdy/+oQjCNAMY+HmOGA77A25/ep/9AV7PQB6FI7xO5Y1PVvlkxZFJ23e373njSsEeg4uDZw== - -"@sentry/cli-linux-x64@2.45.0": - version "2.45.0" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.45.0.tgz#25cd3699297f9433835fb5edd42dad722c11f041" - integrity sha512-a0Oj68mrb25a0WjX/ShZ6AAd4PPiuLcgyzQr7bl2+DvYxIOajwkGbR+CZFEhOVZcfhTnixKy/qIXEzApEPHPQg== - -"@sentry/cli-win32-arm64@2.45.0": - version "2.45.0" - resolved "https://registry.yarnpkg.com/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.45.0.tgz#50c7d29ea2169bdb4d98bbde81c5f7dac0dd3955" - integrity sha512-vn+CwS4p+52pQSLNPoi20ZOrQmv01ZgAmuMnjkh1oUZfTyBAwWLrAh6Cy4cztcN8DfL5dOWKQBo8DBKURE4ttg== - -"@sentry/cli-win32-i686@2.45.0": - version "2.45.0" - resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.45.0.tgz#201075c4aec37a3e797160e0b468641245437f0c" - integrity sha512-8mMoDdlwxtcdNIMtteMK7dbi7054jak8wKSHJ5yzMw8UmWxC5thc/gXBc1uPduiaI56VjoJV+phWHBKCD+6I4w== - -"@sentry/cli-win32-x64@2.45.0": - version "2.45.0" - resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.45.0.tgz#2075e9e1ea3c3609e0fa1a758ca033e94e1c600f" - integrity sha512-ZvK9cIqFaq7vZ0jkHJ/xh5au6902Dr+AUxSk6L6vCL7JCe2p93KGL/4d8VFB5PD/P7Y9b+105G/e0QIFKzpeOw== - -"@sentry/cli@2.45.0": - version "2.45.0" - resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.45.0.tgz#35feed7a2fee54faf25daed73001a2a2a3143396" - integrity sha512-4sWu7zgzgHAjIxIjXUA/66qgeEf5ZOlloO+/JaGD5qXNSW0G7KMTR6iYjReNKMgdBCTH6bUUt9qiuA+Ex9Masw== +"@sentry/babel-plugin-component-annotate@4.3.0": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-4.3.0.tgz#c5b6cbb986952596d3ad233540a90a1fd18bad80" + integrity sha512-OuxqBprXRyhe8Pkfyz/4yHQJc5c3lm+TmYWSSx8u48g5yKewSQDOxkiLU5pAk3WnbLPy8XwU/PN+2BG0YFU9Nw== + +"@sentry/browser@10.12.0": + version "10.12.0" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-10.12.0.tgz#79dffc88e1f9241b9fdb5def5a7c6809f41230b3" + integrity sha512-lKyaB2NFmr7SxPjmMTLLhQ7xfxaY3kdkMhpzuRI5qwOngtKt4+FtvNYHRuz+PTtEFv4OaHhNNbRn6r91gWguQg== + dependencies: + "@sentry-internal/browser-utils" "10.12.0" + "@sentry-internal/feedback" "10.12.0" + "@sentry-internal/replay" "10.12.0" + "@sentry-internal/replay-canvas" "10.12.0" + "@sentry/core" "10.12.0" + +"@sentry/cli-darwin@2.55.0": + version "2.55.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.55.0.tgz#79513547d15223d51905e94d8c1e2bc7377cfdf1" + integrity sha512-jGHE7SHHzqXUmnsmRLgorVH6nmMmTjQQXdPZbSL5tRtH8d3OIYrVNr5D72DSgD26XAPBDMV0ibqOQ9NKoiSpfA== + +"@sentry/cli-linux-arm64@2.55.0": + version "2.55.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.55.0.tgz#5a2e8ea6e088f4884cbe272525f0781f8c748ff3" + integrity sha512-jNB/0/gFcOuDCaY/TqeuEpsy/k52dwyk1SOV3s1ku4DUsln6govTppeAGRewY3T1Rj9B2vgIWTrnB8KVh9+Rgg== + +"@sentry/cli-linux-arm@2.55.0": + version "2.55.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.55.0.tgz#54c330471c4b23ff6769bfd886f092afba3380ce" + integrity sha512-ATjU0PsiWADSPLF/kZroLZ7FPKd5W9TDWHVkKNwIUNTei702LFgTjNeRwOIzTgSvG3yTmVEqtwFQfFN/7hnVXQ== + +"@sentry/cli-linux-i686@2.55.0": + version "2.55.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.55.0.tgz#12c64453ef7014ba89c885497e3c9764a5f3e0a0" + integrity sha512-8LZjo6PncTM6bWdaggscNOi5r7F/fqRREsCwvd51dcjGj7Kp1plqo9feEzYQ+jq+KUzVCiWfHrUjddFmYyZJrg== + +"@sentry/cli-linux-x64@2.55.0": + version "2.55.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.55.0.tgz#c640464fe533fe87e57d0ca5585e0c504e0ef5b3" + integrity sha512-5LUVvq74Yj2cZZy5g5o/54dcWEaX4rf3myTHy73AKhRj1PABtOkfexOLbF9xSrZy95WXWaXyeH+k5n5z/vtHfA== + +"@sentry/cli-win32-arm64@2.55.0": + version "2.55.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.55.0.tgz#da1e8ba13083c281fcdf72d1dd35255df7fdb5c5" + integrity sha512-cWIQdzm1pfLwPARsV6dUb8TVd6Y3V1A2VWxjTons3Ift6GvtVmiAe0OWL8t2Yt95i8v61kTD/6Tq21OAaogqzA== + +"@sentry/cli-win32-i686@2.55.0": + version "2.55.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.55.0.tgz#c2eae0a75fc55f101c31de0999214d7e613d65c4" + integrity sha512-ldepCn2t9r4I0wvgk7NRaA7coJyy4rTQAzM66u9j5nTEsUldf66xym6esd5ZZRAaJUjffqvHqUIr/lrieTIrVg== + +"@sentry/cli-win32-x64@2.55.0": + version "2.55.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.55.0.tgz#499e6697663a3e1453ff2e921bf3876f774520b5" + integrity sha512-4hPc/I/9tXx+HLTdTGwlagtAfDSIa2AoTUP30tl32NAYQhx9a6niUbPAemK2qfxesiufJ7D2djX83rCw6WnJVA== + +"@sentry/cli@2.55.0": + version "2.55.0" + resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.55.0.tgz#c22d2edbd320242e7881c15ecf89649378c05387" + integrity sha512-cynvcIM2xL8ddwELyFRSpZQw4UtFZzoM2rId2l9vg7+wDREPDocMJB9lEQpBIo3eqhp9JswqUT037yjO6iJ5Sw== dependencies: https-proxy-agent "^5.0.0" node-fetch "^2.6.7" @@ -4106,62 +4482,53 @@ proxy-from-env "^1.1.0" which "^2.0.2" optionalDependencies: - "@sentry/cli-darwin" "2.45.0" - "@sentry/cli-linux-arm" "2.45.0" - "@sentry/cli-linux-arm64" "2.45.0" - "@sentry/cli-linux-i686" "2.45.0" - "@sentry/cli-linux-x64" "2.45.0" - "@sentry/cli-win32-arm64" "2.45.0" - "@sentry/cli-win32-i686" "2.45.0" - "@sentry/cli-win32-x64" "2.45.0" - -"@sentry/core@8.54.0": - version "8.54.0" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.54.0.tgz#a2ebec965cadcb6de89e116689feeef79d5862a6" - integrity sha512-03bWf+D1j28unOocY/5FDB6bUHtYlm6m6ollVejhg45ZmK9iPjdtxNWbrLsjT1WRym0Tjzowu+A3p+eebYEv0Q== - -"@sentry/react-native@~6.14.0": - version "6.14.0" - resolved "https://registry.yarnpkg.com/@sentry/react-native/-/react-native-6.14.0.tgz#bc6bdaf03860bb8946f8c30570a9abd82ed6cfc0" - integrity sha512-BBqixN6oV6tCNp1ABXfzvD531zxj1fUAH0HDPvOR/jX0h9f9pYfxCyI64B+DoQbVZKFsg8nte0QIHkZDhRAW9A== - dependencies: - "@sentry/babel-plugin-component-annotate" "3.4.0" - "@sentry/browser" "8.54.0" - "@sentry/cli" "2.45.0" - "@sentry/core" "8.54.0" - "@sentry/react" "8.54.0" - "@sentry/types" "8.54.0" - "@sentry/utils" "8.54.0" - -"@sentry/react@8.54.0": - version "8.54.0" - resolved "https://registry.yarnpkg.com/@sentry/react/-/react-8.54.0.tgz#16cec103b5d5697bdfebacf6e2d35f19699b3ab3" - integrity sha512-42T/fp8snYN19Fy/2P0Mwotu4gcdy+1Lx+uYCNcYP1o7wNGigJ7qb27sW7W34GyCCHjoCCfQgeOqDQsyY8LC9w== - dependencies: - "@sentry/browser" "8.54.0" - "@sentry/core" "8.54.0" - hoist-non-react-statics "^3.3.2" + "@sentry/cli-darwin" "2.55.0" + "@sentry/cli-linux-arm" "2.55.0" + "@sentry/cli-linux-arm64" "2.55.0" + "@sentry/cli-linux-i686" "2.55.0" + "@sentry/cli-linux-x64" "2.55.0" + "@sentry/cli-win32-arm64" "2.55.0" + "@sentry/cli-win32-i686" "2.55.0" + "@sentry/cli-win32-x64" "2.55.0" + +"@sentry/core@10.12.0": + version "10.12.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-10.12.0.tgz#3f6a0f5c2f63f2c1761e3cf442a986d74adf6403" + integrity sha512-Jrf0Yo7DvmI/ZQcvBnA0xKNAFkJlVC/fMlvcin+5IrFNRcqOToZ2vtF+XqTgjRZymXQNE8s1QTD7IomPHk0TAw== -"@sentry/types@8.54.0": - version "8.54.0" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.54.0.tgz#1d57bb094443081de4e0d8b638e6ebc40f5ddd36" - integrity sha512-wztdtr7dOXQKi0iRvKc8XJhJ7HaAfOv8lGu0yqFOFwBZucO/SHnu87GOPi8mvrTiy1bentQO5l+zXWAaMvG4uw== +"@sentry/react-native@~7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@sentry/react-native/-/react-native-7.2.0.tgz#d29deacc36fd55fc52119ffcf53f61c21cab3600" + integrity sha512-rjqYgEjntPz1sPysud78wi4B9ui7LBVPsG6qr8s/htLMYho9GPGFA5dF+eqsQWqMX8NDReAxNkLTC4+gCNklLQ== + dependencies: + "@sentry/babel-plugin-component-annotate" "4.3.0" + "@sentry/browser" "10.12.0" + "@sentry/cli" "2.55.0" + "@sentry/core" "10.12.0" + "@sentry/react" "10.12.0" + "@sentry/types" "10.12.0" + +"@sentry/react@10.12.0": + version "10.12.0" + resolved "https://registry.yarnpkg.com/@sentry/react/-/react-10.12.0.tgz#c121f37bf582f4851108f67ef492de6a4c8f7a8d" + integrity sha512-TpqgdoYbkf5JynmmW2oQhHQ/h5w+XPYk0cEb/UrsGlvJvnBSR+5tgh0AqxCSi3gvtp82rAXI5w1TyRPBbhLDBw== dependencies: - "@sentry/core" "8.54.0" + "@sentry/browser" "10.12.0" + "@sentry/core" "10.12.0" + hoist-non-react-statics "^3.3.2" -"@sentry/utils@8.54.0": - version "8.54.0" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.54.0.tgz#5e28e03a249451b4a55200a0787f4e2c59bab2c5" - integrity sha512-JL8UDjrsKxKclTdLXfuHfE7B3KbrAPEYP7tMyN/xiO2vsF6D84fjwYyalO0ZMtuFZE6vpSze8ZOLEh6hLnPYsw== +"@sentry/types@10.12.0": + version "10.12.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-10.12.0.tgz#86d24346efde7b0757474537af7ae4b3d931d6a8" + integrity sha512-sKGj3l3V8ZKISh2Tu88bHfnm5ztkRtSLdmpZ6TmCeJdSM9pV+RRd6CMJ0RnSEXmYHselPNUod521t2NQFd4W1w== dependencies: - "@sentry/core" "8.54.0" + "@sentry/core" "10.12.0" -"@shopify/flash-list@1.7.6": - version "1.7.6" - resolved "https://registry.yarnpkg.com/@shopify/flash-list/-/flash-list-1.7.6.tgz#367e76866c71d1f1be0ff70f0b28be4bbfbcf595" - integrity sha512-0kuuAbWgy4YSlN05mt0ScvxK8uiDixMsICWvDed+LTxvZ5+5iRyt3M8cRLUroB8sfiZlJJZWlxHrx0frBpsYOQ== +"@shopify/flash-list@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@shopify/flash-list/-/flash-list-2.0.2.tgz#644748f883fccf8cf2e0ca251e0ef88673b89120" + integrity sha512-zhlrhA9eiuEzja4wxVvotgXHtqd3qsYbXkQ3rsBfOgbFA9BVeErpDE/yEwtlIviRGEqpuFj/oU5owD6ByaNX+w== dependencies: - recyclerlistview "4.2.3" tslib "2.8.1" "@sinclair/typebox@^0.27.8": @@ -4756,10 +5123,10 @@ resolved "https://registry.yarnpkg.com/@types/react-native-base64/-/react-native-base64-0.2.2.tgz#d4e1d537e6d547d23d96a1e64627acc13587ae6b" integrity sha512-obr+/L9Jaxdr+xCVS/IQcYgreg5xtnui4Wqw/G1acBUtW2CnqVJj6lK6F/5F3+5d2oZEo5xDDLqy8GVn2HbEmw== -"@types/react@~19.0.10": - version "19.0.14" - resolved "https://registry.yarnpkg.com/@types/react/-/react-19.0.14.tgz#f2f62035290afd755095cb6644e10b599db72f4e" - integrity sha512-ixLZ7zG7j1fM0DijL9hDArwhwcCb4vqmePgwtV0GfnkHRSCUEv4LvzarcTdhoqgyMznUx/EhoTUv31CKZzkQlw== +"@types/react@~19.1.10": + version "19.1.17" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.17.tgz#8be0b9c546cede389b930a98eb3fad1897f209c3" + integrity sha512-Qec1E3mhALmaspIrhWt9jkQMNdw6bReVu64mjvhbhq2NFPftLPVr+l1SZgmw/66WwBNpDh7ao5AT6gF5v41PFA== dependencies: csstype "^3.0.2" @@ -5157,6 +5524,14 @@ accepts@^1.3.7, accepts@^1.3.8: mime-types "~2.1.34" negotiator "0.6.3" +accepts@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895" + integrity sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng== + dependencies: + mime-types "^3.0.0" + negotiator "^1.0.0" + acorn-globals@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" @@ -5170,13 +5545,6 @@ acorn-jsx@^5.3.2: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-loose@^8.3.0: - version "8.5.2" - resolved "https://registry.yarnpkg.com/acorn-loose/-/acorn-loose-8.5.2.tgz#a7cc7dfbb7c8f3c2e55b055db640dc657e278d26" - integrity sha512-PPvV6g8UGMGgjrMu+n/f9E/tCSkNQ2Y97eFvuVdJfG11+xdIeDcLyNdC8SHcrHbRqkfwLASdplyR6B6sKM1U4A== - dependencies: - acorn "^8.15.0" - acorn-walk@^8.0.2, acorn-walk@^8.1.1: version "8.3.4" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" @@ -5222,16 +5590,6 @@ ajv-keywords@^3.4.1: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@8.11.0: - version "8.11.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" - integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - ajv@^6.10.0, ajv@^6.12.0, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -5451,6 +5809,13 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +aria-hidden@^1.2.4: + version "1.2.6" + resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.6.tgz#73051c9b088114c795b1ea414e9c0fff874ffc1a" + integrity sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA== + dependencies: + tslib "^2.0.0" + aria-query@^5.0.0: version "5.3.2" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" @@ -5777,17 +6142,24 @@ babel-plugin-polyfill-regenerator@^0.6.5: dependencies: "@babel/helper-define-polyfill-provider" "^0.6.5" -babel-plugin-react-native-web@~0.19.13: - version "0.19.13" - resolved "https://registry.yarnpkg.com/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.19.13.tgz#bf919bd6f18c4689dd1a528a82bda507363b953d" - integrity sha512-4hHoto6xaN23LCyZgL9LJZc3olmAxd7b6jDzlZnKXAh4rRAbZRKNBJoOOdp46OBqgy+K0t0guTj5/mhA8inymQ== +babel-plugin-react-compiler@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-react-compiler/-/babel-plugin-react-compiler-1.0.0.tgz#bdf7360a23a4d5ebfca090255da3893efd07425f" + integrity sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw== + dependencies: + "@babel/types" "^7.26.0" + +babel-plugin-react-native-web@~0.21.0: + version "0.21.2" + resolved "https://registry.yarnpkg.com/babel-plugin-react-native-web/-/babel-plugin-react-native-web-0.21.2.tgz#d2f7fd673278da82577aa583457edb55d9cccbe0" + integrity sha512-SPD0J6qjJn8231i0HZhlAGH6NORe+QvRSQM2mwQEzJ2Fb3E4ruWTiiicPlHjmeWShDXLcvoorOCXjeR7k/lyWA== -babel-plugin-syntax-hermes-parser@0.25.1, babel-plugin-syntax-hermes-parser@^0.25.1: - version "0.25.1" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.25.1.tgz#58b539df973427fcfbb5176a3aec7e5dee793cb0" - integrity sha512-IVNpGzboFLfXZUAwkLFcI/bnqVbwky0jP3eBno4HKtqvQJAHBLdgxiG6lQ4to0+Q/YCN3PO0od5NZwIKyY4REQ== +babel-plugin-syntax-hermes-parser@0.29.1, babel-plugin-syntax-hermes-parser@^0.29.1: + version "0.29.1" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.29.1.tgz#09ca9ecb0330eba1ef939b6d3f1f55bb06a9dc33" + integrity sha512-2WFYnoWGdmih1I1J5eIqxATOeycOqRwYxAQBu3cUu/rhwInwHUg7k60AFNbuGjSDL8tje5GDrAnxzRLcu2pYcA== dependencies: - hermes-parser "0.25.1" + hermes-parser "0.29.1" babel-plugin-transform-flow-enums@^0.0.2: version "0.0.2" @@ -5817,15 +6189,16 @@ babel-preset-current-node-syntax@^1.0.0, babel-preset-current-node-syntax@^1.1.0 "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" -babel-preset-expo@~13.2.4: - version "13.2.4" - resolved "https://registry.yarnpkg.com/babel-preset-expo/-/babel-preset-expo-13.2.4.tgz#ad31bbfc8b3169a5a61108cebdee5350feebc071" - integrity sha512-3IKORo3KR+4qtLdCkZNDj8KeA43oBn7RRQejFGWfiZgu/NeaRUSri8YwYjZqybm7hn3nmMv9OLahlvXBX23o5Q== +babel-preset-expo@~54.0.10: + version "54.0.10" + resolved "https://registry.yarnpkg.com/babel-preset-expo/-/babel-preset-expo-54.0.10.tgz#3b70f4af3a5f65f945d7957ef511ee016e8f2fd6" + integrity sha512-wTt7POavLFypLcPW/uC5v8y+mtQKDJiyGLzYCjqr9tx0Qc3vCXcDKk1iCFIj/++Iy5CWhhTflEa7VvVPNWeCfw== dependencies: "@babel/helper-module-imports" "^7.25.9" "@babel/plugin-proposal-decorators" "^7.12.9" "@babel/plugin-proposal-export-default-from" "^7.24.7" "@babel/plugin-syntax-export-default-from" "^7.24.7" + "@babel/plugin-transform-class-static-block" "^7.27.1" "@babel/plugin-transform-export-namespace-from" "^7.25.9" "@babel/plugin-transform-flow-strip-types" "^7.25.2" "@babel/plugin-transform-modules-commonjs" "^7.24.8" @@ -5836,12 +6209,12 @@ babel-preset-expo@~13.2.4: "@babel/plugin-transform-runtime" "^7.24.7" "@babel/preset-react" "^7.22.15" "@babel/preset-typescript" "^7.23.0" - "@react-native/babel-preset" "0.79.6" - babel-plugin-react-native-web "~0.19.13" - babel-plugin-syntax-hermes-parser "^0.25.1" + "@react-native/babel-preset" "0.81.5" + babel-plugin-react-compiler "^1.0.0" + babel-plugin-react-native-web "~0.21.0" + babel-plugin-syntax-hermes-parser "^0.29.1" babel-plugin-transform-flow-enums "^0.0.2" debug "^4.3.4" - react-refresh "^0.14.2" resolve-from "^5.0.0" babel-preset-jest@30.0.1: @@ -5870,11 +6243,21 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +balanced-match@^4.0.2: + version "4.0.4" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a" + integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== + base64-js@1.5.1, base64-js@^1.2.3, base64-js@^1.3.0, base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +baseline-browser-mapping@^2.10.12: + version "2.10.18" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.18.tgz#565745085ba7743af7d4072707ad132db3a5a42f" + integrity sha512-VSnGQAOLtP5mib/DPyg2/t+Tlv65NTBz83BJBJvmLVHHuKJVaDOBvJJykiT5TR++em5nfAySPccDZDa4oSrn8A== + baseline-browser-mapping@^2.8.3: version "2.8.9" resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.8.9.tgz#fd0b8543c4f172595131e94965335536b3101b75" @@ -5971,6 +6354,13 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" +brace-expansion@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.5.tgz#dcc3a37116b79f3e1b46db994ced5d570e930fdb" + integrity sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ== + dependencies: + balanced-match "^4.0.2" + braces@^3.0.3, braces@~3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" @@ -5989,6 +6379,17 @@ browserslist@^4.24.0, browserslist@^4.25.3: node-releases "^2.0.21" update-browserslist-db "^1.1.3" +browserslist@^4.25.0: + version "4.28.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.2.tgz#f50b65362ef48974ca9f50b3680566d786b811d2" + integrity sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg== + dependencies: + baseline-browser-mapping "^2.10.12" + caniuse-lite "^1.0.30001782" + electron-to-chromium "^1.5.328" + node-releases "^2.0.36" + update-browserslist-db "^1.2.3" + bs-logger@0.x: version "0.2.6" resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" @@ -6143,25 +6544,6 @@ call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: call-bind-apply-helpers "^1.0.2" get-intrinsic "^1.3.0" -caller-callsite@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" - integrity sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ== - dependencies: - callsites "^2.0.0" - -caller-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" - integrity sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A== - dependencies: - caller-callsite "^2.0.0" - -callsites@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" - integrity sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ== - callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -6192,6 +6574,11 @@ caniuse-lite@^1.0.30001741: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001745.tgz#ab2a36e3b6ed5bfb268adc002c476aab6513f859" integrity sha512-ywt6i8FzvdgrrrGbr1jZVObnVv6adj+0if2/omv9cmR2oiZs30zL4DIyaptKcbOrBdOIc74QTMoJvSE2QHh5UQ== +caniuse-lite@^1.0.30001782: + version "1.0.30001787" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001787.tgz#fd25c5e42e2d35df5c75eddda00d15d9c0c68f81" + integrity sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg== + centra@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/centra/-/centra-2.7.0.tgz#4c8312a58436e8a718302011561db7e6a2b0ec18" @@ -6713,16 +7100,6 @@ cosmiconfig-typescript-loader@^6.1.0: dependencies: jiti "^2.4.1" -cosmiconfig@^5.0.5: - version "5.2.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" - cosmiconfig@^8.1.3, cosmiconfig@^8.3.6: version "8.3.6" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" @@ -6796,11 +7173,6 @@ cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.6: shebang-command "^2.0.0" which "^2.0.1" -crypto-random-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" - integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== - css-in-js-utils@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz#640ae6a33646d401fc720c54fc61c42cd76ae2bb" @@ -6948,7 +7320,7 @@ debounce@^1.2.0: resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== -debug@2.6.9, debug@^2.2.0, debug@^2.6.9: +debug@2.6.9, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -7107,11 +7479,6 @@ destroy@1.2.0: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== -detect-libc@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== - detect-libc@^2.0.1: version "2.1.2" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" @@ -7127,6 +7494,11 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +detect-node-es@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" + integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ== + detect-node@^2.0.4: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" @@ -7385,6 +7757,11 @@ electron-to-chromium@^1.5.218: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.227.tgz#c81b6af045b0d6098faed261f0bd611dc282d3a7" integrity sha512-ITxuoPfJu3lsNWUi2lBM2PaBPYgH3uqmxut5vmBxgYvyI4AlJ6P3Cai1O76mOrkJCBzq0IxWg/NtqOrpu/0gKA== +electron-to-chromium@^1.5.328: + version "1.5.335" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.335.tgz#0b957cea44ef86795c227c616d16b4803d119daa" + integrity sha512-q9n5T4BR4Xwa2cwbrwcsDJtHD/enpQ5S1xF1IAtdqf5AAgqDFmR/aakqH3ChFdqd/QXJhS3rnnXFtexU7rax6Q== + electron@^40.1.0: version "40.1.0" resolved "https://registry.yarnpkg.com/electron/-/electron-40.1.0.tgz#e5c45ecd90bfbaa9dd14db2f7fb5ab730e458a9e" @@ -8062,7 +8439,7 @@ event-target-shim@6.0.2: resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-6.0.2.tgz#ea5348c3618ee8b62ff1d344f01908ee2b8a2b71" integrity sha512-8q3LsZjRezbFZ2PN+uP+Q7pnHUMmAOziU2vA2OwoFaKIXxlxl38IylhSSgUorWu/rf4er67w0ikBqjBFk/pomA== -event-target-shim@^5.0.0, event-target-shim@^5.0.1: +event-target-shim@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== @@ -8082,11 +8459,6 @@ eventsource@^2.0.2: resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-2.0.2.tgz#76dfcc02930fb2ff339520b6d290da573a9e8508" integrity sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA== -exec-async@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/exec-async/-/exec-async-2.2.0.tgz#c7c5ad2eef3478d38390c6dd3acfe8af0efc8301" - integrity sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw== - execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -8143,317 +8515,330 @@ expect@^29.0.0, expect@^29.7.0: jest-message-util "^29.7.0" jest-util "^29.7.0" -expo-application@~6.1.5: - version "6.1.5" - resolved "https://registry.yarnpkg.com/expo-application/-/expo-application-6.1.5.tgz#78e569ed8ab237c9bae67d693fec629dd447e53d" - integrity sha512-ToImFmzw8luY043pWFJhh2ZMm4IwxXoHXxNoGdlhD4Ym6+CCmkAvCglg0FK8dMLzAb+/XabmOE7Rbm8KZb6NZg== +expo-application@~7.0.8: + version "7.0.8" + resolved "https://registry.yarnpkg.com/expo-application/-/expo-application-7.0.8.tgz#320af0d6c39b331456d3bc833b25763c702d23db" + integrity sha512-qFGyxk7VJbrNOQWBbE09XUuGuvkOgFS9QfToaK2FdagM2aQ+x3CvGV2DuVgl/l4ZxPgIf3b/MNh9xHpwSwn74Q== -expo-asset@~11.1.7: - version "11.1.7" - resolved "https://registry.yarnpkg.com/expo-asset/-/expo-asset-11.1.7.tgz#dfc61100312cc0dd394d0e0b33613bb0cc898746" - integrity sha512-b5P8GpjUh08fRCf6m5XPVAh7ra42cQrHBIMgH2UXP+xsj4Wufl6pLy6jRF5w6U7DranUMbsXm8TOyq4EHy7ADg== +expo-asset@~12.0.12: + version "12.0.12" + resolved "https://registry.yarnpkg.com/expo-asset/-/expo-asset-12.0.12.tgz#15eb7d92cd43cc81c37149e5bbcdc3091875a85b" + integrity sha512-CsXFCQbx2fElSMn0lyTdRIyKlSXOal6ilLJd+yeZ6xaC7I9AICQgscY5nj0QcwgA+KYYCCEQEBndMsmj7drOWQ== dependencies: - "@expo/image-utils" "^0.7.6" - expo-constants "~17.1.7" + "@expo/image-utils" "^0.8.8" + expo-constants "~18.0.12" -expo-audio@~0.4.9: - version "0.4.9" - resolved "https://registry.yarnpkg.com/expo-audio/-/expo-audio-0.4.9.tgz#f15f64652785ecd416ad351bf42666315e1e0b69" - integrity sha512-J4mMYEt2mqRqqwmSsXFylMGlrNWa+MbCzGl1IZBs+smvPAMJ3Ni8fNplzCQ0I9RnRzygKhRwJNpnAVL+n4MuyA== - -expo-auth-session@~6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/expo-auth-session/-/expo-auth-session-6.2.1.tgz#27c645575ce98508ed8a0faf2c586b04e1a1ba15" - integrity sha512-9KgqrGpW7PoNOhxJ7toofi/Dz5BU2TE4Q+ktJZsmDXLoFcNOcvBokh2+mkhG58Qvd/xJ9Z5sAt/5QoOFaPb9wA== - dependencies: - expo-application "~6.1.5" - expo-constants "~17.1.7" - expo-crypto "~14.1.5" - expo-linking "~7.1.7" - expo-web-browser "~14.2.0" +expo-audio@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/expo-audio/-/expo-audio-1.1.1.tgz#7b9763118e321c5dfbf2771cd4a5b6790ce4fc8d" + integrity sha512-CPCpJ+0AEHdzWROc0f00Zh6e+irLSl2ALos/LPvxEeIcJw1APfBa4DuHPkL4CQCWsVe7EnUjFpdwpqsEUWcP0g== + +expo-auth-session@~7.0.10: + version "7.0.10" + resolved "https://registry.yarnpkg.com/expo-auth-session/-/expo-auth-session-7.0.10.tgz#37250576baf5d56f16b861fb7c387a990f8eaf45" + integrity sha512-XDnKkudvhHSKkZfJ+KkodM+anQcrxB71i+h0kKabdLa5YDXTQ81aC38KRc3TMqmnBDHAu0NpfbzEVd9WDFY3Qg== + dependencies: + expo-application "~7.0.8" + expo-constants "~18.0.11" + expo-crypto "~15.0.8" + expo-linking "~8.0.10" + expo-web-browser "~15.0.10" invariant "^2.2.4" -expo-av@~15.1.7: - version "15.1.7" - resolved "https://registry.yarnpkg.com/expo-av/-/expo-av-15.1.7.tgz#a8422646eca9250c842e8a44fccccb1a4b070a05" - integrity sha512-NC+JR+65sxXfQN1mOHp3QBaXTL2J+BzNwVO27XgUEc5s9NaoBTdHWElYXrfxvik6xwytZ+a7abrqfNNgsbQzsA== +expo-av@~16.0.8: + version "16.0.8" + resolved "https://registry.yarnpkg.com/expo-av/-/expo-av-16.0.8.tgz#b1671127f3b2ecaeb9c69fc2301cf791d4504dd6" + integrity sha512-cmVPftGR/ca7XBgs7R6ky36lF3OC0/MM/lpgX/yXqfv0jASTsh7AYX9JxHCwFmF+Z6JEB1vne9FDx4GiLcGreQ== -expo-build-properties@~0.14.8: - version "0.14.8" - resolved "https://registry.yarnpkg.com/expo-build-properties/-/expo-build-properties-0.14.8.tgz#03aac5eb297c1f4ad3c5b95d8bf897340104d041" - integrity sha512-GTFNZc5HaCS9RmCi6HspCe2+isleuOWt2jh7UEKHTDQ9tdvzkIoWc7U6bQO9lH3Mefk4/BcCUZD/utl7b1wdqw== +expo-build-properties@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/expo-build-properties/-/expo-build-properties-1.0.10.tgz#2c3fb4248f78828e952defa636635a653e3ad546" + integrity sha512-mFCZbrbrv0AP5RB151tAoRzwRJelqM7bCJzCkxpu+owOyH+p/rFC/q7H5q8B9EpVWj8etaIuszR+gKwohpmu1Q== dependencies: ajv "^8.11.0" semver "^7.6.0" -expo-constants@~17.1.7: - version "17.1.7" - resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-17.1.7.tgz#35194c1cef51f1ea756333418f1e077be79a012b" - integrity sha512-byBjGsJ6T6FrLlhOBxw4EaiMXrZEn/MlUYIj/JAd+FS7ll5X/S4qVRbIimSJtdW47hXMq0zxPfJX6njtA56hHA== +expo-constants@~18.0.11, expo-constants@~18.0.12, expo-constants@~18.0.13: + version "18.0.13" + resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-18.0.13.tgz#0117f1f3d43be7b645192c0f4f431fb4efc4803d" + integrity sha512-FnZn12E1dRYKDHlAdIyNFhBurKTS3F9CrfrBDJI5m3D7U17KBHMQ6JEfYlSj7LG7t+Ulr+IKaj58L1k5gBwTcQ== dependencies: - "@expo/config" "~11.0.12" - "@expo/env" "~1.0.7" + "@expo/config" "~12.0.13" + "@expo/env" "~2.0.8" -expo-crypto@~14.1.5: - version "14.1.5" - resolved "https://registry.yarnpkg.com/expo-crypto/-/expo-crypto-14.1.5.tgz#1c29ddd4657d96af6358a9ecdc85a0c344c9ae0c" - integrity sha512-ZXJoUMoUeiMNEoSD4itItFFz3cKrit6YJ/BR0hjuwNC+NczbV9rorvhvmeJmrU9O2cFQHhJQQR1fjQnt45Vu4Q== +expo-crypto@~15.0.8: + version "15.0.8" + resolved "https://registry.yarnpkg.com/expo-crypto/-/expo-crypto-15.0.8.tgz#339198aae149b3ccc0b44f7150d7261a3a1f5287" + integrity sha512-aF7A914TB66WIlTJvl5J6/itejfY78O7dq3ibvFltL9vnTALJ/7LYHvLT4fwmx9yUNS6ekLBtDGWivFWnj2Fcw== dependencies: base64-js "^1.3.0" -expo-dev-client@~5.2.4: - version "5.2.4" - resolved "https://registry.yarnpkg.com/expo-dev-client/-/expo-dev-client-5.2.4.tgz#cdffaea81841b2903cb9585bdd1566dea275a097" - integrity sha512-s/N/nK5LPo0QZJpV4aPijxyrzV4O49S3dN8D2fljqrX2WwFZzWwFO6dX1elPbTmddxumdcpczsdUPY+Ms8g43g== - dependencies: - expo-dev-launcher "5.1.16" - expo-dev-menu "6.1.14" - expo-dev-menu-interface "1.10.0" - expo-manifests "~0.16.6" - expo-updates-interface "~1.1.0" - -expo-dev-launcher@5.1.16: - version "5.1.16" - resolved "https://registry.yarnpkg.com/expo-dev-launcher/-/expo-dev-launcher-5.1.16.tgz#7f2b4f73421523f1deb5c7c832e56bbf8178335f" - integrity sha512-tbCske9pvbozaEblyxoyo/97D6od9Ma4yAuyUnXtRET1CKAPKYS+c4fiZ+I3B4qtpZwN3JNFUjG3oateN0y6Hg== - dependencies: - ajv "8.11.0" - expo-dev-menu "6.1.14" - expo-manifests "~0.16.6" - resolve-from "^5.0.0" +expo-dev-client@~6.0.20: + version "6.0.20" + resolved "https://registry.yarnpkg.com/expo-dev-client/-/expo-dev-client-6.0.20.tgz#d5b65974785100ae7f2538e16701fa1ef55f5fad" + integrity sha512-5XjoVlj1OxakNxy55j/AUaGPrDOlQlB6XdHLLWAw61w5ffSpUDHDnuZzKzs9xY1eIaogOqTOQaAzZ2ddBkdXLA== + dependencies: + expo-dev-launcher "6.0.20" + expo-dev-menu "7.0.18" + expo-dev-menu-interface "2.0.0" + expo-manifests "~1.0.10" + expo-updates-interface "~2.0.0" + +expo-dev-launcher@6.0.20: + version "6.0.20" + resolved "https://registry.yarnpkg.com/expo-dev-launcher/-/expo-dev-launcher-6.0.20.tgz#b2ce90ff6af4c4de28bc1ea595b0b504ed9b467d" + integrity sha512-a04zHEeT9sB0L5EB38fz7sNnUKJ2Ar1pXpcyl60Ki8bXPNCs9rjY7NuYrDkP/irM8+1DklMBqHpyHiLyJ/R+EA== + dependencies: + ajv "^8.11.0" + expo-dev-menu "7.0.18" + expo-manifests "~1.0.10" -expo-dev-menu-interface@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/expo-dev-menu-interface/-/expo-dev-menu-interface-1.10.0.tgz#04671bda3c163d1d7b9438ce7095c3913a3f53f9" - integrity sha512-NxtM/qot5Rh2cY333iOE87dDg1S8CibW+Wu4WdLua3UMjy81pXYzAGCZGNOeY7k9GpNFqDPNDXWyBSlk9r2pBg== +expo-dev-menu-interface@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/expo-dev-menu-interface/-/expo-dev-menu-interface-2.0.0.tgz#c0d6db65eb4abc44a2701bc2303744619ad05ca6" + integrity sha512-BvAMPt6x+vyXpThsyjjOYyjwfjREV4OOpQkZ0tNl+nGpsPfcY9mc6DRACoWnH9KpLzyIt3BOgh3cuy/h/OxQjw== -expo-dev-menu@6.1.14: - version "6.1.14" - resolved "https://registry.yarnpkg.com/expo-dev-menu/-/expo-dev-menu-6.1.14.tgz#c2ee13d0af2c335d47ca5057b080dcd594a40291" - integrity sha512-yonNMg2GHJZtuisVowdl1iQjZfYP85r1D1IO+ar9D9zlrBPBJhq2XEju52jd1rDmDkmDuEhBSbPNhzIcsBNiPg== +expo-dev-menu@7.0.18: + version "7.0.18" + resolved "https://registry.yarnpkg.com/expo-dev-menu/-/expo-dev-menu-7.0.18.tgz#4f3e2dc20b82fc495afb602301b83fa16430f6b8" + integrity sha512-4kTdlHrnZCAWCT6tZRQHSSjZ7vECFisL4T+nsG/GJDo/jcHNaOVGV5qPV9wzlTxyMk3YOPggRw4+g7Ownrg5eA== dependencies: - expo-dev-menu-interface "1.10.0" + expo-dev-menu-interface "2.0.0" -expo-device@~7.1.4: - version "7.1.4" - resolved "https://registry.yarnpkg.com/expo-device/-/expo-device-7.1.4.tgz#84ae7c2520cc45f15a9cb0433ae1226c33f7a8ef" - integrity sha512-HS04IiE1Fy0FRjBLurr9e5A6yj3kbmQB+2jCZvbSGpsjBnCLdSk/LCii4f5VFhPIBWJLyYuN5QqJyEAw6BcS4Q== +expo-device@~8.0.10: + version "8.0.10" + resolved "https://registry.yarnpkg.com/expo-device/-/expo-device-8.0.10.tgz#88be854d6de5568392ed814b44dad0e19d1d50f8" + integrity sha512-jd5BxjaF7382JkDMaC+P04aXXknB2UhWaVx5WiQKA05ugm/8GH5uaz9P9ckWdMKZGQVVEOC8MHaUADoT26KmFA== dependencies: ua-parser-js "^0.7.33" -expo-document-picker@~13.1.6: - version "13.1.6" - resolved "https://registry.yarnpkg.com/expo-document-picker/-/expo-document-picker-13.1.6.tgz#d31a14eac7f0d44f6d1eee8d41403499ce07924d" - integrity sha512-8FTQPDOkyCvFN/i4xyqzH7ELW4AsB6B3XBZQjn1FEdqpozo6rpNJRr7sWFU/93WrLgA9FJEKpKbyr6XxczK6BA== +expo-document-picker@~14.0.8: + version "14.0.8" + resolved "https://registry.yarnpkg.com/expo-document-picker/-/expo-document-picker-14.0.8.tgz#ca1d99cc432c48e69a6390eb035f3301557e3699" + integrity sha512-3tyQKpPqWWFlI8p9RiMX1+T1Zge5mEKeBuXWp1h8PEItFMUDSiOJbQ112sfdC6Hxt8wSxreV9bCRl/NgBdt+fA== -expo-file-system@~18.1.11: - version "18.1.11" - resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-18.1.11.tgz#a563c715c4bb5c18729d6d104e8c6cdfbd383e69" - integrity sha512-HJw/m0nVOKeqeRjPjGdvm+zBi5/NxcdPf8M8P3G2JFvH5Z8vBWqVDic2O58jnT1OFEy0XXzoH9UqFu7cHg9DTQ== +expo-file-system@~19.0.21: + version "19.0.21" + resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-19.0.21.tgz#e96a68107fb629cf0dd1906fe7b46b566ff13e10" + integrity sha512-s3DlrDdiscBHtab/6W1osrjGL+C2bvoInPJD7sOwmxfJ5Woynv2oc+Fz1/xVXaE/V7HE/+xrHC/H45tu6lZzzg== -expo-font@~13.3.2: - version "13.3.2" - resolved "https://registry.yarnpkg.com/expo-font/-/expo-font-13.3.2.tgz#1b5a1a14d6827a48a3f003577d5f7dc6b344a1d3" - integrity sha512-wUlMdpqURmQ/CNKK/+BIHkDA5nGjMqNlYmW0pJFXY/KE/OG80Qcavdu2sHsL4efAIiNGvYdBS10WztuQYU4X0A== +expo-font@~14.0.11: + version "14.0.11" + resolved "https://registry.yarnpkg.com/expo-font/-/expo-font-14.0.11.tgz#198743d17332520545107df026d8a261e6b2732f" + integrity sha512-ga0q61ny4s/kr4k8JX9hVH69exVSIfcIc19+qZ7gt71Mqtm7xy2c6kwsPTCyhBW2Ro5yXTT8EaZOpuRi35rHbg== dependencies: fontfaceobserver "^2.1.0" -expo-image-loader@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/expo-image-loader/-/expo-image-loader-5.1.0.tgz#f7d65f9b9a9714eaaf5d50a406cb34cb25262153" - integrity sha512-sEBx3zDQIODWbB5JwzE7ZL5FJD+DK3LVLWBVJy6VzsqIA6nDEnSFnsnWyCfCTSvbGigMATs1lgkC2nz3Jpve1Q== +expo-image-loader@~6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/expo-image-loader/-/expo-image-loader-6.0.0.tgz#15230442cbb90e101c080a4c81e37d974e43e072" + integrity sha512-nKs/xnOGw6ACb4g26xceBD57FKLFkSwEUTDXEDF3Gtcu3MqF3ZIYd3YM+sSb1/z9AKV1dYT7rMSGVNgsveXLIQ== -expo-image-manipulator@~13.1.7: - version "13.1.7" - resolved "https://registry.yarnpkg.com/expo-image-manipulator/-/expo-image-manipulator-13.1.7.tgz#e891ce9b49d75962eafdf5b7d670116583379e76" - integrity sha512-DBy/Xdd0E/yFind14x36XmwfWuUxOHI/oH97/giKjjPaRc2dlyjQ3tuW3x699hX6gAs9Sixj5WEJ1qNf3c8sag== +expo-image-manipulator@~14.0.8: + version "14.0.8" + resolved "https://registry.yarnpkg.com/expo-image-manipulator/-/expo-image-manipulator-14.0.8.tgz#1c457acbd2bcabe987fbd650c0f29120c3366ba6" + integrity sha512-sXsXjm7rIxLWZe0j2A41J/Ph53PpFJRdyzJ3EQ/qetxLUvS2m3K1sP5xy37px43qCf0l79N/i6XgFgenFV36/Q== dependencies: - expo-image-loader "~5.1.0" + expo-image-loader "~6.0.0" -expo-image-picker@~16.1.4: - version "16.1.4" - resolved "https://registry.yarnpkg.com/expo-image-picker/-/expo-image-picker-16.1.4.tgz#d4ac2d1f64f6ec9347c3f64f8435b40e6e4dcc40" - integrity sha512-bTmmxtw1AohUT+HxEBn2vYwdeOrj1CLpMXKjvi9FKSoSbpcarT4xxI0z7YyGwDGHbrJqyyic3I9TTdP2J2b4YA== +expo-image-picker@~17.0.10: + version "17.0.10" + resolved "https://registry.yarnpkg.com/expo-image-picker/-/expo-image-picker-17.0.10.tgz#b4a714971378b2813e53d97d8ca81ab2c32cdf30" + integrity sha512-a2xrowp2trmvXyUWgX3O6Q2rZaa2C59AqivKI7+bm+wLvMfTEbZgldLX4rEJJhM8xtmEDTNU+lzjtObwzBRGaw== dependencies: - expo-image-loader "~5.1.0" + expo-image-loader "~6.0.0" -expo-image@~2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/expo-image/-/expo-image-2.4.0.tgz#02f7fd743387206914cd431a6367f5be53509e3e" - integrity sha512-TQ/LvrtJ9JBr+Tf198CAqflxcvdhuj7P24n0LQ1jHaWIVA7Z+zYKbYHnSMPSDMul/y0U46Z5bFLbiZiSidgcNw== +expo-image@~3.0.11: + version "3.0.11" + resolved "https://registry.yarnpkg.com/expo-image/-/expo-image-3.0.11.tgz#54195565dc710e632c10414c3609deebb7149ac5" + integrity sha512-4TudfUCLgYgENv+f48omnU8tjS2S0Pd9EaON5/s1ZUBRwZ7K8acEr4NfvLPSaeXvxW24iLAiyQ7sV7BXQH3RoA== expo-json-utils@~0.15.0: version "0.15.0" resolved "https://registry.yarnpkg.com/expo-json-utils/-/expo-json-utils-0.15.0.tgz#6723574814b9e6b0a90e4e23662be76123ab6ae9" integrity sha512-duRT6oGl80IDzH2LD2yEFWNwGIC2WkozsB6HF3cDYNoNNdUvFk6uN3YiwsTsqVM/D0z6LEAQ01/SlYvN+Fw0JQ== -expo-keep-awake@~14.1.4: - version "14.1.4" - resolved "https://registry.yarnpkg.com/expo-keep-awake/-/expo-keep-awake-14.1.4.tgz#80197728563e0e17523e5a606fbd6fbed9639503" - integrity sha512-wU9qOnosy4+U4z/o4h8W9PjPvcFMfZXrlUoKTMBW7F4pLqhkkP/5G4EviPZixv4XWFMjn1ExQ5rV6BX8GwJsWA== +expo-keep-awake@~15.0.8: + version "15.0.8" + resolved "https://registry.yarnpkg.com/expo-keep-awake/-/expo-keep-awake-15.0.8.tgz#911c5effeba9baff2ccde79ef0ff5bf856215f8d" + integrity sha512-YK9M1VrnoH1vLJiQzChZgzDvVimVoriibiDIFLbQMpjYBnvyfUeHJcin/Gx1a+XgupNXy92EQJLgI/9ZuXajYQ== -expo-linking@~7.1.7: - version "7.1.7" - resolved "https://registry.yarnpkg.com/expo-linking/-/expo-linking-7.1.7.tgz#8e41ef1ca5d1190dfc01b7f4dbc4c3993bdc4523" - integrity sha512-ZJaH1RIch2G/M3hx2QJdlrKbYFUTOjVVW4g39hfxrE5bPX9xhZUYXqxqQtzMNl1ylAevw9JkgEfWbBWddbZ3UA== +expo-linking@~8.0.10, expo-linking@~8.0.11: + version "8.0.11" + resolved "https://registry.yarnpkg.com/expo-linking/-/expo-linking-8.0.11.tgz#b13ca9fc409ef0536352443221eb50e5e2bee366" + integrity sha512-+VSaNL5om3kOp/SSKO5qe6cFgfSIWnnQDSbA7XLs3ECkYzXRquk5unxNS3pg7eK5kNUmQ4kgLI7MhTggAEUBLA== dependencies: - expo-constants "~17.1.7" + expo-constants "~18.0.12" invariant "^2.2.4" -expo-localization@~16.1.6: - version "16.1.6" - resolved "https://registry.yarnpkg.com/expo-localization/-/expo-localization-16.1.6.tgz#0ad7bd6bb61f14d9d786e63fd0f7d190d44695cc" - integrity sha512-v4HwNzs8QvyKHwl40MvETNEKr77v1o9/eVC8WCBY++DIlBAvonHyJe2R9CfqpZbC4Tlpl7XV+07nLXc8O5PQsA== +expo-localization@~17.0.8: + version "17.0.8" + resolved "https://registry.yarnpkg.com/expo-localization/-/expo-localization-17.0.8.tgz#eb74ae0f9b5b49596322d68d2005662346211100" + integrity sha512-UrdwklZBDJ+t+ZszMMiE0SXZ2eJxcquCuQcl6EvGHM9K+e6YqKVRQ+w8qE+iIB3H75v2RJy6MHAaLK+Mqeo04g== dependencies: rtl-detect "^1.0.2" -expo-location@~18.1.6: - version "18.1.6" - resolved "https://registry.yarnpkg.com/expo-location/-/expo-location-18.1.6.tgz#b855e14e8b4e29a1bde470fc4dc2a341abecf631" - integrity sha512-l5dQQ2FYkrBgNzaZN1BvSmdhhcztFOUucu2kEfDBMV4wSIuTIt/CKsho+F3RnAiWgvui1wb1WTTf80E8zq48hA== +expo-location@~19.0.8: + version "19.0.8" + resolved "https://registry.yarnpkg.com/expo-location/-/expo-location-19.0.8.tgz#1805393151b1286021c1ad36246b6fd095d09b55" + integrity sha512-H/FI75VuJ1coodJbbMu82pf+Zjess8X8Xkiv9Bv58ZgPKS/2ztjC1YO1/XMcGz7+s9DrbLuMIw22dFuP4HqneA== -expo-manifests@~0.16.6: - version "0.16.6" - resolved "https://registry.yarnpkg.com/expo-manifests/-/expo-manifests-0.16.6.tgz#a0e5b3225ee032999eac8408337494f464603209" - integrity sha512-1A+do6/mLUWF9xd3uCrlXr9QFDbjbfqAYmUy8UDLOjof1lMrOhyeC4Yi6WexA/A8dhZEpIxSMCKfn7G4aHAh4w== +expo-manifests@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/expo-manifests/-/expo-manifests-1.0.10.tgz#5dfb3db1cdf6b46fee349f1d68a25edf5e087994" + integrity sha512-oxDUnURPcL4ZsOBY6X1DGWGuoZgVAFzp6PISWV7lPP2J0r8u1/ucuChBgpK7u1eLGFp6sDIPwXyEUCkI386XSQ== dependencies: - "@expo/config" "~11.0.12" + "@expo/config" "~12.0.11" expo-json-utils "~0.15.0" -expo-modules-autolinking@2.1.14: - version "2.1.14" - resolved "https://registry.yarnpkg.com/expo-modules-autolinking/-/expo-modules-autolinking-2.1.14.tgz#4b4afddefba1a8b6fec0bf795cf6fe52c81343d1" - integrity sha512-nT5ERXwc+0ZT/pozDoJjYZyUQu5RnXMk9jDGm5lg+PiKvsrCTSA/2/eftJGMxLkTjVI2MXp5WjSz3JRjbA7UXA== +expo-modules-autolinking@3.0.24: + version "3.0.24" + resolved "https://registry.yarnpkg.com/expo-modules-autolinking/-/expo-modules-autolinking-3.0.24.tgz#55fdfe1ef5a053d5cc287582170a5f6d69ab0e30" + integrity sha512-TP+6HTwhL7orDvsz2VzauyQlXJcAWyU3ANsZ7JGL4DQu8XaZv/A41ZchbtAYLfozNA2Ya1Hzmhx65hXryBMjaQ== dependencies: "@expo/spawn-async" "^1.7.2" chalk "^4.1.0" commander "^7.2.0" - find-up "^5.0.0" - glob "^10.4.2" require-from-string "^2.0.2" resolve-from "^5.0.0" -expo-modules-core@2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/expo-modules-core/-/expo-modules-core-2.5.0.tgz#cc098607c9b0665e0cbd0a423c9542253caab58a" - integrity sha512-aIbQxZE2vdCKsolQUl6Q9Farlf8tjh/ROR4hfN1qT7QBGPl1XrJGnaOKkcgYaGrlzCPg/7IBe0Np67GzKMZKKQ== +expo-modules-core@3.0.29: + version "3.0.29" + resolved "https://registry.yarnpkg.com/expo-modules-core/-/expo-modules-core-3.0.29.tgz#99287eba52f21784bcb2e4f4edd4fc4c21b5b265" + integrity sha512-LzipcjGqk8gvkrOUf7O2mejNWugPkf3lmd9GkqL9WuNyeN2fRwU0Dn77e3ZUKI3k6sI+DNwjkq4Nu9fNN9WS7Q== dependencies: invariant "^2.2.4" -expo-navigation-bar@~4.2.8: - version "4.2.8" - resolved "https://registry.yarnpkg.com/expo-navigation-bar/-/expo-navigation-bar-4.2.8.tgz#6450500c2939454d53b637dd6d1fc6a850b6dfdd" - integrity sha512-Ykdz5/22el8Bf8c8llYBfpJvT8kjWJxvgMwNbyF0oZ/7ngoJu1HLijvblA1ppNW7WFLICL6iz6GczcizBVVo5g== +expo-navigation-bar@~5.0.10: + version "5.0.10" + resolved "https://registry.yarnpkg.com/expo-navigation-bar/-/expo-navigation-bar-5.0.10.tgz#64e4fdb91ff3872110373b56c8e65d196b40979c" + integrity sha512-r9rdLw8mY6GPMQmVVOY/r1NBBw74DZefXHF60HxhRsdNI2kjc1wLdfWfR2rk4JVdOvdMDujnGrc9HQmqM3n8Jg== dependencies: - "@react-native/normalize-colors" "0.79.6" + "@react-native/normalize-colors" "0.81.5" debug "^4.3.2" - react-native-edge-to-edge "1.6.0" - react-native-is-edge-to-edge "^1.1.6" + react-native-is-edge-to-edge "^1.2.1" -expo-notifications@~0.31.4: - version "0.31.4" - resolved "https://registry.yarnpkg.com/expo-notifications/-/expo-notifications-0.31.4.tgz#295a4345f7fea6ff974df9005790dda1d17ce00e" - integrity sha512-NnGKIFGpgZU66qfiFUyjEBYsS77VahURpSSeWEOLt+P1zOaUFlgx2XqS+dxH3/Bn1Vm7TMj04qKsK5KvzR/8Lw== +expo-notifications@~0.32.16: + version "0.32.16" + resolved "https://registry.yarnpkg.com/expo-notifications/-/expo-notifications-0.32.16.tgz#1a1304b89efedd7cdeba92de39b21f09fb2db2c7" + integrity sha512-QQD/UA6v7LgvwIJ+tS7tSvqJZkdp0nCSj9MxsDk/jU1GttYdK49/5L2LvE/4U0H7sNBz1NZAyhDZozg8xgBLXw== dependencies: - "@expo/image-utils" "^0.7.6" + "@expo/image-utils" "^0.8.8" "@ide/backoff" "^1.0.0" abort-controller "^3.0.0" assert "^2.0.0" badgin "^1.1.5" - expo-application "~6.1.5" - expo-constants "~17.1.7" + expo-application "~7.0.8" + expo-constants "~18.0.13" -expo-router@~5.1.7: - version "5.1.7" - resolved "https://registry.yarnpkg.com/expo-router/-/expo-router-5.1.7.tgz#ce8d812df91dcbf9d15bb7e8a4bbec63c7ca60b5" - integrity sha512-E7hIqTZs4Cub4sbYPeednfYPi+2cyRGMdqc5IYBJ/vC+WBKoYJ8C9eU13ZLbPz//ZybSo2Dsm7v89uFIlO2Gow== +expo-router@~6.0.23: + version "6.0.23" + resolved "https://registry.yarnpkg.com/expo-router/-/expo-router-6.0.23.tgz#480fbcb4901fd692f9d11762f33894280dcbd75a" + integrity sha512-qCxVAiCrCyu0npky6azEZ6dJDMt77OmCzEbpF6RbUTlfkaCA417LvY14SBkk0xyGruSxy/7pvJOI6tuThaUVCA== dependencies: - "@expo/metro-runtime" "5.0.5" - "@expo/schema-utils" "^0.1.0" - "@expo/server" "^0.6.3" + "@expo/metro-runtime" "^6.1.2" + "@expo/schema-utils" "^0.1.8" "@radix-ui/react-slot" "1.2.0" - "@react-navigation/bottom-tabs" "^7.3.10" - "@react-navigation/native" "^7.1.6" - "@react-navigation/native-stack" "^7.3.10" + "@radix-ui/react-tabs" "^1.1.12" + "@react-navigation/bottom-tabs" "^7.4.0" + "@react-navigation/native" "^7.1.8" + "@react-navigation/native-stack" "^7.3.16" client-only "^0.0.1" + debug "^4.3.4" + escape-string-regexp "^4.0.0" + expo-server "^1.0.5" + fast-deep-equal "^3.1.3" invariant "^2.2.4" + nanoid "^3.3.8" + query-string "^7.1.3" react-fast-compare "^3.2.2" react-native-is-edge-to-edge "^1.1.6" semver "~7.6.3" server-only "^0.0.1" + sf-symbols-typescript "^2.1.0" shallowequal "^1.1.0" + use-latest-callback "^0.2.1" + vaul "^1.1.2" -expo-screen-orientation@~8.1.7: - version "8.1.7" - resolved "https://registry.yarnpkg.com/expo-screen-orientation/-/expo-screen-orientation-8.1.7.tgz#3751b441f2bfcbde798b1508c0ff9f099f4be911" - integrity sha512-nYwadYtdU6mMDk0MCHMPPPQtBoeFYJ2FspLRW+J35CMLqzE4nbpwGeiImfXzkvD94fpOCfI4KgLj5vGauC3pfA== +expo-screen-orientation@~9.0.8: + version "9.0.8" + resolved "https://registry.yarnpkg.com/expo-screen-orientation/-/expo-screen-orientation-9.0.8.tgz#15b8f85bd4d183831943fc5a21e3020e17432867" + integrity sha512-qRoPi3E893o3vQHT4h1NKo51+7g2hjRSbDeg1fsSo/u2pOW5s6FCeoacLvD+xofOP33cH2MkE4ua54aWWO7Icw== -expo-sharing@~13.1.5: - version "13.1.5" - resolved "https://registry.yarnpkg.com/expo-sharing/-/expo-sharing-13.1.5.tgz#73d86cdcc037b46ddc82be224dfd3d6bceec497c" - integrity sha512-X/5sAEiWXL2kdoGE3NO5KmbfcmaCWuWVZXHu8OQef7Yig4ZgHFkGD11HKJ5KqDrDg+SRZe4ISd6MxE7vGUgm4w== +expo-server@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/expo-server/-/expo-server-1.0.5.tgz#2d52002199a2af99c2c8771d0657916004345ca9" + integrity sha512-IGR++flYH70rhLyeXF0Phle56/k4cee87WeQ4mamS+MkVAVP+dDlOHf2nN06Z9Y2KhU0Gp1k+y61KkghF7HdhA== -expo-splash-screen@~0.30.10: - version "0.30.10" - resolved "https://registry.yarnpkg.com/expo-splash-screen/-/expo-splash-screen-0.30.10.tgz#d249d350f867b6513fa34a9c4cf6545260ab5208" - integrity sha512-Tt9va/sLENQDQYeOQ6cdLdGvTZ644KR3YG9aRlnpcs2/beYjOX1LHT510EGzVN9ljUTg+1ebEo5GGt2arYtPjw== +expo-sharing@~14.0.8: + version "14.0.8" + resolved "https://registry.yarnpkg.com/expo-sharing/-/expo-sharing-14.0.8.tgz#cfd5fcf77ab5f64cf3d192a40a925abb316d3545" + integrity sha512-A1pPr2iBrxypFDCWVAESk532HK+db7MFXbvO2sCV9ienaFXAk7lIBm6bkqgE6vzRd9O3RGdEGzYx80cYlc089Q== + +expo-splash-screen@~31.0.13: + version "31.0.13" + resolved "https://registry.yarnpkg.com/expo-splash-screen/-/expo-splash-screen-31.0.13.tgz#f41f1a4c8bb1ae7fcc52b760e7dd485d7ddec642" + integrity sha512-1epJLC1cDlwwj089R2h8cxaU5uk4ONVAC+vzGiTZH4YARQhL4Stlz1MbR6yAS173GMosvkE6CAeihR7oIbCkDA== dependencies: - "@expo/prebuild-config" "^9.0.10" + "@expo/prebuild-config" "^54.0.8" -expo-status-bar@~2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/expo-status-bar/-/expo-status-bar-2.2.3.tgz#09385a866732328e0af3b4588c4f349a15fd7cd0" - integrity sha512-+c8R3AESBoduunxTJ8353SqKAKpxL6DvcD8VKBuh81zzJyUUbfB4CVjr1GufSJEKsMzNPXZU+HJwXx7Xh7lx8Q== +expo-status-bar@~3.0.9: + version "3.0.9" + resolved "https://registry.yarnpkg.com/expo-status-bar/-/expo-status-bar-3.0.9.tgz#87cfc803fa614f09a985b8e75e3dd7abd51ce2cb" + integrity sha512-xyYyVg6V1/SSOZWh4Ni3U129XHCnFHBTcUo0dhWtFDrZbNp/duw5AGsQfb2sVeU0gxWHXSY1+5F0jnKYC7WuOw== dependencies: - react-native-edge-to-edge "1.6.0" - react-native-is-edge-to-edge "^1.1.6" + react-native-is-edge-to-edge "^1.2.1" -expo-system-ui@~5.0.11: - version "5.0.11" - resolved "https://registry.yarnpkg.com/expo-system-ui/-/expo-system-ui-5.0.11.tgz#2bb70b2dd9f3f5137df85e43aa5d2d557432ad49" - integrity sha512-PG5VdaG5cwBe1Rj02mJdnsihKl9Iw/w/a6+qh2mH3f2K/IvQ+Hf7aG2kavSADtkGNCNj7CEIg7Rn4DQz/SE5rQ== +expo-system-ui@~6.0.9: + version "6.0.9" + resolved "https://registry.yarnpkg.com/expo-system-ui/-/expo-system-ui-6.0.9.tgz#09b4a4301ab25ec594ae39beb7d24197c231a30c" + integrity sha512-eQTYGzw1V4RYiYHL9xDLYID3Wsec2aZS+ypEssmF64D38aDrqbDgz1a2MSlHLQp2jHXSs3FvojhZ9FVela1Zcg== dependencies: - "@react-native/normalize-colors" "0.79.6" + "@react-native/normalize-colors" "0.81.5" debug "^4.3.2" -expo-task-manager@~13.1.6: - version "13.1.6" - resolved "https://registry.yarnpkg.com/expo-task-manager/-/expo-task-manager-13.1.6.tgz#73c99fb9e3bc6159d6b0a1173d92c9df0bbc61e0" - integrity sha512-sYNAftpIeZ+j6ur17Jo0OpSTk9ks/MDvTbrNCimXMyjIt69XXYL/kAPYf76bWuxOuN8bcJ8Ef8YvihkwFG9hDA== +expo-task-manager@~14.0.9: + version "14.0.9" + resolved "https://registry.yarnpkg.com/expo-task-manager/-/expo-task-manager-14.0.9.tgz#7e410711cf3fd0c465a191916d699c6560c93192" + integrity sha512-GKWtXrkedr4XChHfTm5IyTcSfMtCPxzx89y4CMVqKfyfROATibrE/8UI5j7UC/pUOfFoYlQvulQEvECMreYuUA== dependencies: - unimodules-app-loader "~5.1.3" + unimodules-app-loader "~6.0.8" -expo-updates-interface@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/expo-updates-interface/-/expo-updates-interface-1.1.0.tgz#62497d4647b381da9fdb68868ed180203ae737ef" - integrity sha512-DeB+fRe0hUDPZhpJ4X4bFMAItatFBUPjw/TVSbJsaf3Exeami+2qbbJhWkcTMoYHOB73nOIcaYcWXYJnCJXO0w== +expo-updates-interface@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/expo-updates-interface/-/expo-updates-interface-2.0.0.tgz#7721cb64c37bcb46b23827b2717ef451a9378749" + integrity sha512-pTzAIufEZdVPKql6iMi5ylVSPqV1qbEopz9G6TSECQmnNde2nwq42PxdFBaUEd8IZJ/fdJLQnOT3m6+XJ5s7jg== -expo-web-browser@~14.2.0: - version "14.2.0" - resolved "https://registry.yarnpkg.com/expo-web-browser/-/expo-web-browser-14.2.0.tgz#d8fb521ae349aebbf5c0ca32448877480124c06c" - integrity sha512-6S51d8pVlDRDsgGAp8BPpwnxtyKiMWEFdezNz+5jVIyT+ctReW42uxnjRgtsdn5sXaqzhaX+Tzk/CWaKCyC0hw== +expo-web-browser@~15.0.10: + version "15.0.10" + resolved "https://registry.yarnpkg.com/expo-web-browser/-/expo-web-browser-15.0.10.tgz#ee7fb59b4f143f262c13c020433a83444181f1a3" + integrity sha512-fvDhW4bhmXAeWFNFiInmsGCK83PAqAcQaFyp/3pE/jbdKmFKoRCWr46uZGIfN4msLK/OODhaQ/+US7GSJNDHJg== -expo@~53.0.23: - version "53.0.23" - resolved "https://registry.yarnpkg.com/expo/-/expo-53.0.23.tgz#b6fd102ac74537d86f99e87bd26a254a1b560b9b" - integrity sha512-6TOLuNCP3AsSkXBJA5W6U/7wpZUop3Q6BxHMtRD2OOgT7CCPvnYgJdnTzqU+gD1hMfcryD8Ejq9RdHbLduXohg== +expo@^54.0.33: + version "54.0.33" + resolved "https://registry.yarnpkg.com/expo/-/expo-54.0.33.tgz#f7d572857323f5a8250a9afe245a487d2ee2735f" + integrity sha512-3yOEfAKqo+gqHcV8vKcnq0uA5zxlohnhA3fu4G43likN8ct5ZZ3LjAh9wDdKteEkoad3tFPvwxmXW711S5OHUw== dependencies: "@babel/runtime" "^7.20.0" - "@expo/cli" "0.24.22" - "@expo/config" "~11.0.13" - "@expo/config-plugins" "~10.1.2" - "@expo/fingerprint" "0.13.4" - "@expo/metro-config" "0.20.17" - "@expo/vector-icons" "^14.0.0" - babel-preset-expo "~13.2.4" - expo-asset "~11.1.7" - expo-constants "~17.1.7" - expo-file-system "~18.1.11" - expo-font "~13.3.2" - expo-keep-awake "~14.1.4" - expo-modules-autolinking "2.1.14" - expo-modules-core "2.5.0" - react-native-edge-to-edge "1.6.0" + "@expo/cli" "54.0.23" + "@expo/config" "~12.0.13" + "@expo/config-plugins" "~54.0.4" + "@expo/devtools" "0.1.8" + "@expo/fingerprint" "0.15.4" + "@expo/metro" "~54.2.0" + "@expo/metro-config" "54.0.14" + "@expo/vector-icons" "^15.0.3" + "@ungap/structured-clone" "^1.3.0" + babel-preset-expo "~54.0.10" + expo-asset "~12.0.12" + expo-constants "~18.0.13" + expo-file-system "~19.0.21" + expo-font "~14.0.11" + expo-keep-awake "~15.0.8" + expo-modules-autolinking "3.0.24" + expo-modules-core "3.0.29" + pretty-format "^29.7.0" + react-refresh "^0.14.2" whatwg-url-without-unicode "8.0.0-3" exponential-backoff@^3.1.1: @@ -8914,6 +9299,11 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.4, get-intrinsic@ hasown "^2.0.2" math-intrinsics "^1.1.0" +get-nonce@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3" + integrity sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q== + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -9018,7 +9408,7 @@ glob@^10.2.2: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" -glob@^10.3.10, glob@^10.4.2: +glob@^10.3.10: version "10.4.5" resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== @@ -9030,6 +9420,15 @@ glob@^10.3.10, glob@^10.4.2: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" +glob@^13.0.0: + version "13.0.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-13.0.6.tgz#078666566a425147ccacfbd2e332deb66a2be71d" + integrity sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw== + dependencies: + minimatch "^10.2.2" + minipass "^7.1.3" + path-scurry "^2.0.2" + glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -9224,30 +9623,42 @@ hermes-estree@0.20.1: resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.20.1.tgz#0b9a544cf883a779a8e1444b915fa365bef7f72d" integrity sha512-SQpZK4BzR48kuOg0v4pb3EAGNclzIlqMj3Opu/mu7bbAoFw6oig6cEt/RAi0zTFW/iW6Iz9X9ggGuZTAZ/yZHg== -hermes-estree@0.25.1: - version "0.25.1" - resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480" - integrity sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw== - hermes-estree@0.29.1: version "0.29.1" resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.29.1.tgz#043c7db076e0e8ef8c5f6ed23828d1ba463ebcc5" integrity sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ== -hermes-parser@0.25.1: - version "0.25.1" - resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.25.1.tgz#5be0e487b2090886c62bd8a11724cd766d5f54d1" - integrity sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA== - dependencies: - hermes-estree "0.25.1" +hermes-estree@0.32.0: + version "0.32.0" + resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.32.0.tgz#bb7da6613ab8e67e334a1854ea1e209f487d307b" + integrity sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ== + +hermes-estree@0.33.3: + version "0.33.3" + resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.33.3.tgz#6d6b593d4b471119772c82bdb0212dfadabb6f17" + integrity sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg== -hermes-parser@0.29.1: +hermes-parser@0.29.1, hermes-parser@^0.29.1: version "0.29.1" resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.29.1.tgz#436b24bcd7bb1e71f92a04c396ccc0716c288d56" integrity sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA== dependencies: hermes-estree "0.29.1" +hermes-parser@0.32.0: + version "0.32.0" + resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.32.0.tgz#7916984ef6fdce62e7415d354cf35392061cd303" + integrity sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw== + dependencies: + hermes-estree "0.32.0" + +hermes-parser@0.33.3: + version "0.33.3" + resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.33.3.tgz#da50ababb7a5ab636d339e7b2f6e3848e217e09d" + integrity sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA== + dependencies: + hermes-estree "0.33.3" + hermes-parser@^0.20.1: version "0.20.1" resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.20.1.tgz#ad10597b99f718b91e283f81cbe636c50c3cff92" @@ -9444,14 +9855,6 @@ image-size@^1.0.2: dependencies: queue "6.0.2" -import-fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" - integrity sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg== - dependencies: - caller-path "^2.0.0" - resolve-from "^3.0.0" - import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.1" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" @@ -9739,11 +10142,6 @@ is-date-object@^1.0.5, is-date-object@^1.1.0: call-bound "^1.0.2" has-tostringtag "^1.0.2" -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" - integrity sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw== - is-docker@^2.0.0, is-docker@^2.1.1: version "2.2.1" resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" @@ -10328,25 +10726,23 @@ jest-environment-node@^29.7.0: jest-mock "^29.7.0" jest-util "^29.7.0" -jest-expo@~53.0.10: - version "53.0.10" - resolved "https://registry.yarnpkg.com/jest-expo/-/jest-expo-53.0.10.tgz#52dc368b3a54f431acbb1aaf298f20196732df4a" - integrity sha512-J6vGCNOImXxUXv0c70J2hMlGSHTIyVwCviezMtnZeg966lzshESJhLxQatuvA8r7nJ2riffQgM3cWvL+/Hdewg== +jest-expo@~54.0.17: + version "54.0.17" + resolved "https://registry.yarnpkg.com/jest-expo/-/jest-expo-54.0.17.tgz#c4b905097889340fe44f868d601c165c113ddc55" + integrity sha512-LyIhrsP4xvHEEcR1R024u/LBj3uPpAgB+UljgV+YXWkEHjprnr0KpE4tROsMNYCVTM1pPlAnPuoBmn5gnAN9KA== dependencies: - "@expo/config" "~11.0.13" - "@expo/json-file" "^9.1.5" + "@expo/config" "~12.0.13" + "@expo/json-file" "^10.0.8" "@jest/create-cache-key-function" "^29.2.1" "@jest/globals" "^29.2.1" babel-jest "^29.2.1" - find-up "^5.0.0" jest-environment-jsdom "^29.2.1" jest-snapshot "^29.2.1" jest-watch-select-projects "^2.0.0" jest-watch-typeahead "2.2.1" json5 "^2.2.3" lodash "^4.17.19" - react-server-dom-webpack "~19.0.0" - react-test-renderer "19.0.0" + react-test-renderer "19.1.0" server-only "^0.0.1" stacktrace-js "^2.0.2" @@ -10791,11 +11187,6 @@ json-buffer@3.0.1: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== -json-parse-better-errors@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - json-parse-even-better-errors@^2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" @@ -10957,106 +11348,111 @@ lightningcss-android-arm64@1.30.2: resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz#6966b7024d39c94994008b548b71ab360eb3a307" integrity sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A== -lightningcss-darwin-arm64@1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.27.0.tgz#565bd610533941cba648a70e105987578d82f996" - integrity sha512-Gl/lqIXY+d+ySmMbgDf0pgaWSqrWYxVHoc88q+Vhf2YNzZ8DwoRzGt5NZDVqqIW5ScpSnmmjcgXP87Dn2ylSSQ== +lightningcss-android-arm64@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz#f033885116dfefd9c6f54787523e3514b61e1968" + integrity sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg== lightningcss-darwin-arm64@1.30.2: version "1.30.2" resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz#a5fa946d27c029e48c7ff929e6e724a7de46eb2c" integrity sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA== -lightningcss-darwin-x64@1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.27.0.tgz#c906a267237b1c7fe08bff6c5ac032c099bc9482" - integrity sha512-0+mZa54IlcNAoQS9E0+niovhyjjQWEMrwW0p2sSdLRhLDc8LMQ/b67z7+B5q4VmjYCMSfnFi3djAAQFIDuj/Tg== +lightningcss-darwin-arm64@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz#50b71871b01c8199584b649e292547faea7af9b5" + integrity sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ== lightningcss-darwin-x64@1.30.2: version "1.30.2" resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz#5ce87e9cd7c4f2dcc1b713f5e8ee185c88d9b7cd" integrity sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ== -lightningcss-freebsd-x64@1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.27.0.tgz#a7c3c4d6ee18dffeb8fa69f14f8f9267f7dc0c34" - integrity sha512-n1sEf85fePoU2aDN2PzYjoI8gbBqnmLGEhKq7q0DKLj0UTVmOTwDC7PtLcy/zFxzASTSBlVQYJUhwIStQMIpRA== +lightningcss-darwin-x64@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz#35f3e97332d130b9ca181e11b568ded6aebc6d5e" + integrity sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w== lightningcss-freebsd-x64@1.30.2: version "1.30.2" resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz#6ae1d5e773c97961df5cff57b851807ef33692a5" integrity sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA== -lightningcss-linux-arm-gnueabihf@1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.27.0.tgz#c7c16432a571ec877bf734fe500e4a43d48c2814" - integrity sha512-MUMRmtdRkOkd5z3h986HOuNBD1c2lq2BSQA1Jg88d9I7bmPGx08bwGcnB75dvr17CwxjxD6XPi3Qh8ArmKFqCA== +lightningcss-freebsd-x64@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz#9777a76472b64ed6ff94342ad64c7bafd794a575" + integrity sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig== lightningcss-linux-arm-gnueabihf@1.30.2: version "1.30.2" resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz#62c489610c0424151a6121fa99d77731536cdaeb" integrity sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA== -lightningcss-linux-arm64-gnu@1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.27.0.tgz#cfd9e18df1cd65131da286ddacfa3aee6862a752" - integrity sha512-cPsxo1QEWq2sfKkSq2Bq5feQDHdUEwgtA9KaB27J5AX22+l4l0ptgjMZZtYtUnteBofjee+0oW1wQ1guv04a7A== +lightningcss-linux-arm-gnueabihf@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz#13ae652e1ab73b9135d7b7da172f666c410ad53d" + integrity sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw== lightningcss-linux-arm64-gnu@1.30.2: version "1.30.2" resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz#2a3661b56fe95a0cafae90be026fe0590d089298" integrity sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A== -lightningcss-linux-arm64-musl@1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.27.0.tgz#6682ff6b9165acef9a6796bd9127a8e1247bb0ed" - integrity sha512-rCGBm2ax7kQ9pBSeITfCW9XSVF69VX+fm5DIpvDZQl4NnQoMQyRwhZQm9pd59m8leZ1IesRqWk2v/DntMo26lg== +lightningcss-linux-arm64-gnu@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz#417858795a94592f680123a1b1f9da8a0e1ef335" + integrity sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ== lightningcss-linux-arm64-musl@1.30.2: version "1.30.2" resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz#d7ddd6b26959245e026bc1ad9eb6aa983aa90e6b" integrity sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA== -lightningcss-linux-x64-gnu@1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.27.0.tgz#714221212ad184ddfe974bbb7dbe9300dfde4bc0" - integrity sha512-Dk/jovSI7qqhJDiUibvaikNKI2x6kWPN79AQiD/E/KeQWMjdGe9kw51RAgoWFDi0coP4jinaH14Nrt/J8z3U4A== +lightningcss-linux-arm64-musl@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz#6be36692e810b718040802fd809623cffe732133" + integrity sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg== lightningcss-linux-x64-gnu@1.30.2: version "1.30.2" resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz#5a89814c8e63213a5965c3d166dff83c36152b1a" integrity sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w== -lightningcss-linux-x64-musl@1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.27.0.tgz#247958daf622a030a6dc2285afa16b7184bdf21e" - integrity sha512-QKjTxXm8A9s6v9Tg3Fk0gscCQA1t/HMoF7Woy1u68wCk5kS4fR+q3vXa1p3++REW784cRAtkYKrPy6JKibrEZA== +lightningcss-linux-x64-gnu@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz#0b7803af4eb21cfd38dd39fe2abbb53c7dd091f6" + integrity sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA== lightningcss-linux-x64-musl@1.30.2: version "1.30.2" resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz#808c2e91ce0bf5d0af0e867c6152e5378c049728" integrity sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA== -lightningcss-win32-arm64-msvc@1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.27.0.tgz#64cfe473c264ef5dc275a4d57a516d77fcac6bc9" - integrity sha512-/wXegPS1hnhkeG4OXQKEMQeJd48RDC3qdh+OA8pCuOPCyvnm/yEayrJdJVqzBsqpy1aJklRCVxscpFur80o6iQ== +lightningcss-linux-x64-musl@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz#88dc8ba865ddddb1ac5ef04b0f161804418c163b" + integrity sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg== lightningcss-win32-arm64-msvc@1.30.2: version "1.30.2" resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz#ab4a8a8a2e6a82a4531e8bbb6bf0ff161ee6625a" integrity sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ== -lightningcss-win32-x64-msvc@1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.27.0.tgz#237d0dc87d9cdc9cf82536bcbc07426fa9f3f422" - integrity sha512-/OJLj94Zm/waZShL8nB5jsNj3CfNATLCTyFxZyouilfTmSoLDX7VlVAmhPHoZWVFp4vdmoiEbPEYC8HID3m6yw== +lightningcss-win32-arm64-msvc@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz#4f30ba3fa5e925f5b79f945e8cc0d176c3b1ab38" + integrity sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw== lightningcss-win32-x64-msvc@1.30.2: version "1.30.2" resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz#f01f382c8e0a27e1c018b0bee316d210eac43b6e" integrity sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw== +lightningcss-win32-x64-msvc@1.32.0: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz#141aa5605645064928902bb4af045fa7d9f4220a" + integrity sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q== + lightningcss@^1.27.0: version "1.30.2" resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.30.2.tgz#4ade295f25d140f487d37256f4cd40dc607696d0" @@ -11076,23 +11472,24 @@ lightningcss@^1.27.0: lightningcss-win32-arm64-msvc "1.30.2" lightningcss-win32-x64-msvc "1.30.2" -lightningcss@~1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.27.0.tgz#d4608e63044343836dd9769f6c8b5d607867649a" - integrity sha512-8f7aNmS1+etYSLHht0fQApPc2kNO8qGRutifN5rVIc6Xo6ABsEbqOr758UwI7ALVbTt4x1fllKt0PYgzD9S3yQ== +lightningcss@^1.30.1: + version "1.32.0" + resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.32.0.tgz#b85aae96486dcb1bf49a7c8571221273f4f1e4a9" + integrity sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ== dependencies: - detect-libc "^1.0.3" + detect-libc "^2.0.3" optionalDependencies: - lightningcss-darwin-arm64 "1.27.0" - lightningcss-darwin-x64 "1.27.0" - lightningcss-freebsd-x64 "1.27.0" - lightningcss-linux-arm-gnueabihf "1.27.0" - lightningcss-linux-arm64-gnu "1.27.0" - lightningcss-linux-arm64-musl "1.27.0" - lightningcss-linux-x64-gnu "1.27.0" - lightningcss-linux-x64-musl "1.27.0" - lightningcss-win32-arm64-msvc "1.27.0" - lightningcss-win32-x64-msvc "1.27.0" + lightningcss-android-arm64 "1.32.0" + lightningcss-darwin-arm64 "1.32.0" + lightningcss-darwin-x64 "1.32.0" + lightningcss-freebsd-x64 "1.32.0" + lightningcss-linux-arm-gnueabihf "1.32.0" + lightningcss-linux-arm64-gnu "1.32.0" + lightningcss-linux-arm64-musl "1.32.0" + lightningcss-linux-x64-gnu "1.32.0" + lightningcss-linux-x64-musl "1.32.0" + lightningcss-win32-arm64-msvc "1.32.0" + lightningcss-win32-x64-msvc "1.32.0" lilconfig@^2.1.0: version "2.1.0" @@ -11254,7 +11651,7 @@ lodash.camelcase@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== -lodash.debounce@4.0.8, lodash.debounce@^4.0.8: +lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== @@ -11408,6 +11805,11 @@ lru-cache@^10.0.1, lru-cache@^10.2.0: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== +lru-cache@^11.0.0: + version "11.3.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.3.3.tgz#d6c633c2a9657760fd30594d8d98da65330d9d78" + integrity sha512-JvNw9Y81y33E+BEYPr0U7omo+U9AySnsMsEiXgwT6yqd31VQWTLNQqmT4ou5eqPFUrTfIDFta2wKhB1hyohtAQ== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -11567,60 +11969,110 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -metro-babel-transformer@0.82.5: - version "0.82.5" - resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.82.5.tgz#a65ed29265d8257109ab8c37884e6e3a2edee86d" - integrity sha512-W/scFDnwJXSccJYnOFdGiYr9srhbHPdxX9TvvACOFsIXdLilh3XuxQl/wXW6jEJfgIb0jTvoTlwwrqvuwymr6Q== +metro-babel-transformer@0.83.3: + version "0.83.3" + resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.83.3.tgz#d8c134615530c9ee61364526d44ca4bb0c5343ea" + integrity sha512-1vxlvj2yY24ES1O5RsSIvg4a4WeL7PFXgKOHvXTXiW0deLvQr28ExXj6LjwCCDZ4YZLhq6HddLpZnX4dEdSq5g== dependencies: "@babel/core" "^7.25.2" flow-enums-runtime "^0.0.6" - hermes-parser "0.29.1" + hermes-parser "0.32.0" + nullthrows "^1.1.1" + +metro-babel-transformer@0.83.5: + version "0.83.5" + resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.83.5.tgz#91f3fa269171ad5189ebba625f1f0aa124ce06ea" + integrity sha512-d9FfmgUEVejTiSb7bkQeLRGl6aeno2UpuPm3bo3rCYwxewj03ymvOn8s8vnS4fBqAPQ+cE9iQM40wh7nGXR+eA== + dependencies: + "@babel/core" "^7.25.2" + flow-enums-runtime "^0.0.6" + hermes-parser "0.33.3" nullthrows "^1.1.1" -metro-cache-key@0.82.5: - version "0.82.5" - resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.82.5.tgz#290a0054b28a708266fb91c8028cf94be04f99c9" - integrity sha512-qpVmPbDJuRLrT4kcGlUouyqLGssJnbTllVtvIgXfR7ZuzMKf0mGS+8WzcqzNK8+kCyakombQWR0uDd8qhWGJcA== +metro-cache-key@0.83.3: + version "0.83.3" + resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.83.3.tgz#ae6c5d4eb1ad8d06a92bf7294ca730a8d880b573" + integrity sha512-59ZO049jKzSmvBmG/B5bZ6/dztP0ilp0o988nc6dpaDsU05Cl1c/lRf+yx8m9WW/JVgbmfO5MziBU559XjI5Zw== + dependencies: + flow-enums-runtime "^0.0.6" + +metro-cache-key@0.83.5: + version "0.83.5" + resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.83.5.tgz#96896a1768f0494a375e1d5957b7ad487e508a4c" + integrity sha512-Ycl8PBajB7bhbAI7Rt0xEyiF8oJ0RWX8EKkolV1KfCUlC++V/GStMSGpPLwnnBZXZWkCC5edBPzv1Hz1Yi0Euw== + dependencies: + flow-enums-runtime "^0.0.6" + +metro-cache@0.83.3: + version "0.83.3" + resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.83.3.tgz#f1245cc48570c47d8944495e61d67f0228f10172" + integrity sha512-3jo65X515mQJvKqK3vWRblxDEcgY55Sk3w4xa6LlfEXgQ9g1WgMh9m4qVZVwgcHoLy0a2HENTPCCX4Pk6s8c8Q== dependencies: + exponential-backoff "^3.1.1" flow-enums-runtime "^0.0.6" + https-proxy-agent "^7.0.5" + metro-core "0.83.3" -metro-cache@0.82.5: - version "0.82.5" - resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.82.5.tgz#4c8fe58cd5fa30b87db0b2b6a650a771ec6fe162" - integrity sha512-AwHV9607xZpedu1NQcjUkua8v7HfOTKfftl6Vc9OGr/jbpiJX6Gpy8E/V9jo/U9UuVYX2PqSUcVNZmu+LTm71Q== +metro-cache@0.83.5: + version "0.83.5" + resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.83.5.tgz#5675f4ad56905aa78fff3dec1b6bf213e0b6c86d" + integrity sha512-oH+s4U+IfZyg8J42bne2Skc90rcuESIYf86dYittcdWQtPfcaFXWpByPyTuWk3rR1Zz3Eh5HOrcVImfEhhJLng== dependencies: exponential-backoff "^3.1.1" flow-enums-runtime "^0.0.6" https-proxy-agent "^7.0.5" - metro-core "0.82.5" + metro-core "0.83.5" + +metro-config@0.83.3: + version "0.83.3" + resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.83.3.tgz#a30e7a69b5cf8c4ac4c4b68b1b4c33649ae129a2" + integrity sha512-mTel7ipT0yNjKILIan04bkJkuCzUUkm2SeEaTads8VfEecCh+ltXchdq6DovXJqzQAXuR2P9cxZB47Lg4klriA== + dependencies: + connect "^3.6.5" + flow-enums-runtime "^0.0.6" + jest-validate "^29.7.0" + metro "0.83.3" + metro-cache "0.83.3" + metro-core "0.83.3" + metro-runtime "0.83.3" + yaml "^2.6.1" -metro-config@0.82.5, metro-config@^0.82.0: - version "0.82.5" - resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.82.5.tgz#07366f32c3fe6203d630af7df4781900816c7c85" - integrity sha512-/r83VqE55l0WsBf8IhNmc/3z71y2zIPe5kRSuqA5tY/SL/ULzlHUJEMd1szztd0G45JozLwjvrhAzhDPJ/Qo/g== +metro-config@0.83.5, metro-config@^0.83.1: + version "0.83.5" + resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.83.5.tgz#a3dd20fc5d5582aa4ad3704678e52abcf4d46b2b" + integrity sha512-JQ/PAASXH7yczgV6OCUSRhZYME+NU8NYjI2RcaG5ga4QfQ3T/XdiLzpSb3awWZYlDCcQb36l4Vl7i0Zw7/Tf9w== dependencies: connect "^3.6.5" - cosmiconfig "^5.0.5" flow-enums-runtime "^0.0.6" jest-validate "^29.7.0" - metro "0.82.5" - metro-cache "0.82.5" - metro-core "0.82.5" - metro-runtime "0.82.5" + metro "0.83.5" + metro-cache "0.83.5" + metro-core "0.83.5" + metro-runtime "0.83.5" + yaml "^2.6.1" + +metro-core@0.83.3: + version "0.83.3" + resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.83.3.tgz#007e93f7d1983777da8988dfb103ad897c9835b8" + integrity sha512-M+X59lm7oBmJZamc96usuF1kusd5YimqG/q97g4Ac7slnJ3YiGglW5CsOlicTR5EWf8MQFxxjDoB6ytTqRe8Hw== + dependencies: + flow-enums-runtime "^0.0.6" + lodash.throttle "^4.1.1" + metro-resolver "0.83.3" -metro-core@0.82.5, metro-core@^0.82.0: - version "0.82.5" - resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.82.5.tgz#fda1b2f7365e3a09055dd72ba1681f8fc1f6f492" - integrity sha512-OJL18VbSw2RgtBm1f2P3J5kb892LCVJqMvslXxuxjAPex8OH7Eb8RBfgEo7VZSjgb/LOf4jhC4UFk5l5tAOHHA== +metro-core@0.83.5, metro-core@^0.83.1: + version "0.83.5" + resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.83.5.tgz#1592033633034feb5d368d22bf18e38052146970" + integrity sha512-YcVcLCrf0ed4mdLa82Qob0VxYqfhmlRxUS8+TO4gosZo/gLwSvtdeOjc/Vt0pe/lvMNrBap9LlmvZM8FIsMgJQ== dependencies: flow-enums-runtime "^0.0.6" lodash.throttle "^4.1.1" - metro-resolver "0.82.5" + metro-resolver "0.83.5" -metro-file-map@0.82.5: - version "0.82.5" - resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.82.5.tgz#3e47410a9ce8f6c913480970226a17371c2d2974" - integrity sha512-vpMDxkGIB+MTN8Af5hvSAanc6zXQipsAUO+XUx3PCQieKUfLwdoa8qaZ1WAQYRpaU+CJ8vhBcxtzzo3d9IsCIQ== +metro-file-map@0.83.3: + version "0.83.3" + resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.83.3.tgz#3d79fbb1d379ab178dd895ce54cb5ecb183d74a2" + integrity sha512-jg5AcyE0Q9Xbbu/4NAwwZkmQn7doJCKGW0SLeSJmzNB9Z24jBe0AL2PHNMy4eu0JiKtNWHz9IiONGZWq7hjVTA== dependencies: debug "^4.4.0" fb-watchman "^2.0.0" @@ -11632,61 +12084,126 @@ metro-file-map@0.82.5: nullthrows "^1.1.1" walker "^1.0.7" -metro-minify-terser@0.82.5: - version "0.82.5" - resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.82.5.tgz#5dc77d53b6ef4079bd9c752ae046d557df4ae584" - integrity sha512-v6Nx7A4We6PqPu/ta1oGTqJ4Usz0P7c+3XNeBxW9kp8zayS3lHUKR0sY0wsCHInxZlNAEICx791x+uXytFUuwg== +metro-file-map@0.83.5: + version "0.83.5" + resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.83.5.tgz#394aa61d54b3822f10e68c18cbd1318f18865d20" + integrity sha512-ZEt8s3a1cnYbn40nyCD+CsZdYSlwtFh2kFym4lo+uvfM+UMMH+r/BsrC6rbNClSrt+B7rU9T+Te/sh/NL8ZZKQ== + dependencies: + debug "^4.4.0" + fb-watchman "^2.0.0" + flow-enums-runtime "^0.0.6" + graceful-fs "^4.2.4" + invariant "^2.2.4" + jest-worker "^29.7.0" + micromatch "^4.0.4" + nullthrows "^1.1.1" + walker "^1.0.7" + +metro-minify-terser@0.83.3: + version "0.83.3" + resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.83.3.tgz#c1c70929c86b14c8bf03e6321b4f9310bc8dbe87" + integrity sha512-O2BmfWj6FSfzBLrNCXt/rr2VYZdX5i6444QJU0fFoc7Ljg+Q+iqebwE3K0eTvkI6TRjELsXk1cjU+fXwAR4OjQ== + dependencies: + flow-enums-runtime "^0.0.6" + terser "^5.15.0" + +metro-minify-terser@0.83.5: + version "0.83.5" + resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.83.5.tgz#ee43a11a9d3442760781434c599d45eb1274e6fd" + integrity sha512-Toe4Md1wS1PBqbvB0cFxBzKEVyyuYTUb0sgifAZh/mSvLH84qA1NAWik9sISWatzvfWf3rOGoUoO5E3f193a3Q== dependencies: flow-enums-runtime "^0.0.6" terser "^5.15.0" -metro-resolver@0.82.5: - version "0.82.5" - resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.82.5.tgz#cb810038d488a47334df444312b23f0090eca5c3" - integrity sha512-kFowLnWACt3bEsuVsaRNgwplT8U7kETnaFHaZePlARz4Fg8tZtmRDUmjaD68CGAwc0rwdwNCkWizLYpnyVcs2g== +metro-resolver@0.83.3: + version "0.83.3" + resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.83.3.tgz#06207bdddc280b9335722a8c992aeec017413942" + integrity sha512-0js+zwI5flFxb1ktmR///bxHYg7OLpRpWZlBBruYG8OKYxeMP7SV0xQ/o/hUelrEMdK4LJzqVtHAhBm25LVfAQ== + dependencies: + flow-enums-runtime "^0.0.6" + +metro-resolver@0.83.5: + version "0.83.5" + resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.83.5.tgz#72340ca8071941eafe92ff2dcb8e33c581870ef7" + integrity sha512-7p3GtzVUpbAweJeCcUJihJeOQl1bDuimO5ueo1K0BUpUtR41q5EilbQ3klt16UTPPMpA+tISWBtsrqU556mY1A== + dependencies: + flow-enums-runtime "^0.0.6" + +metro-runtime@0.83.3: + version "0.83.3" + resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.83.3.tgz#ff504df5d93f38b1af396715b327e589ba8d184d" + integrity sha512-JHCJb9ebr9rfJ+LcssFYA2x1qPYuSD/bbePupIGhpMrsla7RCwC/VL3yJ9cSU+nUhU4c9Ixxy8tBta+JbDeZWw== dependencies: + "@babel/runtime" "^7.25.0" flow-enums-runtime "^0.0.6" -metro-runtime@0.82.5, metro-runtime@^0.82.0: - version "0.82.5" - resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.82.5.tgz#97840760e4cee49f08948dd918dbeba08dd0d0ec" - integrity sha512-rQZDoCUf7k4Broyw3Ixxlq5ieIPiR1ULONdpcYpbJQ6yQ5GGEyYjtkztGD+OhHlw81LCR2SUAoPvtTus2WDK5g== +metro-runtime@0.83.5, metro-runtime@^0.83.1: + version "0.83.5" + resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.83.5.tgz#52c1edafc6cc82e57729cc9c21700ab1e53a1777" + integrity sha512-f+b3ue9AWTVlZe2Xrki6TAoFtKIqw30jwfk7GQ1rDUBQaE0ZQ+NkiMEtb9uwH7uAjJ87U7Tdx1Jg1OJqUfEVlA== dependencies: "@babel/runtime" "^7.25.0" flow-enums-runtime "^0.0.6" -metro-source-map@0.82.5, metro-source-map@^0.82.0: - version "0.82.5" - resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.82.5.tgz#85e2e9672bff6d6cefb3b65b96fcc69f929c69c6" - integrity sha512-wH+awTOQJVkbhn2SKyaw+0cd+RVSCZ3sHVgyqJFQXIee/yLs3dZqKjjeKKhhVeudgjXo7aE/vSu/zVfcQEcUfw== +metro-source-map@0.83.3: + version "0.83.3" + resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.83.3.tgz#04bb464f7928ea48bcdfd18912c8607cf317c898" + integrity sha512-xkC3qwUBh2psVZgVavo8+r2C9Igkk3DibiOXSAht1aYRRcztEZNFtAMtfSB7sdO2iFMx2Mlyu++cBxz/fhdzQg== dependencies: "@babel/traverse" "^7.25.3" "@babel/traverse--for-generate-function-map" "npm:@babel/traverse@^7.25.3" "@babel/types" "^7.25.2" flow-enums-runtime "^0.0.6" invariant "^2.2.4" - metro-symbolicate "0.82.5" + metro-symbolicate "0.83.3" + nullthrows "^1.1.1" + ob1 "0.83.3" + source-map "^0.5.6" + vlq "^1.0.0" + +metro-source-map@0.83.5, metro-source-map@^0.83.1: + version "0.83.5" + resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.83.5.tgz#384f311f83fa2bf51cbec08d77210aa951bf9ee3" + integrity sha512-VT9bb2KO2/4tWY9Z2yeZqTUao7CicKAOps9LUg2aQzsz+04QyuXL3qgf1cLUVRjA/D6G5u1RJAlN1w9VNHtODQ== + dependencies: + "@babel/traverse" "^7.29.0" + "@babel/types" "^7.29.0" + flow-enums-runtime "^0.0.6" + invariant "^2.2.4" + metro-symbolicate "0.83.5" + nullthrows "^1.1.1" + ob1 "0.83.5" + source-map "^0.5.6" + vlq "^1.0.0" + +metro-symbolicate@0.83.3: + version "0.83.3" + resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.83.3.tgz#67af03950f0dfe19a7c059e3983e39a31e95d03a" + integrity sha512-F/YChgKd6KbFK3eUR5HdUsfBqVsanf5lNTwFd4Ca7uuxnHgBC3kR/Hba/RGkenR3pZaGNp5Bu9ZqqP52Wyhomw== + dependencies: + flow-enums-runtime "^0.0.6" + invariant "^2.2.4" + metro-source-map "0.83.3" nullthrows "^1.1.1" - ob1 "0.82.5" source-map "^0.5.6" vlq "^1.0.0" -metro-symbolicate@0.82.5: - version "0.82.5" - resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.82.5.tgz#b53255cad11f1e6795f319ca4b41857bfe295d65" - integrity sha512-1u+07gzrvYDJ/oNXuOG1EXSvXZka/0JSW1q2EYBWerVKMOhvv9JzDGyzmuV7hHbF2Hg3T3S2uiM36sLz1qKsiw== +metro-symbolicate@0.83.5: + version "0.83.5" + resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.83.5.tgz#62167db423be6c68b4b9f39935c9cb7330cc9526" + integrity sha512-EMIkrjNRz/hF+p0RDdxoE60+dkaTLPN3vaaGkFmX5lvFdO6HPfHA/Ywznzkev+za0VhPQ5KSdz49/MALBRteHA== dependencies: flow-enums-runtime "^0.0.6" invariant "^2.2.4" - metro-source-map "0.82.5" + metro-source-map "0.83.5" nullthrows "^1.1.1" source-map "^0.5.6" vlq "^1.0.0" -metro-transform-plugins@0.82.5: - version "0.82.5" - resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.82.5.tgz#678da4d0f9085b2a3fc0b4350062f19cc625c5fc" - integrity sha512-57Bqf3rgq9nPqLrT2d9kf/2WVieTFqsQ6qWHpEng5naIUtc/Iiw9+0bfLLWSAw0GH40iJ4yMjFcFJDtNSYynMA== +metro-transform-plugins@0.83.3: + version "0.83.3" + resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.83.3.tgz#2c59ba841e269363cf3acb13138cb992f0c75013" + integrity sha512-eRGoKJU6jmqOakBMH5kUB7VitEWiNrDzBHpYbkBXW7C5fUGeOd2CyqrosEzbMK5VMiZYyOcNFEphvxk3OXey2A== dependencies: "@babel/core" "^7.25.2" "@babel/generator" "^7.25.0" @@ -11695,29 +12212,60 @@ metro-transform-plugins@0.82.5: flow-enums-runtime "^0.0.6" nullthrows "^1.1.1" -metro-transform-worker@0.82.5: - version "0.82.5" - resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.82.5.tgz#aabdccf17aaa584ec0fd97b5283e806958fb3418" - integrity sha512-mx0grhAX7xe+XUQH6qoHHlWedI8fhSpDGsfga7CpkO9Lk9W+aPitNtJWNGrW8PfjKEWbT9Uz9O50dkI8bJqigw== +metro-transform-plugins@0.83.5: + version "0.83.5" + resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.83.5.tgz#ba21c6a5fa9bf6c5c2c222e2c8e7a668ffb3d341" + integrity sha512-KxYKzZL+lt3Os5H2nx7YkbkWVduLZL5kPrE/Yq+Prm/DE1VLhpfnO6HtPs8vimYFKOa58ncl60GpoX0h7Wm0Vw== + dependencies: + "@babel/core" "^7.25.2" + "@babel/generator" "^7.29.1" + "@babel/template" "^7.28.6" + "@babel/traverse" "^7.29.0" + flow-enums-runtime "^0.0.6" + nullthrows "^1.1.1" + +metro-transform-worker@0.83.3: + version "0.83.3" + resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.83.3.tgz#ca6ae4a02b0f61b33299e6e56bacaba32dcd607f" + integrity sha512-Ztekew9t/gOIMZX1tvJOgX7KlSLL5kWykl0Iwu2cL2vKMKVALRl1hysyhUw0vjpAvLFx+Kfq9VLjnHIkW32fPA== dependencies: "@babel/core" "^7.25.2" "@babel/generator" "^7.25.0" "@babel/parser" "^7.25.3" "@babel/types" "^7.25.2" flow-enums-runtime "^0.0.6" - metro "0.82.5" - metro-babel-transformer "0.82.5" - metro-cache "0.82.5" - metro-cache-key "0.82.5" - metro-minify-terser "0.82.5" - metro-source-map "0.82.5" - metro-transform-plugins "0.82.5" + metro "0.83.3" + metro-babel-transformer "0.83.3" + metro-cache "0.83.3" + metro-cache-key "0.83.3" + metro-minify-terser "0.83.3" + metro-source-map "0.83.3" + metro-transform-plugins "0.83.3" nullthrows "^1.1.1" -metro@0.82.5, metro@^0.82.0: - version "0.82.5" - resolved "https://registry.yarnpkg.com/metro/-/metro-0.82.5.tgz#a27fbc08dd283a14ae58496288c10adaae65f461" - integrity sha512-8oAXxL7do8QckID/WZEKaIFuQJFUTLzfVcC48ghkHhNK2RGuQq8Xvf4AVd+TUA0SZtX0q8TGNXZ/eba1ckeGCg== +metro-transform-worker@0.83.5: + version "0.83.5" + resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.83.5.tgz#8616b54282e727027fdb5c475aade719394a8e8a" + integrity sha512-8N4pjkNXc6ytlP9oAM6MwqkvUepNSW39LKYl9NjUMpRDazBQ7oBpQDc8Sz4aI8jnH6AGhF7s1m/ayxkN1t04yA== + dependencies: + "@babel/core" "^7.25.2" + "@babel/generator" "^7.29.1" + "@babel/parser" "^7.29.0" + "@babel/types" "^7.29.0" + flow-enums-runtime "^0.0.6" + metro "0.83.5" + metro-babel-transformer "0.83.5" + metro-cache "0.83.5" + metro-cache-key "0.83.5" + metro-minify-terser "0.83.5" + metro-source-map "0.83.5" + metro-transform-plugins "0.83.5" + nullthrows "^1.1.1" + +metro@0.83.3: + version "0.83.3" + resolved "https://registry.yarnpkg.com/metro/-/metro-0.83.3.tgz#1e7e04c15519af746f8932c7f9c553d92c39e922" + integrity sha512-+rP+/GieOzkt97hSJ0MrPOuAH/jpaS21ZDvL9DJ35QYRDlQcwzcvUlGUf79AnQxq/2NPiS/AULhhM4TKutIt8Q== dependencies: "@babel/code-frame" "^7.24.7" "@babel/core" "^7.25.2" @@ -11734,24 +12282,24 @@ metro@0.82.5, metro@^0.82.0: error-stack-parser "^2.0.6" flow-enums-runtime "^0.0.6" graceful-fs "^4.2.4" - hermes-parser "0.29.1" + hermes-parser "0.32.0" image-size "^1.0.2" invariant "^2.2.4" jest-worker "^29.7.0" jsc-safe-url "^0.2.2" lodash.throttle "^4.1.1" - metro-babel-transformer "0.82.5" - metro-cache "0.82.5" - metro-cache-key "0.82.5" - metro-config "0.82.5" - metro-core "0.82.5" - metro-file-map "0.82.5" - metro-resolver "0.82.5" - metro-runtime "0.82.5" - metro-source-map "0.82.5" - metro-symbolicate "0.82.5" - metro-transform-plugins "0.82.5" - metro-transform-worker "0.82.5" + metro-babel-transformer "0.83.3" + metro-cache "0.83.3" + metro-cache-key "0.83.3" + metro-config "0.83.3" + metro-core "0.83.3" + metro-file-map "0.83.3" + metro-resolver "0.83.3" + metro-runtime "0.83.3" + metro-source-map "0.83.3" + metro-symbolicate "0.83.3" + metro-transform-plugins "0.83.3" + metro-transform-worker "0.83.3" mime-types "^2.1.27" nullthrows "^1.1.1" serialize-error "^2.1.0" @@ -11760,6 +12308,52 @@ metro@0.82.5, metro@^0.82.0: ws "^7.5.10" yargs "^17.6.2" +metro@0.83.5, metro@^0.83.1: + version "0.83.5" + resolved "https://registry.yarnpkg.com/metro/-/metro-0.83.5.tgz#f5441075d5211c980ac8c79109e9e6fa2df68924" + integrity sha512-BgsXevY1MBac/3ZYv/RfNFf/4iuW9X7f4H8ZNkiH+r667HD9sVujxcmu4jvEzGCAm4/WyKdZCuyhAcyhTHOucQ== + dependencies: + "@babel/code-frame" "^7.29.0" + "@babel/core" "^7.25.2" + "@babel/generator" "^7.29.1" + "@babel/parser" "^7.29.0" + "@babel/template" "^7.28.6" + "@babel/traverse" "^7.29.0" + "@babel/types" "^7.29.0" + accepts "^2.0.0" + chalk "^4.0.0" + ci-info "^2.0.0" + connect "^3.6.5" + debug "^4.4.0" + error-stack-parser "^2.0.6" + flow-enums-runtime "^0.0.6" + graceful-fs "^4.2.4" + hermes-parser "0.33.3" + image-size "^1.0.2" + invariant "^2.2.4" + jest-worker "^29.7.0" + jsc-safe-url "^0.2.2" + lodash.throttle "^4.1.1" + metro-babel-transformer "0.83.5" + metro-cache "0.83.5" + metro-cache-key "0.83.5" + metro-config "0.83.5" + metro-core "0.83.5" + metro-file-map "0.83.5" + metro-resolver "0.83.5" + metro-runtime "0.83.5" + metro-source-map "0.83.5" + metro-symbolicate "0.83.5" + metro-transform-plugins "0.83.5" + metro-transform-worker "0.83.5" + mime-types "^3.0.1" + nullthrows "^1.1.1" + serialize-error "^2.1.0" + source-map "^0.5.6" + throat "^5.0.0" + ws "^7.5.10" + yargs "^17.6.2" + micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5, micromatch@^4.0.8, micromatch@~4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" @@ -11773,7 +12367,7 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -"mime-db@>= 1.43.0 < 2": +"mime-db@>= 1.43.0 < 2", mime-db@^1.54.0: version "1.54.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== @@ -11785,6 +12379,13 @@ mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.34: dependencies: mime-db "1.52.0" +mime-types@^3.0.0, mime-types@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.2.tgz#39002d4182575d5af036ffa118100f2524b2e2ab" + integrity sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A== + dependencies: + mime-db "^1.54.0" + mime@1.6.0, mime@^1.3.4: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -11844,6 +12445,13 @@ minimatch@^10.0.3: dependencies: "@isaacs/brace-expansion" "^5.0.0" +minimatch@^10.2.2: + version "10.2.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.5.tgz#bd48687a0be38ed2961399105600f832095861d1" + integrity sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg== + dependencies: + brace-expansion "^5.0.5" + minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -11933,6 +12541,11 @@ minipass@^4.2.4: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== +minipass@^7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.3.tgz#79389b4eb1bb2d003a9bba87d492f2bd37bdc65b" + integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== + minizlib@^3.0.1, minizlib@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-3.1.0.tgz#6ad76c3a8f10227c9b51d1c9ac8e30b27f5a251c" @@ -12001,7 +12614,7 @@ mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nanoid@^3.3.1, nanoid@^3.3.11, nanoid@^3.3.7: +nanoid@^3.3.1, nanoid@^3.3.11, nanoid@^3.3.7, nanoid@^3.3.8: version "3.3.11" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== @@ -12045,11 +12658,6 @@ negotiator@~0.6.4: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== -neo-async@^2.6.1: - version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - nested-error-stacks@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.0.1.tgz#d2cc9fc5235ddb371fc44d506234339c8e4b0a4b" @@ -12096,10 +12704,10 @@ node-fetch@^2.6.1, node-fetch@^2.6.7, node-fetch@^2.7.0: dependencies: whatwg-url "^5.0.0" -node-forge@^1.2.1, node-forge@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" - integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== +node-forge@^1.3.3: + version "1.4.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.4.0.tgz#1c7b7d8bdc2d078739f58287d589d903a11b2fc2" + integrity sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ== node-gyp@^11.2.0: version "11.5.0" @@ -12127,6 +12735,11 @@ node-releases@^2.0.21: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.21.tgz#f59b018bc0048044be2d4c4c04e4c8b18160894c" integrity sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw== +node-releases@^2.0.36: + version "2.0.37" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.37.tgz#9bd4f10b77ba39c2b9402d4e8399c482a797f671" + integrity sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg== + nopt@^8.0.0: version "8.1.0" resolved "https://registry.yarnpkg.com/nopt/-/nopt-8.1.0.tgz#b11d38caf0f8643ce885818518064127f602eae3" @@ -12266,10 +12879,17 @@ nwsapi@^2.2.2: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.22.tgz#109f9530cda6c156d6a713cdf5939e9f0de98b9d" integrity sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ== -ob1@0.82.5: - version "0.82.5" - resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.82.5.tgz#a2860e39385f4602bc2666c46f331b7531b94a8b" - integrity sha512-QyQQ6e66f+Ut/qUVjEce0E/wux5nAGLXYZDn1jr15JWstHsCH3l6VVrg8NKDptW9NEiBXKOJeGF/ydxeSDF3IQ== +ob1@0.83.3: + version "0.83.3" + resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.83.3.tgz#2208e20c9070e9beff3ad067f2db458fa6b07014" + integrity sha512-egUxXCDwoWG06NGCS5s5AdcpnumHKJlfd3HH06P3m9TEMwwScfcY35wpQxbm9oHof+dM/lVH9Rfyu1elTVelSA== + dependencies: + flow-enums-runtime "^0.0.6" + +ob1@0.83.5: + version "0.83.5" + resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.83.5.tgz#f9c289d759142b76577948eea7fd1f07d36f825f" + integrity sha512-vNKPYC8L5ycVANANpF/S+WZHpfnRWKx/F3AYP4QMn6ZJTh+l2HOrId0clNkEmua58NB9vmI9Qh7YOoV/4folYg== dependencies: flow-enums-runtime "^0.0.6" @@ -12645,14 +13265,6 @@ parse-headers@^2.0.0: resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.6.tgz#7940f0abe5fe65df2dd25d4ce8800cb35b49d01c" integrity sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A== -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - integrity sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw== - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" @@ -12780,6 +13392,14 @@ path-scurry@^1.11.1, path-scurry@^1.6.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" +path-scurry@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.2.tgz#6be0d0ee02a10d9e0de7a98bae65e182c9061f85" + integrity sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg== + dependencies: + lru-cache "^11.0.0" + minipass "^7.1.2" + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -12834,6 +13454,11 @@ picomatch@^4.0.2, picomatch@^4.0.3: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== +picomatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589" + integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A== + pidtree@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" @@ -13096,7 +13721,7 @@ prompts@^2.0.1, prompts@^2.2.1, prompts@^2.3.2: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@15.8.1, prop-types@^15.8.1: +prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -13230,7 +13855,7 @@ rc@1.2.8, rc@~1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-devtools-core@^6.1.1: +react-devtools-core@^6.1.5: version "6.1.5" resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-6.1.5.tgz#c5eca79209dab853a03b2158c034c5166975feee" integrity sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA== @@ -13238,12 +13863,12 @@ react-devtools-core@^6.1.1: shell-quote "^1.6.1" ws "^7" -react-dom@19.0.0: - version "19.0.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.0.0.tgz#43446f1f01c65a4cd7f7588083e686a6726cfb57" - integrity sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ== +react-dom@19.1.0: + version "19.1.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.1.0.tgz#133558deca37fa1d682708df8904b25186793623" + integrity sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g== dependencies: - scheduler "^0.25.0" + scheduler "^0.26.0" react-error-boundary@~4.0.13: version "4.0.13" @@ -13285,7 +13910,7 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== -react-is@^19.0.0, react-is@^19.1.0: +react-is@^19.1.0: version "19.1.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.1.1.tgz#038ebe313cf18e1fd1235d51c87360eb87f7c36a" integrity sha512-tr41fA15Vn8p4X9ntI+yCyeGSf1TlYaY5vlTZfQmeLBrFo3psOPX6HhTDnFNL9uj3EhP0KAQ80cugCl4b4BERA== @@ -13329,10 +13954,10 @@ react-native-flash-message@~0.4.2: prop-types "^15.8.1" react-native-iphone-screen-helper "^2.0.2" -react-native-gesture-handler@~2.24.0: - version "2.24.0" - resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-2.24.0.tgz#b6e1f13ec9bf8dfa5f4911854b6e0d73d882a81a" - integrity sha512-ZdWyOd1C8axKJHIfYxjJKCcxjWEpUtUWgTOVY2wynbiveSQDm8X/PDyAKXSer/GOtIpjudUbACOndZXCN3vHsw== +react-native-gesture-handler@~2.28.0: + version "2.28.0" + resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-2.28.0.tgz#07fb4f5eae72f810aac3019b060d26c1835bfd0c" + integrity sha512-0msfJ1vRxXKVgTgvL+1ZOoYw3/0z1R+Ked0+udoJhyplC2jbVKIJ8Z1bzWdpQRCV3QcQ87Op0zJVE5DhKK2A0A== dependencies: "@egjs/hammerjs" "^2.0.17" hoist-non-react-statics "^3.3.0" @@ -13343,20 +13968,15 @@ react-native-iphone-screen-helper@^2.0.2: resolved "https://registry.yarnpkg.com/react-native-iphone-screen-helper/-/react-native-iphone-screen-helper-2.2.1.tgz#34125de16426f1011ecec595be01876be9ccf2bf" integrity sha512-gMHawcFa9O8St9rJ6zugW55O/sM3UUTLqc4MDaZQewBTIDjfvHzLmk2A/+PxySl5ZTfv6fyIV5g+2ozLRW5kcw== -react-native-is-edge-to-edge@1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.1.7.tgz#28947688f9fafd584e73a4f935ea9603bd9b1939" - integrity sha512-EH6i7E8epJGIcu7KpfXYXiV2JFIYITtq+rVS8uEb+92naMRBdxhTuS8Wn2Q7j9sqyO0B+Xbaaf9VdipIAmGW4w== - -react-native-is-edge-to-edge@^1.1.6, react-native-is-edge-to-edge@^1.1.7, react-native-is-edge-to-edge@^1.2.1: +react-native-is-edge-to-edge@^1.1.6, react-native-is-edge-to-edge@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.2.1.tgz#64e10851abd9d176cbf2b40562f751622bde3358" integrity sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q== -react-native-keyboard-controller@^1.18.6: - version "1.18.6" - resolved "https://registry.yarnpkg.com/react-native-keyboard-controller/-/react-native-keyboard-controller-1.18.6.tgz#ee7449d669bb599c29399d7f5f8cfc72c8528e45" - integrity sha512-K/RMw3MdtuykkACFN5d9RTapAcO0v4T34gmSyHkEraU5UsX+fxEHd6j4MvL7KUihvmLLod0NV/mQC0nL4cOurw== +react-native-keyboard-controller@1.18.5: + version "1.18.5" + resolved "https://registry.yarnpkg.com/react-native-keyboard-controller/-/react-native-keyboard-controller-1.18.5.tgz#ae12131f2019c574178479d2c55784f55e08bb68" + integrity sha512-wbYN6Tcu3G5a05dhRYBgjgd74KqoYWuUmroLpigRg9cXy5uYo7prTMIvMgvLtARQtUF7BOtFggUnzgoBOgk0TQ== dependencies: react-native-is-edge-to-edge "^1.2.1" @@ -13377,41 +13997,31 @@ react-native-quick-base64@2.1.1: dependencies: base64-js "^1.5.1" -react-native-reanimated@~3.17.4: - version "3.17.5" - resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-3.17.5.tgz#09ebe3c9e3379c5c0c588b7ab30c131ea29b60f0" - integrity sha512-SxBK7wQfJ4UoWoJqQnmIC7ZjuNgVb9rcY5Xc67upXAFKftWg0rnkknTw6vgwnjRcvYThrjzUVti66XoZdDJGtw== +react-native-reanimated@~4.1.1: + version "4.1.7" + resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-4.1.7.tgz#b4e8524503a1b6ec1b5a40c460ee807a6a9fd2cf" + integrity sha512-Q4H6xA3Tn7QL0/E/KjI86I1KK4tcf+ErRE04LH34Etka2oVQhW6oXQ+Q8ZcDCVxiWp5vgbBH6XcH8BOo4w/Rhg== dependencies: - "@babel/plugin-transform-arrow-functions" "^7.0.0-0" - "@babel/plugin-transform-class-properties" "^7.0.0-0" - "@babel/plugin-transform-classes" "^7.0.0-0" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.0.0-0" - "@babel/plugin-transform-optional-chaining" "^7.0.0-0" - "@babel/plugin-transform-shorthand-properties" "^7.0.0-0" - "@babel/plugin-transform-template-literals" "^7.0.0-0" - "@babel/plugin-transform-unicode-regex" "^7.0.0-0" - "@babel/preset-typescript" "^7.16.7" - convert-source-map "^2.0.0" - invariant "^2.2.4" - react-native-is-edge-to-edge "1.1.7" + react-native-is-edge-to-edge "^1.2.1" + semver "^7.7.2" react-native-restart@0.0.27: version "0.0.27" resolved "https://registry.yarnpkg.com/react-native-restart/-/react-native-restart-0.0.27.tgz#43aa8210312c9dfa5ec7bd4b2f35238ad7972b19" integrity sha512-8KScVICrXwcTSJ1rjWkqVTHyEKQIttm5AIMGSK1QG1+RS5owYlE4z/1DykOTdWfVl9l16FIk0w9Xzk9ZO6jxlA== -react-native-safe-area-context@5.4.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-5.4.0.tgz#04b51940408c114f75628a12a93569d30c525454" - integrity sha512-JaEThVyJcLhA+vU0NU8bZ0a1ih6GiF4faZ+ArZLqpYbL6j7R3caRqj+mE3lEtKCuHgwjLg3bCxLL1GPUJZVqUA== +react-native-safe-area-context@~5.6.0: + version "5.6.2" + resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz#283e006f5b434fb247fcb4be0971ad7473d5c560" + integrity sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg== -react-native-screens@~4.11.1: - version "4.11.1" - resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.11.1.tgz#7d0f3d313d8ddc1e55437c5e038f15f8805dc991" - integrity sha512-F0zOzRVa3ptZfLpD0J8ROdo+y1fEPw+VBFq1MTY/iyDu08al7qFUO5hLMd+EYMda5VXGaTFCa8q7bOppUszhJw== +react-native-screens@~4.16.0: + version "4.16.0" + resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.16.0.tgz#efa42e77a092aa0b5277c9ae41391ea0240e0870" + integrity sha512-yIAyh7F/9uWkOzCi1/2FqvNvK6Wb9Y1+Kzn16SuGfN9YFJDTbwlzGRvePCNTOX0recpLQF3kc2FmvMUhyTCH1Q== dependencies: react-freeze "^1.0.0" - react-native-is-edge-to-edge "^1.1.7" + react-native-is-edge-to-edge "^1.2.1" warn-once "^0.1.0" react-native-svg-transformer@~1.5.1: @@ -13424,10 +14034,10 @@ react-native-svg-transformer@~1.5.1: "@svgr/plugin-svgo" "^8.1.0" path-dirname "^1.0.2" -react-native-svg@15.11.2: - version "15.11.2" - resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-15.11.2.tgz#7540e8e1eabc4dcd3b1e35ada5a1d9f1b96d37c4" - integrity sha512-+YfF72IbWQUKzCIydlijV1fLuBsQNGMT6Da2kFlo1sh+LE3BIm/2Q7AR1zAAR6L0BFLi1WaQPLfFUC9bNZpOmw== +react-native-svg@15.12.1: + version "15.12.1" + resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-15.12.1.tgz#7ba756dd6a235f86a2c312a1e7911f9b0d18ad3a" + integrity sha512-vCuZJDf8a5aNC2dlMovEv4Z0jjEUET53lm/iILFnFewa15b4atjVxU6Wirm6O9y6dEsdjDZVD7Q3QM4T1wlI8g== dependencies: css-select "^5.1.0" css-tree "^1.1.3" @@ -13440,10 +14050,10 @@ react-native-url-polyfill@^1.3.0: dependencies: whatwg-url-without-unicode "8.0.0-3" -react-native-web@^0.20.0: - version "0.20.0" - resolved "https://registry.yarnpkg.com/react-native-web/-/react-native-web-0.20.0.tgz#3fb0591999ed4b54d7822a2785547415e8a5c031" - integrity sha512-OOSgrw+aON6R3hRosCau/xVxdLzbjEcsLysYedka0ZON4ZZe6n9xgeN9ZkoejhARM36oTlUgHIQqxGutEJ9Wxg== +react-native-web@^0.21.0: + version "0.21.2" + resolved "https://registry.yarnpkg.com/react-native-web/-/react-native-web-0.21.2.tgz#0f6983dfea600d9cc1c66fda87ff9ca585eaa647" + integrity sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg== dependencies: "@babel/runtime" "^7.18.6" "@react-native/normalize-colors" "^0.74.1" @@ -13454,50 +14064,65 @@ react-native-web@^0.20.0: postcss-value-parser "^4.2.0" styleq "^0.1.3" -react-native-webview@~13.13.1: - version "13.13.5" - resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-13.13.5.tgz#4ef5f9310ddff5747f884a6655228ec9c7d52c73" - integrity sha512-MfC2B+woL4Hlj2WCzcb1USySKk+SteXnUKmKktOk/H/AQy5+LuVdkPKm8SknJ0/RxaxhZ48WBoTRGaqgR137hw== +react-native-webview@13.15.0: + version "13.15.0" + resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-13.15.0.tgz#b6d2f8d8dd65897db76659ddd8198d2c74ec5a79" + integrity sha512-Vzjgy8mmxa/JO6l5KZrsTC7YemSdq+qB01diA0FqjUTaWGAGwuykpJ73MDj3+mzBSlaDxAEugHzTtkUQkQEQeQ== dependencies: escape-string-regexp "^4.0.0" invariant "2.2.4" -react-native@0.79.6: - version "0.79.6" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.79.6.tgz#ee95428f67da2f62ede473eaa6e8a2f4ee40e272" - integrity sha512-kvIWSmf4QPfY41HC25TR285N7Fv0Pyn3DAEK8qRL9dA35usSaxsJkHfw+VqnonqJjXOaoKCEanwudRAJ60TBGA== +react-native-worklets@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/react-native-worklets/-/react-native-worklets-0.5.1.tgz#d153242655e3757b6c62a474768831157316ad33" + integrity sha512-lJG6Uk9YuojjEX/tQrCbcbmpdLCSFxDK1rJlkDhgqkVi1KZzG7cdcBFQRqyNOOzR9Y0CXNuldmtWTGOyM0k0+w== + dependencies: + "@babel/plugin-transform-arrow-functions" "^7.0.0-0" + "@babel/plugin-transform-class-properties" "^7.0.0-0" + "@babel/plugin-transform-classes" "^7.0.0-0" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.0.0-0" + "@babel/plugin-transform-optional-chaining" "^7.0.0-0" + "@babel/plugin-transform-shorthand-properties" "^7.0.0-0" + "@babel/plugin-transform-template-literals" "^7.0.0-0" + "@babel/plugin-transform-unicode-regex" "^7.0.0-0" + "@babel/preset-typescript" "^7.16.7" + convert-source-map "^2.0.0" + semver "7.7.2" + +react-native@0.81.5: + version "0.81.5" + resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.81.5.tgz#6c963f137d3979b22aef2d8482067775c8fe2fed" + integrity sha512-1w+/oSjEXZjMqsIvmkCRsOc8UBYv163bTWKTI8+1mxztvQPhCRYGTvZ/PL1w16xXHneIj/SLGfxWg2GWN2uexw== dependencies: "@jest/create-cache-key-function" "^29.7.0" - "@react-native/assets-registry" "0.79.6" - "@react-native/codegen" "0.79.6" - "@react-native/community-cli-plugin" "0.79.6" - "@react-native/gradle-plugin" "0.79.6" - "@react-native/js-polyfills" "0.79.6" - "@react-native/normalize-colors" "0.79.6" - "@react-native/virtualized-lists" "0.79.6" + "@react-native/assets-registry" "0.81.5" + "@react-native/codegen" "0.81.5" + "@react-native/community-cli-plugin" "0.81.5" + "@react-native/gradle-plugin" "0.81.5" + "@react-native/js-polyfills" "0.81.5" + "@react-native/normalize-colors" "0.81.5" + "@react-native/virtualized-lists" "0.81.5" abort-controller "^3.0.0" anser "^1.4.9" ansi-regex "^5.0.0" babel-jest "^29.7.0" - babel-plugin-syntax-hermes-parser "0.25.1" + babel-plugin-syntax-hermes-parser "0.29.1" base64-js "^1.5.1" - chalk "^4.0.0" commander "^12.0.0" - event-target-shim "^5.0.1" flow-enums-runtime "^0.0.6" glob "^7.1.1" invariant "^2.2.4" jest-environment-node "^29.7.0" memoize-one "^5.0.0" - metro-runtime "^0.82.0" - metro-source-map "^0.82.0" + metro-runtime "^0.83.1" + metro-source-map "^0.83.1" nullthrows "^1.1.1" pretty-format "^29.7.0" promise "^8.3.0" - react-devtools-core "^6.1.1" + react-devtools-core "^6.1.5" react-refresh "^0.14.0" regenerator-runtime "^0.13.2" - scheduler "0.25.0" + scheduler "0.26.0" semver "^7.1.3" stacktrace-parser "^0.1.10" whatwg-fetch "^3.0.0" @@ -13514,14 +14139,24 @@ react-refresh@^0.14.0, react-refresh@^0.14.2: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9" integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA== -react-server-dom-webpack@~19.0.0: - version "19.0.0" - resolved "https://registry.yarnpkg.com/react-server-dom-webpack/-/react-server-dom-webpack-19.0.0.tgz#c60819b6cb54e317e675ddc0c5959ff915b789d0" - integrity sha512-hLug9KEXLc8vnU9lDNe2b2rKKDaqrp5gNiES4uyu2Up3FZfZJZmdwLFXlWzdA9gTB/6/cWduSB2K1Lfag2pSvw== +react-remove-scroll-bar@^2.3.7: + version "2.3.8" + resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz#99c20f908ee467b385b68a3469b4a3e750012223" + integrity sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q== + dependencies: + react-style-singleton "^2.2.2" + tslib "^2.0.0" + +react-remove-scroll@^2.6.3: + version "2.7.2" + resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz#6442da56791117661978ae99cd29be9026fecca0" + integrity sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q== dependencies: - acorn-loose "^8.3.0" - neo-async "^2.6.1" - webpack-sources "^3.2.0" + react-remove-scroll-bar "^2.3.7" + react-style-singleton "^2.2.3" + tslib "^2.1.0" + use-callback-ref "^1.3.3" + use-sidecar "^1.1.3" react-stately@^3.21.0: version "3.41.0" @@ -13555,18 +14190,26 @@ react-stately@^3.21.0: "@react-stately/tree" "^3.9.2" "@react-types/shared" "^3.32.0" -react-test-renderer@19.0.0: - version "19.0.0" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-19.0.0.tgz#ca6fa322c58d4bfa34635788fe242a8c3daa4c7d" - integrity sha512-oX5u9rOQlHzqrE/64CNr0HB0uWxkCQmZNSfozlYvwE71TLVgeZxVf0IjouGEr1v7r1kcDifdAJBeOhdhxsG/DA== +react-style-singleton@^2.2.2, react-style-singleton@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.3.tgz#4265608be69a4d70cfe3047f2c6c88b2c3ace388" + integrity sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ== + dependencies: + get-nonce "^1.0.0" + tslib "^2.0.0" + +react-test-renderer@19.1.0: + version "19.1.0" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-19.1.0.tgz#89e1baa9e45a6da064b9760f92251d5b8e1f34ab" + integrity sha512-jXkSl3CpvPYEF+p/eGDLB4sPoDX8pKkYvRl9+rR8HxLY0X04vW7hCm1/0zHoUSjPZ3bDa+wXWNTDVIw/R8aDVw== dependencies: - react-is "^19.0.0" - scheduler "^0.25.0" + react-is "^19.1.0" + scheduler "^0.26.0" -react@19.0.0: - version "19.0.0" - resolved "https://registry.yarnpkg.com/react/-/react-19.0.0.tgz#6e1969251b9f108870aa4bff37a0ce9ddfaaabdd" - integrity sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ== +react@19.1.0: + version "19.1.0" + resolved "https://registry.yarnpkg.com/react/-/react-19.1.0.tgz#926864b6c48da7627f004795d6cce50e90793b75" + integrity sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg== read-binary-file-arch@^1.0.6: version "1.0.6" @@ -13655,15 +14298,6 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -recyclerlistview@4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/recyclerlistview/-/recyclerlistview-4.2.3.tgz#14032e7ad2f24396e24d5b3060c6ba76b567f000" - integrity sha512-STR/wj/FyT8EMsBzzhZ1l2goYirMkIgfV3gYEPxI3Kf3lOnu6f7Dryhyw7/IkQrgX5xtTcDrZMqytvteH9rL3g== - dependencies: - lodash.debounce "4.0.8" - prop-types "15.8.1" - ts-object-utils "0.0.5" - redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" @@ -13813,11 +14447,6 @@ resolve-cwd@^3.0.0: dependencies: resolve-from "^5.0.0" -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" - integrity sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw== - resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -14062,10 +14691,10 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" -scheduler@0.25.0, scheduler@^0.25.0: - version "0.25.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.25.0.tgz#336cd9768e8cceebf52d3c80e3dcf5de23e7e015" - integrity sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA== +scheduler@0.26.0, scheduler@^0.26.0: + version "0.26.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.26.0.tgz#4ce8a8c2a2095f13ea11bf9a445be50c555d6337" + integrity sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA== scoped-regex@^3.0.0: version "3.0.0" @@ -14092,21 +14721,26 @@ semver-compare@^1.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== +semver@7.7.2, semver@^7.1.3, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3, semver@^7.7.1: + version "7.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" + integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== + semver@^6.2.0, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.1.3, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3, semver@^7.7.1: - version "7.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" - integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== - semver@^7.3.2, semver@~7.7.3: version "7.7.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== +semver@^7.7.2: + version "7.7.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" + integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== + semver@~7.6.3: version "7.6.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" @@ -14238,6 +14872,11 @@ setprototypeof@1.2.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== +sf-symbols-typescript@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/sf-symbols-typescript/-/sf-symbols-typescript-2.2.0.tgz#926d6e0715e3d8784cadf7658431e36581254208" + integrity sha512-TPbeg0b7ylrswdGCji8FRGFAKuqbpQlLbL8SOle3j1iHSs5Ob5mhvMAxWN2UItOjgALAB5Zp3fmMfj8mbWvXKw== + shallowequal@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" @@ -14892,7 +15531,7 @@ styleq@^0.1.3: resolved "https://registry.yarnpkg.com/styleq/-/styleq-0.1.3.tgz#8efb2892debd51ce7b31dc09c227ad920decab71" integrity sha512-3ZUifmCDCQanjeej1f6kyl/BeP/Vae5EYkQ9iJfUm/QwZvlgnZzyflqAsAWYURdtea8Vkvswu2GrC57h3qffcA== -sucrase@3.35.0, sucrase@^3.32.0: +sucrase@^3.32.0: version "3.35.0" resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263" integrity sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA== @@ -14905,6 +15544,19 @@ sucrase@3.35.0, sucrase@^3.32.0: pirates "^4.0.1" ts-interface-checker "^0.1.9" +sucrase@~3.35.1: + version "3.35.1" + resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.1.tgz#4619ea50393fe8bd0ae5071c26abd9b2e346bfe1" + integrity sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.2" + commander "^4.0.0" + lines-and-columns "^1.1.6" + mz "^2.7.0" + pirates "^4.0.1" + tinyglobby "^0.2.11" + ts-interface-checker "^0.1.9" + sumchecker@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42" @@ -15066,6 +15718,17 @@ tar@^7.4.3: minizlib "^3.1.0" yallist "^5.0.0" +tar@^7.5.2: + version "7.5.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.13.tgz#0d214ed56781a26edc313581c0e2d929ceeb866d" + integrity sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng== + dependencies: + "@isaacs/fs-minipass" "^4.0.0" + chownr "^3.0.0" + minipass "^7.1.2" + minizlib "^3.1.0" + yallist "^5.0.0" + tar@^7.5.6, tar@^7.5.7: version "7.5.7" resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.7.tgz#adf99774008ba1c89819f15dbd6019c630539405" @@ -15077,11 +15740,6 @@ tar@^7.5.6, tar@^7.5.7: minizlib "^3.1.0" yallist "^5.0.0" -temp-dir@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e" - integrity sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg== - temp-file@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/temp-file/-/temp-file-3.4.0.tgz#766ea28911c683996c248ef1a20eea04d51652c7" @@ -15181,6 +15839,14 @@ tinyexec@^1.0.0: resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-1.0.1.tgz#70c31ab7abbb4aea0a24f55d120e5990bfa1e0b1" integrity sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw== +tinyglobby@^0.2.11: + version "0.2.16" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.16.tgz#1c3b7eb953fce42b226bc5a1ee06428281aff3d6" + integrity sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.4" + tinyglobby@^0.2.12, tinyglobby@^0.2.13: version "0.2.15" resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" @@ -15325,11 +15991,6 @@ ts-node@~10.9.2: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -ts-object-utils@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/ts-object-utils/-/ts-object-utils-0.0.5.tgz#95361cdecd7e52167cfc5e634c76345e90a26077" - integrity sha512-iV0GvHqOmilbIKJsfyfJY9/dNHCs969z3so90dQWsO1eMMozvTpnB1MEaUbb3FYtZTGjv5sIy/xmslEz0Rg2TA== - tsconfig-paths@^3.15.0: version "3.15.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" @@ -15350,7 +16011,7 @@ tsconfig@7: strip-bom "^3.0.0" strip-json-comments "^2.0.0" -tslib@2.8.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.8.0, tslib@^2.8.1: +tslib@2.8.1, tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.8.0, tslib@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -15486,10 +16147,10 @@ typescript@^5.6.3: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.2.tgz#d93450cddec5154a2d5cabe3b8102b83316fb2a6" integrity sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A== -typescript@~5.8.3: - version "5.8.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" - integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== +typescript@~5.9.2: + version "5.9.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" + integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== ua-parser-js@^0.7.33: version "0.7.41" @@ -15526,11 +16187,6 @@ undici@^6.18.2: resolved "https://registry.yarnpkg.com/undici/-/undici-6.21.3.tgz#185752ad92c3d0efe7a7d1f6854a50f83b552d7a" integrity sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw== -"undici@^6.18.2 || ^7.0.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/undici/-/undici-7.16.0.tgz#cb2a1e957726d458b536e3f076bf51f066901c1a" - integrity sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g== - unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz#cb3173fe47ca743e228216e4a3ddc4c84d628cc2" @@ -15559,10 +16215,10 @@ unicorn-magic@^0.1.0: resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== -unimodules-app-loader@~5.1.3: - version "5.1.3" - resolved "https://registry.yarnpkg.com/unimodules-app-loader/-/unimodules-app-loader-5.1.3.tgz#c3be527cd36120fc77d6843253075c8a9246f622" - integrity sha512-nPUkwfkpJWvdOQrVvyQSUol93/UdmsCVd9Hkx9RgAevmKSVYdZI+S87W73NGKl6QbwK9L1BDSY5OrQuo8Oq15g== +unimodules-app-loader@~6.0.8: + version "6.0.8" + resolved "https://registry.yarnpkg.com/unimodules-app-loader/-/unimodules-app-loader-6.0.8.tgz#81c868b726e24b7e37d708fe0117e1869c721cdb" + integrity sha512-fqS8QwT/MC/HAmw1NKCHdzsPA6WaLm0dNmoC5Pz6lL+cDGYeYCNdHMO9fy08aL2ZD7cVkNM0pSR/AoNRe+rslA== unique-filename@^4.0.0: version "4.0.0" @@ -15578,13 +16234,6 @@ unique-slug@^5.0.0: dependencies: imurmurhash "^0.1.4" -unique-string@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" - integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== - dependencies: - crypto-random-string "^2.0.0" - universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -15640,6 +16289,14 @@ update-browserslist-db@^1.1.3: escalade "^3.2.0" picocolors "^1.1.1" +update-browserslist-db@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" + integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + update-notifier@^7.0.0: version "7.3.1" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-7.3.1.tgz#49af1ad6acfa0ea01c0d0f3c04047c154ead7096" @@ -15671,11 +16328,31 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" +use-callback-ref@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.3.tgz#98d9fab067075841c5b2c6852090d5d0feabe2bf" + integrity sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg== + dependencies: + tslib "^2.0.0" + +use-latest-callback@^0.2.1: + version "0.2.6" + resolved "https://registry.yarnpkg.com/use-latest-callback/-/use-latest-callback-0.2.6.tgz#e5ea752808c86219acc179ace0ae3c1203255e77" + integrity sha512-FvRG9i1HSo0wagmX63Vrm8SnlUU3LMM3WyZkQ76RnslpBrX694AdG4A0zQBx2B3ZifFA0yv/BaEHGBnEax5rZg== + use-latest-callback@^0.2.4: version "0.2.4" resolved "https://registry.yarnpkg.com/use-latest-callback/-/use-latest-callback-0.2.4.tgz#35c0f028f85a3f4cf025b06011110e87cc18f57e" integrity sha512-LS2s2n1usUUnDq4oVh1ca6JFX9uSqUncTfAm44WMg0v6TxL7POUTk1B044NH8TeLkFbNajIsgDHcgNpNzZucdg== +use-sidecar@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.3.tgz#10e7fd897d130b896e2c546c63a5e8233d00efdb" + integrity sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ== + dependencies: + detect-node-es "^1.1.0" + tslib "^2.0.0" + use-sync-external-store@^1.2.2, use-sync-external-store@^1.4.0, use-sync-external-store@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz#55122e2a3edd2a6c106174c27485e0fd59bcfca0" @@ -15763,6 +16440,13 @@ vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== +vaul@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vaul/-/vaul-1.1.2.tgz#c959f8b9dc2ed4f7d99366caee433fbef91f5ba9" + integrity sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA== + dependencies: + "@radix-ui/react-dialog" "^1.1.1" + verror@^1.10.0: version "1.10.1" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.1.tgz#4bf09eeccf4563b109ed4b3d458380c972b0cdeb" @@ -15839,11 +16523,6 @@ webidl-conversions@^7.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== -webpack-sources@^3.2.0: - version "3.3.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.3.3.tgz#d4bf7f9909675d7a070ff14d0ef2a4f3c982c723" - integrity sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg== - webrtc-adapter@^9.0.1: version "9.0.3" resolved "https://registry.yarnpkg.com/webrtc-adapter/-/webrtc-adapter-9.0.3.tgz#b446ed7cd72129d00c652dd7b9a5716d9ffdd87d" @@ -16197,6 +16876,11 @@ yaml@^2.2.2, yaml@^2.3.4: resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.1.tgz#1870aa02b631f7e8328b93f8bc574fac5d6c4d79" integrity sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw== +yaml@^2.6.1: + version "2.8.3" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.3.tgz#a0d6bd2efb3dd03c59370223701834e60409bd7d" + integrity sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg== + yaml@~2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.1.tgz#42f2b1ba89203f374609572d5349fb8686500773"