diff --git a/android/app/build.gradle b/android/app/build.gradle index af82bf6..c44818d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,6 +1,7 @@ apply plugin: "com.android.application" apply plugin: "org.jetbrains.kotlin.android" apply plugin: "com.facebook.react" +apply plugin: 'com.google.gms.google-services' apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle" apply from: "../../node_modules/react-native-vector-icons/fonts.gradle" @@ -81,14 +82,14 @@ android { buildToolsVersion rootProject.ext.buildToolsVersion compileSdk rootProject.ext.compileSdkVersion - namespace "com.convex_crm" + namespace "app.convexsol.convexsolcrm" defaultConfig { - applicationId "com.convex_crm" + applicationId "app.convexsol.convexsolcrm" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" - resValue "string", "build_config_package", "com.convex_crm" + resValue "string", "build_config_package", "app.convexsol.convexsolcrm" } signingConfigs { debug { diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 0000000..8cb0fa6 --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "712641953559", + "project_id": "convex-crm-cdb42", + "storage_bucket": "convex-crm-cdb42.firebasestorage.app" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:712641953559:android:85a912a03f43148f5d0152", + "android_client_info": { + "package_name": "app.convexsol.convexsolcrm" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyBFeKwZ8CBf3x62U6qHTv-T_ufp4qTHDrA" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index fb78f39..f4bacd8 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,27 +1,28 @@ + + - - - + - - - - - - - + android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode" + android:launchMode="singleTask" + android:windowSoftInputMode="adjustResize" + android:exported="true" + android:theme="@style/BootTheme"> + + + + + + diff --git a/android/app/src/main/java/com/convex_crm/MainActivity.kt b/android/app/src/main/java/com/convex_crm/MainActivity.kt index feaa95d..f5e509b 100644 --- a/android/app/src/main/java/com/convex_crm/MainActivity.kt +++ b/android/app/src/main/java/com/convex_crm/MainActivity.kt @@ -1,12 +1,19 @@ -package com.convex_crm +package app.convexsol.convexsolcrm +import android.os.Bundle import com.facebook.react.ReactActivity import com.facebook.react.ReactActivityDelegate import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled import com.facebook.react.defaults.DefaultReactActivityDelegate +import com.zoontek.rnbootsplash.RNBootSplash class MainActivity : ReactActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + RNBootSplash.init(this, R.style.BootTheme) + super.onCreate(savedInstanceState) + } + /** * Returns the name of the main component registered from JavaScript. This is used to schedule * rendering of the component. diff --git a/android/app/src/main/java/com/convex_crm/MainApplication.kt b/android/app/src/main/java/com/convex_crm/MainApplication.kt index 0e80b2c..14c3552 100644 --- a/android/app/src/main/java/com/convex_crm/MainApplication.kt +++ b/android/app/src/main/java/com/convex_crm/MainApplication.kt @@ -1,4 +1,4 @@ -package com.convex_crm +package app.convexsol.convexsolcrm import android.app.Application import com.facebook.react.PackageList diff --git a/android/app/src/main/res/drawable-hdpi/bootsplash_logo.png b/android/app/src/main/res/drawable-hdpi/bootsplash_logo.png new file mode 100644 index 0000000..1c10762 Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/bootsplash_logo.png differ diff --git a/android/app/src/main/res/drawable-mdpi/bootsplash_logo.png b/android/app/src/main/res/drawable-mdpi/bootsplash_logo.png new file mode 100644 index 0000000..68d08e8 Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/bootsplash_logo.png differ diff --git a/android/app/src/main/res/drawable-xhdpi/bootsplash_logo.png b/android/app/src/main/res/drawable-xhdpi/bootsplash_logo.png new file mode 100644 index 0000000..d6ae84f Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/bootsplash_logo.png differ diff --git a/android/app/src/main/res/drawable-xxhdpi/bootsplash_logo.png b/android/app/src/main/res/drawable-xxhdpi/bootsplash_logo.png new file mode 100644 index 0000000..713b027 Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/bootsplash_logo.png differ diff --git a/android/app/src/main/res/drawable-xxxhdpi/bootsplash_logo.png b/android/app/src/main/res/drawable-xxxhdpi/bootsplash_logo.png new file mode 100644 index 0000000..7d9ff5d Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/bootsplash_logo.png differ diff --git a/android/app/src/main/res/drawable/ic_launcher_background.xml b/android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..ca3826a --- /dev/null +++ b/android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index a2f5908..b55a4dd 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..b305806 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png index 1b52399..84b8a0b 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index ff10afd..ce1e92b 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..c258c2d Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png index 115a4c7..03ad41d 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index dcd3cd8..b4fc194 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..4f1fec5 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png index 459ca60..bcc3731 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/drawable/ic_launcher_background.xml b/android/app/src/main/res/mipmap-xxhdpi/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..ca3826a --- /dev/null +++ b/android/app/src/main/res/mipmap-xxhdpi/drawable/ic_launcher_background.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 8ca12fe..c9f4b83 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..45d5602 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png index 8e19b41..09da6de 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index b824ebd..6070f9d 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..6992c1d Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png index 4c19a13..fa13b3b 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/playstore-icon.png b/android/app/src/main/res/playstore-icon.png new file mode 100644 index 0000000..e16cf8c Binary files /dev/null and b/android/app/src/main/res/playstore-icon.png differ diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..d4aaff4 --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,3 @@ + + #ffffff + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 7ba83a2..4b89c01 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -1,9 +1,12 @@ - - + diff --git a/android/build.gradle b/android/build.gradle index dad99b0..441fc30 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -15,6 +15,7 @@ buildscript { classpath("com.android.tools.build:gradle") classpath("com.facebook.react:react-native-gradle-plugin") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") + classpath ("com.google.gms:google-services:4.3.15") } } diff --git a/app/App.tsx b/app/App.tsx index 6628476..2501891 100644 --- a/app/App.tsx +++ b/app/App.tsx @@ -1,11 +1,13 @@ import React, { useEffect } from 'react'; -import { StyleSheet, StatusBar } from 'react-native'; +import { StatusBar } from 'react-native'; import { SafeAreaProvider } from 'react-native-safe-area-context'; import { RootNavigator } from './navigation/rootNavigator'; import { ThemeProvider, useTheme } from './theme'; import { Provider } from 'react-redux'; -import { store, persistor } from '@store'; +import { store, persistor, useAppDispatch, useAppSelector, getStatusList, getSourceList, getCountryList } from '@store'; import { PersistGate } from 'redux-persist/integration/react'; +import { NotificationService } from '@services'; +import BootSplash from 'react-native-bootsplash'; const ThemedStatusBar = () => { const { theme: colors, isDark } = useTheme(); @@ -23,13 +25,61 @@ const ThemedStatusBar = () => { ); }; +/** Dispatches startup data fetches once the Redux store is ready. */ +const AppInit = () => { + const dispatch = useAppDispatch(); + const token = useAppSelector(state => state.auth.token); + + useEffect(() => { + if (token) { + dispatch(getStatusList()); + dispatch(getSourceList()); + dispatch(getCountryList()); + } + }, [dispatch, token]); + + return null; +}; + const App = () => { + useEffect(() => { + let cleanup: (() => void) | undefined; + + const initApp = async () => { + try { + const cleanupFn = await NotificationService.initialize({ + onNotificationOpen: data => { + // TODO: Use data (e.g. { screen, id }) to deep-navigate once + console.log('[App] Notification opened with data:', data); + }, + onTokenRefreshed: token => { + // TODO: Send the refreshed token to your backend so the server + // always has the latest FCM token for this device. + console.log('[App] FCM token refreshed — sync to server:', token); + }, + }); + cleanup = cleanupFn; + } catch (err) { + console.error('[App] NotificationService.initialize error:', err); + } finally { + await BootSplash.hide({ fade: true }); + } + }; + + initApp(); + + return () => { + cleanup?.(); + }; + }, []); + return ( + diff --git a/app/api/index.ts b/app/api/index.ts index a1272e1..9ef0b73 100644 --- a/app/api/index.ts +++ b/app/api/index.ts @@ -1,3 +1,4 @@ export * from './authApi'; export * from './leadsApi'; export * from './leadDetailsApi'; +export * from './listApi'; diff --git a/app/api/leadsApi.ts b/app/api/leadsApi.ts index 3926dbd..2fd506d 100644 --- a/app/api/leadsApi.ts +++ b/app/api/leadsApi.ts @@ -1,4 +1,4 @@ -import { LeadItem } from '@interfaces'; +import { LeadItem, LeadCountItem } from '@interfaces'; import { api } from '@utils'; export const getLeadsApi = async ( @@ -8,3 +8,10 @@ export const getLeadsApi = async ( const url = `/api/leads/${leadId}/${staffId}`; return await api.get(url); }; + +export const getLeadCountListApi = async ( + staffId: string, +): Promise => { + const url = `/api/leadcountlist/${staffId}`; + return await api.get(url); +}; diff --git a/app/api/listApi.ts b/app/api/listApi.ts new file mode 100644 index 0000000..10c6720 --- /dev/null +++ b/app/api/listApi.ts @@ -0,0 +1,17 @@ +import { StatusListItem, SourceListItem, CountryListItem } from '@interfaces'; +import { api } from '@utils'; + +export const getStatusListApi = async (): Promise => { + const url = 'api/statuslist'; + return await api.get(url); +}; + +export const getSourceListApi = async (): Promise => { + const url = 'api/sourcelist'; + return await api.get(url); +}; + +export const getCountryListApi = async (): Promise => { + const url = 'api/countrylist'; + return await api.get(url); +}; diff --git a/app/components/actionCard/actionCard.styles.ts b/app/components/actionCard/actionCard.styles.ts index 8954a99..4451e56 100644 --- a/app/components/actionCard/actionCard.styles.ts +++ b/app/components/actionCard/actionCard.styles.ts @@ -5,20 +5,22 @@ export const getStyles = (colors: ThemeColors) => StyleSheet.create({ card: { backgroundColor: colors.card, - borderRadius: 12, + borderRadius: 18, padding: 16, - marginBottom: 12, - shadowColor: '#000', - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.05, - shadowRadius: 4, - elevation: 2, + marginBottom: 14, + borderWidth: 1, + borderColor: colors.border, + shadowColor: '#5B4CF5', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.06, + shadowRadius: 12, + elevation: 3, }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', - marginBottom: 12, + marginBottom: 14, }, leftSection: { flexDirection: 'row', @@ -26,19 +28,21 @@ export const getStyles = (colors: ThemeColors) => flex: 1, }, iconContainer: { - width: 40, - height: 40, - borderRadius: 20, + width: 42, + height: 42, + borderRadius: 12, backgroundColor: colors.surface, justifyContent: 'center', alignItems: 'center', marginRight: 12, + borderWidth: 1, + borderColor: colors.border, }, titleSection: { flex: 1, }, title: { - fontSize: 16, + fontSize: 15, fontWeight: '700', color: colors.text, }, @@ -49,31 +53,31 @@ export const getStyles = (colors: ThemeColors) => }, actionsRow: { flexDirection: 'row', - gap: 8, + gap: 10, }, - actionButton: { + actionBtn: { flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', + gap: 6, paddingVertical: 10, - paddingHorizontal: 16, - borderRadius: 8, + paddingHorizontal: 14, + borderRadius: 12, backgroundColor: colors.surface, borderWidth: 1, borderColor: colors.border, }, - addButton: { + addBtn: { backgroundColor: colors.icon, borderColor: colors.icon, }, - actionButtonText: { - fontSize: 14, + actionText: { + fontSize: 13, fontWeight: '600', color: colors.text, - marginLeft: 6, }, - addButtonText: { + addText: { color: '#FFFFFF', }, }); diff --git a/app/components/actionCard/actionCard.tsx b/app/components/actionCard/actionCard.tsx index 89b9a46..5f8f02e 100644 --- a/app/components/actionCard/actionCard.tsx +++ b/app/components/actionCard/actionCard.tsx @@ -17,11 +17,10 @@ export const ActionCard: React.FC = ({ return ( - {/* Header with Icon and Title */} - + {title} @@ -34,24 +33,21 @@ export const ActionCard: React.FC = ({ - {/* Action Buttons */} - - - Add - + activeOpacity={0.75}> + + Add - - View All + activeOpacity={0.75}> + + View All diff --git a/app/components/formInput/formInput.props.ts b/app/components/formInput/formInput.props.ts index 7068b9f..8266252 100644 --- a/app/components/formInput/formInput.props.ts +++ b/app/components/formInput/formInput.props.ts @@ -1,10 +1,15 @@ -import { TextInputProps } from 'react-native'; +import { TextInputProps, StyleProp, ViewStyle, TextStyle } from 'react-native'; export interface FormInputProps extends Omit { - label: string; + label?: string; value: string; onChangeText: (text: string) => void; placeholder?: string; required?: boolean; multiline?: boolean; + leftIcon?: React.ReactNode; + rightIcon?: React.ReactNode; + containerStyle?: StyleProp; + inputContainerStyle?: StyleProp; + inputStyle?: StyleProp; } diff --git a/app/components/formInput/formInput.styles.ts b/app/components/formInput/formInput.styles.ts index d0f78a9..7522372 100644 --- a/app/components/formInput/formInput.styles.ts +++ b/app/components/formInput/formInput.styles.ts @@ -7,21 +7,38 @@ export const getStyles = (colors: ThemeColors) => marginBottom: 16, }, label: { - fontSize: 14, + fontSize: 13, fontWeight: '600', - color: colors.text, + color: colors.textSecondary, marginBottom: 8, + textTransform: 'uppercase', + letterSpacing: 0.5, }, required: { color: '#EF4444', marginLeft: 2, }, - input: { + inputWrapper: { + flexDirection: 'row', + alignItems: 'center', backgroundColor: colors.surface, borderWidth: 1, borderColor: colors.border, - borderRadius: 10, - paddingHorizontal: 16, + borderRadius: 12, + paddingHorizontal: 12, + }, + leftIconWrapper: { + marginRight: 8, + justifyContent: 'center', + alignItems: 'center', + }, + rightIconWrapper: { + marginLeft: 8, + justifyContent: 'center', + alignItems: 'center', + }, + input: { + flex: 1, paddingVertical: 12, fontSize: 15, color: colors.text, diff --git a/app/components/formInput/formInput.tsx b/app/components/formInput/formInput.tsx index 100ba53..9d87ba8 100644 --- a/app/components/formInput/formInput.tsx +++ b/app/components/formInput/formInput.tsx @@ -12,28 +12,39 @@ export const FormInput: React.FC = ({ required = false, multiline = false, keyboardType = 'default', + leftIcon, + rightIcon, + containerStyle, + inputContainerStyle, + inputStyle, ...rest }) => { const { theme: colors } = useTheme(); const styles = getStyles(colors); return ( - - - {label} - {required && *} - - + + {label ? ( + + {label} + {required && *} + + ) : null} + + {leftIcon ? {leftIcon} : null} + + {rightIcon ? {rightIcon} : null} + ); }; diff --git a/app/components/index.ts b/app/components/index.ts index fa09fb6..ef4d718 100644 --- a/app/components/index.ts +++ b/app/components/index.ts @@ -8,3 +8,4 @@ export * from './formPicker'; export * from './addItemButton'; export * from './actionButton'; export * from './checkboxWithLabel'; +export * from './statCard'; diff --git a/app/components/leadDetailHeader/leadDetailHeader.styles.ts b/app/components/leadDetailHeader/leadDetailHeader.styles.ts index 7362bf7..eaf24e5 100644 --- a/app/components/leadDetailHeader/leadDetailHeader.styles.ts +++ b/app/components/leadDetailHeader/leadDetailHeader.styles.ts @@ -3,77 +3,98 @@ import { ThemeColors } from '../../theme'; export const getStyles = (colors: ThemeColors) => StyleSheet.create({ - container: { - alignItems: 'center', - paddingVertical: 24, - paddingHorizontal: 16, - borderBottomWidth: 1, - borderBottomColor: colors.border, + card: { backgroundColor: colors.card, - }, - avatar: { - width: 80, - height: 80, - borderRadius: 40, - justifyContent: 'center', + borderRadius: 20, + padding: 20, alignItems: 'center', marginBottom: 16, + borderWidth: 1, + borderColor: colors.border, + shadowColor: '#5B4CF5', + shadowOffset: { width: 0, height: 6 }, + shadowOpacity: 0.08, + shadowRadius: 16, + elevation: 4, + }, + avatar: { + width: 76, + height: 76, + borderRadius: 38, + justifyContent: 'center', + alignItems: 'center', + marginBottom: 14, }, avatarText: { - fontSize: 32, - fontWeight: '700', + fontSize: 26, + fontWeight: '800', color: '#FFFFFF', + letterSpacing: 0.5, }, - nameRow: { + name: { + fontSize: 20, + fontWeight: '700', + color: colors.text, + textAlign: 'center', + marginBottom: 4, + }, + companyRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', + gap: 6, marginBottom: 12, - flexWrap: 'wrap', - paddingHorizontal: 16, - }, - name: { - fontSize: 24, - fontWeight: '700', - color: colors.text, - marginRight: 8, - }, - statusBadge: { - paddingHorizontal: 12, - paddingVertical: 6, - borderRadius: 16, - }, - statusText: { - fontSize: 13, - fontWeight: '600', - textTransform: 'capitalize', }, company: { - fontSize: 16, + fontSize: 13, color: colors.textSecondary, - marginBottom: 12, - textAlign: 'center', + fontWeight: '500', }, - contactRow: { + statusBadge: { flexDirection: 'row', alignItems: 'center', - marginBottom: 8, - paddingHorizontal: 16, + gap: 6, + paddingHorizontal: 12, + paddingVertical: 5, + borderRadius: 20, + marginBottom: 16, }, - contactLabel: { - fontSize: 14, + statusDot: { + width: 6, + height: 6, + borderRadius: 3, + }, + statusText: { + fontSize: 11, + fontWeight: '700', + textTransform: 'uppercase', + letterSpacing: 0.5, + }, + quickActions: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + gap: 12, + width: '100%', + paddingTop: 14, + borderTopWidth: StyleSheet.hairlineWidth, + borderTopColor: colors.border, + }, + actionBtn: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + gap: 6, + paddingVertical: 10, + borderRadius: 12, + borderWidth: 1, + borderColor: colors.border, + backgroundColor: colors.surface, + }, + actionBtnText: { + fontSize: 12, fontWeight: '600', color: colors.text, - marginRight: 8, - minWidth: 50, - }, - contactText: { - fontSize: 14, - color: colors.textSecondary, - flex: 1, - }, - iconButton: { - marginLeft: 8, - padding: 4, }, }); diff --git a/app/components/leadDetailHeader/leadDetailHeader.tsx b/app/components/leadDetailHeader/leadDetailHeader.tsx index 099a513..67cb38b 100644 --- a/app/components/leadDetailHeader/leadDetailHeader.tsx +++ b/app/components/leadDetailHeader/leadDetailHeader.tsx @@ -4,6 +4,7 @@ import Icon from 'react-native-vector-icons/Ionicons'; import { useTheme } from '@theme'; import { getStyles } from './leadDetailHeader.styles'; import { LeadDetailHeaderProps } from './leadDetailHeader.props'; +import { getInitials, toRgba } from '@utils'; export const LeadDetailHeader: React.FC = ({ name, @@ -17,87 +18,62 @@ export const LeadDetailHeader: React.FC = ({ onEmailPress, onPhonePress, }) => { - const { theme: colors } = useTheme(); + const { theme: colors, isDark } = useTheme(); const styles = getStyles(colors); - // Get initials from name - const getInitials = (name: string): string => { - if (!name) return '?'; - const nameParts = name.trim().split(' '); - if (nameParts.length >= 2) { - return (nameParts[0][0] + nameParts[1][0]).toUpperCase(); - } - return name.substring(0, 2).toUpperCase(); - }; + const accent = statusColor || avatarColor || '#5B4CF5'; + const badgeBg = toRgba(accent, isDark ? 0.20 : 0.12); return ( - - {/* Avatar */} - + + {getInitials(name)} - {/* Name and Status Row */} - - {name || 'No Name'} - - - - {statusName || 'No Status'} - - - + + {name || 'No Name'} + + + {company && ( + + + {company} + + )} + + + + + + {statusName || 'No Status'} + + + + + + {email && ( + + + Email + + )} + + {phonenumber && ( + + + Call + + )} - - {/* Company */} - {company && {company}} - - {/* Email Row */} - {email && ( - - Email: - {email} - {onEmailPress && ( - - - - )} - - )} - - {/* Phone Row */} - {phonenumber && ( - - Phone: - {phonenumber} - {onPhonePress && ( - - - - )} - - )} ); }; diff --git a/app/components/leadItemCard/leadItemCard.styles.ts b/app/components/leadItemCard/leadItemCard.styles.ts index 14b9c69..f427deb 100644 --- a/app/components/leadItemCard/leadItemCard.styles.ts +++ b/app/components/leadItemCard/leadItemCard.styles.ts @@ -3,72 +3,174 @@ import { ThemeColors } from '../../theme'; export const getStyles = (colors: ThemeColors) => StyleSheet.create({ + cardWrapper: { + marginBottom: 16, + borderRadius: 20, + shadowColor: '#5B4CF5', + shadowOffset: { width: 0, height: 6 }, + shadowOpacity: 0.10, + shadowRadius: 18, + elevation: 6, + }, card: { backgroundColor: colors.card, - borderRadius: 12, - padding: 16, - marginBottom: 12, + borderRadius: 20, flexDirection: 'row', - alignItems: 'center', - shadowColor: '#000', - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.05, - shadowRadius: 4, - elevation: 2, + overflow: 'hidden', + borderWidth: 1, + borderColor: colors.border, }, - avatarContainer: { - width: 56, - height: 56, - borderRadius: 28, + leftStrip: { + width: 4, + borderTopLeftRadius: 20, + borderBottomLeftRadius: 20, + }, + cardInner: { + flex: 1, + padding: 15, + paddingLeft: 14, + }, + topSection: { + flexDirection: 'row', + alignItems: 'flex-start', + marginBottom: 12, + }, + avatar: { + width: 48, + height: 48, + borderRadius: 24, justifyContent: 'center', alignItems: 'center', marginRight: 12, }, avatarText: { - fontSize: 20, - fontWeight: '700', + fontSize: 16, + fontWeight: '800', color: '#FFFFFF', + letterSpacing: 0.5, }, - contentContainer: { + meta: { flex: 1, - }, - headerRow: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - marginBottom: 8, + minWidth: 0, }, name: { - fontSize: 16, + fontSize: 15, fontWeight: '700', color: colors.text, - flex: 1, - marginRight: 8, + marginBottom: 3, + letterSpacing: 0.05, + }, + companyRow: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: 8, + gap: 5, + flexWrap: 'wrap', + }, + companyText: { + fontSize: 12, + color: colors.textSecondary, + fontWeight: '500', + flexShrink: 1, + }, + titleText: { + fontSize: 11, + color: colors.textMuted, + flexShrink: 1, + }, + dot: { + width: 3, + height: 3, + borderRadius: 2, + backgroundColor: colors.textMuted, + opacity: 0.6, }, statusBadge: { + alignSelf: 'flex-start', + flexDirection: 'row', + alignItems: 'center', + gap: 5, paddingHorizontal: 10, paddingVertical: 4, - borderRadius: 12, - flexShrink: 0, + borderRadius: 20, + }, + statusDot: { + width: 5, + height: 5, + borderRadius: 3, }, statusText: { - fontSize: 11, - fontWeight: '600', - textTransform: 'capitalize', + fontSize: 10, + fontWeight: '700', + textTransform: 'uppercase', + letterSpacing: 0.7, + }, + separator: { + height: StyleSheet.hairlineWidth, + backgroundColor: colors.border, + marginVertical: 11, + }, + contacts: { + gap: 8, }, contactRow: { flexDirection: 'row', alignItems: 'center', - justifyContent: 'space-between', - marginBottom: 6, + gap: 9, + }, + iconWrap: { + width: 28, + height: 28, + borderRadius: 9, + justifyContent: 'center', + alignItems: 'center', }, contactText: { - fontSize: 13, + fontSize: 12, color: colors.textSecondary, flex: 1, - marginRight: 8, }, - contactIconButton: { - padding: 4, + actionBtn: { + width: 28, + height: 28, + borderRadius: 9, + justifyContent: 'center', + alignItems: 'center', + }, + footer: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + marginTop: 12, + paddingTop: 10, + borderTopWidth: StyleSheet.hairlineWidth, + borderTopColor: colors.border, + }, + sourcePill: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: colors.surface, + borderRadius: 20, + paddingHorizontal: 9, + paddingVertical: 4, + gap: 5, + borderWidth: 1, + borderColor: colors.border, + }, + sourceText: { + fontSize: 10, + color: colors.textMuted, + fontWeight: '600', + textTransform: 'uppercase', + letterSpacing: 0.5, + }, + dateRow: { + flexDirection: 'row', + alignItems: 'center', + gap: 4, + }, + dateText: { + fontSize: 10, + color: colors.textMuted, }, }); diff --git a/app/components/leadItemCard/leadItemCard.tsx b/app/components/leadItemCard/leadItemCard.tsx index 9f76af7..2bcb78f 100644 --- a/app/components/leadItemCard/leadItemCard.tsx +++ b/app/components/leadItemCard/leadItemCard.tsx @@ -1,83 +1,111 @@ import React from 'react'; -import { - View, - Text, - TouchableOpacity, -} from 'react-native'; +import { View, Text, TouchableOpacity } from 'react-native'; import Icon from 'react-native-vector-icons/Ionicons'; import { useTheme } from '@theme'; import { getStyles } from './leadItemCard.styles'; import { LeadItemCardProps } from './leadItemCard.props'; -import { getInitials, handleEmailPress, handlePhonePress } from '@utils'; +import { getInitials, handleEmailPress, handlePhonePress, toRgba, formatDate } from '@utils'; -export const LeadItemCard: React.FC = ({ - item, - onPress, -}) => { - const { theme: colors } = useTheme(); +export const LeadItemCard: React.FC = ({ item, onPress }) => { + const { theme: colors, isDark } = useTheme(); const styles = getStyles(colors); - return ( - - {/* Left side - Avatar/Initials */} - - {getInitials(item.name)} - + const accent = item.color?.startsWith('#') ? item.color : '#5B4CF5'; + const tint = (a: number) => toRgba(accent, isDark ? a + 0.08 : a); - {/* Right side - Content */} - - {/* Name and Status Row */} - - - {item.name || 'No Name'} - - - - {item.status_name || item.status || 'No Status'} - + return ( + + + + + + + + {getInitials(item.name)} + + + + + {item.name || 'No Name'} + + + {(item.company || item.title) && ( + + {item.company && ( + <> + + {item.company} + + )} + {item.company && item.title && } + {item.title && ( + {item.title} + )} + + )} + + + + + {item.status_name || item.status || 'No Status'} + + + + + + {(item.email || item.phonenumber) && ( + <> + + + {item.email && ( + + + + + {item.email} + handleEmailPress(item.email)} + hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}> + + + + )} + + {item.phonenumber && ( + + + + + {item.phonenumber} + handlePhonePress(item.phonenumber)} + hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}> + + + + )} + + + )} + + + {(item.source_name || item.source) ? ( + + + {item.source_name || item.source} + + ) : } + + {item.dateadded && ( + + + {formatDate(item.dateadded)} + + )} - - {/* Email Row */} - {item.email ? ( - - - {item.email} - - handleEmailPress(item.email)} - hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}> - - - - ) : null} - - {/* Phone Row */} - {item.phonenumber ? ( - - - {item.phonenumber} - - handlePhonePress(item.phonenumber)} - hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}> - - - - ) : null} ); diff --git a/app/components/searchInput/searchInput.styles.ts b/app/components/searchInput/searchInput.styles.ts index 4f3cffc..c97e8ea 100644 --- a/app/components/searchInput/searchInput.styles.ts +++ b/app/components/searchInput/searchInput.styles.ts @@ -9,18 +9,25 @@ export const getStyles = (colors: ThemeColors) => backgroundColor: colors.surface, borderWidth: 1, borderColor: colors.border, - borderRadius: 12, - paddingHorizontal: 12, + borderRadius: 14, + paddingHorizontal: 14, height: 48, + // Branded shadow — subtle indigo glow + shadowColor: '#5B4CF5', + shadowOffset: { width: 0, height: 3 }, + shadowOpacity: 0.07, + shadowRadius: 8, + elevation: 2, }, searchIcon: { - marginRight: 8, + marginRight: 9, }, input: { flex: 1, - fontSize: 15, + fontSize: 14, color: colors.text, paddingVertical: 0, + letterSpacing: 0.1, }, clearButton: { marginLeft: 8, diff --git a/app/components/statCard/index.ts b/app/components/statCard/index.ts new file mode 100644 index 0000000..23a9f95 --- /dev/null +++ b/app/components/statCard/index.ts @@ -0,0 +1,2 @@ +export * from './statCard'; +export * from './statCard.props'; diff --git a/app/components/statCard/statCard.props.ts b/app/components/statCard/statCard.props.ts new file mode 100644 index 0000000..807615a --- /dev/null +++ b/app/components/statCard/statCard.props.ts @@ -0,0 +1,7 @@ +export interface StatCardProps { + label: string; + count: number; + color: string; + isSelected?: boolean; + onPress?: () => void; +} diff --git a/app/components/statCard/statCard.styles.ts b/app/components/statCard/statCard.styles.ts new file mode 100644 index 0000000..281e77c --- /dev/null +++ b/app/components/statCard/statCard.styles.ts @@ -0,0 +1,60 @@ +import { StyleSheet } from 'react-native'; +import { ThemeColors } from '../../theme'; + +export const getStyles = (colors: ThemeColors) => + StyleSheet.create({ + card: { + minWidth: 105, + height: 84, + borderRadius: 16, + backgroundColor: colors.card, + borderWidth: 1.5, + borderColor: colors.border, + paddingHorizontal: 8, + paddingVertical: 10, + alignItems: 'center', + justifyContent: 'center', + shadowColor: '#5B4CF5', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.05, + shadowRadius: 6, + elevation: 2, + }, + cardSelected: { + borderWidth: 2, + shadowOpacity: 0.12, + shadowRadius: 8, + elevation: 3, + }, + pillTag: { + flexDirection: 'row', + alignItems: 'center', + paddingHorizontal: 9, + paddingVertical: 4, + borderRadius: 10, + gap: 5, + maxWidth: '100%', + }, + pillTagInactive: { + backgroundColor: colors.surface, + }, + statusDot: { + width: 6, + height: 6, + borderRadius: 3, + }, + labelText: { + fontSize: 11, + fontWeight: '700', + }, + countText: { + fontSize: 24, + fontWeight: '900', + color: colors.text, + textAlign: 'center', + marginTop: 6, + lineHeight: 28, + includeFontPadding: false, + letterSpacing: -0.5, + }, + }); diff --git a/app/components/statCard/statCard.tsx b/app/components/statCard/statCard.tsx new file mode 100644 index 0000000..0900e5c --- /dev/null +++ b/app/components/statCard/statCard.tsx @@ -0,0 +1,67 @@ +import React from 'react'; +import { View, Text, TouchableOpacity } from 'react-native'; +import { useTheme } from '../../theme'; +import { getStyles } from './statCard.styles'; +import { StatCardProps } from './statCard.props'; + +export const StatCard: React.FC = ({ + label, + count, + color, + isSelected = false, + onPress, +}) => { + const { theme: colors } = useTheme(); + const styles = getStyles(colors); + + const themeColor = color || colors.icon; + const borderColor = isSelected ? themeColor : colors.border; + + return ( + + {/* Top Status Pill Tag */} + + + + {label} + + + + {/* Large Metric Count */} + + {count} + + + ); +}; diff --git a/app/features/leadDetails/leadDetails.screen.tsx b/app/features/leadDetails/leadDetails.screen.tsx index 306aaca..b386c39 100644 --- a/app/features/leadDetails/leadDetails.screen.tsx +++ b/app/features/leadDetails/leadDetails.screen.tsx @@ -1,11 +1,6 @@ import React, { useEffect } from 'react'; -import { - View, - Text, - ScrollView, - Linking, - Platform, -} from 'react-native'; +import { View, Text, ScrollView, TouchableOpacity } from 'react-native'; +import Icon from 'react-native-vector-icons/Ionicons'; import { RouteProp, useRoute, useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { useTheme } from '@theme'; @@ -14,14 +9,10 @@ import { getLeadDetails } from './thunk'; import { getStyles } from './leadDetails.styles'; import { LeadsStackParamList } from '../../navigation/leadsStack'; import { Loader, LeadDetailHeader, ActionCard } from '@components'; -import { handleEmailPress, handlePhonePress } from '@utils'; -import { route as routes } from '@utils'; +import { handleEmailPress, handlePhonePress, handleWebsitePress, formatDate, route as routes } from '@utils'; type LeadDetailsRouteProp = RouteProp; -type LeadDetailsNavigationProp = NativeStackNavigationProp< - LeadsStackParamList, - 'leadDetails' ->; +type LeadDetailsNavigationProp = NativeStackNavigationProp; export const LeadDetailsScreen = () => { const route = useRoute(); @@ -30,35 +21,23 @@ export const LeadDetailsScreen = () => { const { theme: colors } = useTheme(); const styles = getStyles(colors); - // Get lead ID from route params const leadId = route.params?.lead?.id; - - // Get data from Redux store - const { item: lead, loading, error } = useAppSelector( - (state: RootState) => state.leadDetails, - ); + const { item: lead, loading, error } = useAppSelector((state: RootState) => state.leadDetails); const user_data = useAppSelector((state: RootState) => state.auth.user_data); - // Fetch lead details on mount useEffect(() => { if (leadId && user_data?.staffid) { dispatch(getLeadDetails({ leadId, staffid: user_data.staffid })); } }, [dispatch, leadId, user_data]); - // Show loader while loading - if (loading) { - return ; - } + if (loading) return ; - // Show error if any if (error) { return ( - - - {error} - + + {error} ); @@ -67,50 +46,19 @@ export const LeadDetailsScreen = () => { if (!lead) { return ( - No lead data available + + No lead data available + ); } - // Handle status press (for future status change functionality) - const handleStatusPress = () => { - // TODO: Show status list modal/picker - console.log('Status pressed - show status list'); - }; - - // Proposals handlers - const handleAddProposal = () => { - // TODO: Navigate to add proposal screen - navigation.navigate(routes.leadProposals, { leadId: lead.id }); - }; - - const handleViewAllProposals = () => { - console.log('view Proposal pressed'); - }; - - // Tasks handlers - const handleAddTask = () => { - // TODO: Navigate to add task screen - navigation.navigate(routes.leadTasks, { leadId: lead.id }); - }; - - const handleViewAllTasks = () => { - console.log('view Add Task pressed'); - }; - - // Notes handlers - const handleAddNote = () => { - // TODO: Navigate to add note screen - navigation.navigate(routes.leadNotes, { leadId: lead.id }); - }; - - const handleViewAllNotes = () => { - console.log('view Add Note pressed'); - }; + const addressString = [lead.address, lead.city, lead.state, lead.country] + .filter(Boolean) + .join(', '); return ( - {/* Header with Avatar */} { phonenumber={lead.phonenumber} statusName={lead.status_name || lead.status} statusColor={lead.color} - onStatusPress={handleStatusPress} onEmailPress={() => handleEmailPress(lead.email)} onPhonePress={() => handlePhonePress(lead.phonenumber)} /> - {/* Action Cards Section */} + + + Lead Value + + {lead.lead_value ? `$${lead.lead_value}` : 'N/A'} + + + + Source + + {lead.source_name || lead.source || 'Direct'} + + + + Created + + {formatDate(lead.dateadded) || 'N/A'} + + + + + Lead Information + + {lead.email ? ( + <> + handleEmailPress(lead.email)} + activeOpacity={0.7}> + + + + + Email + {lead.email} + + + + + ) : null} + + {lead.phonenumber ? ( + <> + handlePhonePress(lead.phonenumber)} + activeOpacity={0.7}> + + + + + Phone + {lead.phonenumber} + + + + + ) : null} + + {lead.website ? ( + <> + handleWebsitePress(lead.website)} + activeOpacity={0.7}> + + + + + Website + {lead.website} + + + + + ) : null} + + {addressString ? ( + <> + + + + + + Address + {addressString} + + + + + ) : null} + + {lead.assigned ? ( + + + + + + Assigned Staff + {lead.assigned} + + + ) : null} + + + Activity & Management navigation.navigate(routes.leadProposals, { leadId: lead.id })} + onViewAllPress={() => navigation.navigate(routes.leadProposals, { leadId: lead.id })} /> navigation.navigate(routes.leadTasks, { leadId: lead.id })} + onViewAllPress={() => navigation.navigate(routes.leadTasks, { leadId: lead.id })} /> navigation.navigate(routes.leadNotes, { leadId: lead.id })} + onViewAllPress={() => navigation.navigate(routes.leadNotes, { leadId: lead.id })} /> diff --git a/app/features/leadDetails/leadDetails.styles.ts b/app/features/leadDetails/leadDetails.styles.ts index 70ad542..745060f 100644 --- a/app/features/leadDetails/leadDetails.styles.ts +++ b/app/features/leadDetails/leadDetails.styles.ts @@ -9,114 +9,102 @@ export const getStyles = (colors: ThemeColors) => }, scrollContent: { padding: 16, - }, - cardsContainer: { - marginTop: 16, - }, - header: { - alignItems: 'center', - paddingVertical: 24, - borderBottomWidth: 1, - borderBottomColor: colors.border, - marginBottom: 16, - }, - avatar: { - width: 80, - height: 80, - borderRadius: 40, - justifyContent: 'center', - alignItems: 'center', - marginBottom: 12, - }, - avatarText: { - fontSize: 32, - fontWeight: '700', - color: '#FFFFFF', - }, - name: { - fontSize: 24, - fontWeight: '700', - color: colors.text, - marginBottom: 4, - }, - company: { - fontSize: 16, - color: colors.textSecondary, - marginBottom: 8, - }, - statusBadge: { - paddingHorizontal: 16, - paddingVertical: 6, - borderRadius: 16, - }, - statusText: { - fontSize: 13, - fontWeight: '600', - textTransform: 'capitalize', - }, - section: { - backgroundColor: colors.card, - borderRadius: 12, - padding: 16, - marginBottom: 16, - shadowColor: '#000', - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.05, - shadowRadius: 4, - elevation: 2, + paddingBottom: 32, }, sectionTitle: { - fontSize: 16, + fontSize: 15, fontWeight: '700', color: colors.text, marginBottom: 12, + letterSpacing: 0.1, + }, + statsRow: { + flexDirection: 'row', + gap: 10, + marginBottom: 16, + }, + statCard: { + flex: 1, + backgroundColor: colors.card, + borderRadius: 16, + padding: 14, + borderWidth: 1, + borderColor: colors.border, + shadowColor: '#5B4CF5', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.05, + shadowRadius: 10, + elevation: 2, + }, + statLabel: { + fontSize: 11, + color: colors.textMuted, + marginBottom: 4, + fontWeight: '500', + }, + statValue: { + fontSize: 15, + fontWeight: '700', + color: colors.text, + }, + infoCard: { + backgroundColor: colors.card, + borderRadius: 18, + padding: 16, + marginBottom: 16, + borderWidth: 1, + borderColor: colors.border, + shadowColor: '#5B4CF5', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.05, + shadowRadius: 10, + elevation: 2, }, infoRow: { flexDirection: 'row', alignItems: 'center', - marginBottom: 12, + paddingVertical: 9, + gap: 12, }, - infoIcon: { - width: 40, - height: 40, - borderRadius: 20, + infoIconWrap: { + width: 32, + height: 32, + borderRadius: 10, backgroundColor: colors.surface, justifyContent: 'center', alignItems: 'center', - marginRight: 12, + borderWidth: 1, + borderColor: colors.border, }, - infoContent: { + infoMeta: { flex: 1, }, infoLabel: { - fontSize: 12, + fontSize: 11, color: colors.textMuted, marginBottom: 2, }, infoValue: { - fontSize: 14, - color: colors.text, - fontWeight: '500', - }, - actionButton: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - backgroundColor: colors.icon, - paddingVertical: 14, - paddingHorizontal: 20, - borderRadius: 12, - marginBottom: 12, - }, - actionButtonText: { - fontSize: 15, + fontSize: 13, fontWeight: '600', - color: '#FFFFFF', - marginLeft: 8, + color: colors.text, }, - noteText: { + divider: { + height: StyleSheet.hairlineWidth, + backgroundColor: colors.border, + }, + cardsContainer: { + marginTop: 4, + }, + errorContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + padding: 32, + }, + errorText: { + color: '#EF4444', fontSize: 14, - color: colors.textSecondary, - lineHeight: 20, + textAlign: 'center', }, }); diff --git a/app/features/leads/leads.screen.tsx b/app/features/leads/leads.screen.tsx index d6ef546..3dab9a3 100644 --- a/app/features/leads/leads.screen.tsx +++ b/app/features/leads/leads.screen.tsx @@ -3,7 +3,9 @@ import { Text, View, FlatList, + ScrollView, TouchableOpacity, + ActivityIndicator, } from 'react-native'; import { useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; @@ -11,9 +13,9 @@ import Icon from 'react-native-vector-icons/Ionicons'; import { getStyles } from './leads.styles'; import { useTheme } from '@theme'; import { useAppDispatch, useAppSelector, RootState } from '@store'; -import { getLeads } from './thunk'; +import { getLeads, getLeadCountList } from './thunk'; import { LeadItem } from '@interfaces'; -import { LeadItemCard, SearchInput, Loader } from '@components'; +import { LeadItemCard, SearchInput, Loader, StatCard } from '@components'; import { LeadsStackParamList } from '../../navigation/leadsStack'; import { route } from '@utils'; @@ -28,8 +30,9 @@ export const LeadsScreen = () => { const { theme: colors } = useTheme(); const styles = getStyles(colors); const [searchTerm, setSearchTerm] = useState(''); + const [selectedStatusId, setSelectedStatusId] = useState(null); - const { items, loading, error } = useAppSelector( + const { items, loading, error, counts, countsLoading } = useAppSelector( (state: RootState) => state.leads, ); const user_data = useAppSelector((state: RootState) => state.auth.user_data); @@ -37,20 +40,30 @@ export const LeadsScreen = () => { useEffect(() => { if (user_data?.staffid) { dispatch(getLeads({ leadId: null, staffid: user_data.staffid })); + dispatch(getLeadCountList(user_data.staffid)); } }, [dispatch, user_data]); - const filteredLeads = searchTerm - ? items.filter( - lead => - lead.name.toLowerCase().includes(searchTerm.toLowerCase()) || - lead.company.toLowerCase().includes(searchTerm.toLowerCase()) || - lead.email.toLowerCase().includes(searchTerm.toLowerCase()), - ) - : items; + const filteredLeads = items.filter(lead => { + const matchesSearch = searchTerm + ? lead.name.toLowerCase().includes(searchTerm.toLowerCase()) || + lead.company.toLowerCase().includes(searchTerm.toLowerCase()) || + lead.email.toLowerCase().includes(searchTerm.toLowerCase()) + : true; + + const matchesStatus = + !selectedStatusId || selectedStatusId === '0' + ? true + : lead.status === selectedStatusId; + + return matchesSearch && matchesStatus; + }); + + const handleStatusPress = (id: string) => { + setSelectedStatusId(prev => (prev === id ? null : id)); + }; const handleLeadPress = (lead: LeadItem) => { - // Navigate to lead details screen navigation.navigate(route.leadDetails, { lead }); }; @@ -60,6 +73,7 @@ export const LeadsScreen = () => { return ( + {/* Search + Filter Header (Fixed at top) */} { + {/* Status count cards */} + {countsLoading ? ( + + + + ) : counts.length > 0 ? ( + + {counts.map(item => ( + handleStatusPress(item.id)} + /> + ))} + + ) : null} + + {/* Lead list */} {loading ? ( ) : error ? ( @@ -82,11 +122,20 @@ export const LeadsScreen = () => { item.id} + style={{ flex: 1 }} contentContainerStyle={styles.listContent} renderItem={renderLead} ListEmptyComponent={() => ( - No leads available. + + + + No Leads Found + + {searchTerm + ? `No leads match "${searchTerm}". Try a different search.` + : 'You have no leads yet. New leads will appear here.'} + )} /> diff --git a/app/features/leads/leads.styles.ts b/app/features/leads/leads.styles.ts index faef590..b425082 100644 --- a/app/features/leads/leads.styles.ts +++ b/app/features/leads/leads.styles.ts @@ -7,9 +7,22 @@ export const getStyles = (colors: ThemeColors) => flex: 1, backgroundColor: colors.background, }, + countsLoading: { + height: 82, + justifyContent: 'center', + alignItems: 'center', + }, + countsScroll: { + paddingHorizontal: 16, + paddingTop: 4, + paddingBottom: 10, + gap: 10, + }, header: { flexDirection: 'row', - padding: 16, + paddingHorizontal: 16, + paddingTop: 12, + paddingBottom: 10, alignItems: 'center', gap: 10, }, @@ -22,13 +35,20 @@ export const getStyles = (colors: ThemeColors) => backgroundColor: colors.surface, borderWidth: 1, borderColor: colors.border, - borderRadius: 12, + borderRadius: 14, justifyContent: 'center', alignItems: 'center', + // Branded indigo shadow + shadowColor: '#5B4CF5', + shadowOffset: { width: 0, height: 3 }, + shadowOpacity: 0.12, + shadowRadius: 6, + elevation: 3, }, listContent: { - padding: 16, - paddingTop: 0, + paddingHorizontal: 16, + paddingTop: 6, + paddingBottom: 40, }, errorContainer: { flex: 1, @@ -42,14 +62,40 @@ export const getStyles = (colors: ThemeColors) => textAlign: 'center', }, emptyContainer: { - flex: 1, + alignItems: 'center', + paddingTop: 80, + padding: 32, + gap: 10, + }, + emptyIconWrap: { + width: 76, + height: 76, + borderRadius: 38, + backgroundColor: colors.surface, justifyContent: 'center', alignItems: 'center', - padding: 32, + marginBottom: 10, + borderWidth: 1, + borderColor: colors.border, + // Subtle shadow + shadowColor: '#5B4CF5', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.10, + shadowRadius: 10, + elevation: 3, + }, + emptyTitle: { + color: colors.text, + fontSize: 17, + fontWeight: '700', + textAlign: 'center', + letterSpacing: 0.1, }, emptyText: { color: colors.textSecondary, - fontSize: 14, + fontSize: 13, textAlign: 'center', + lineHeight: 20, + maxWidth: 280, }, }); diff --git a/app/features/leads/reducers.ts b/app/features/leads/reducers.ts index 4d84599..7372e1d 100644 --- a/app/features/leads/reducers.ts +++ b/app/features/leads/reducers.ts @@ -1,17 +1,23 @@ import { createReducer } from '@reduxjs/toolkit'; -import { getLeads } from './thunk'; -import { LeadItem } from '@interfaces'; +import { getLeads, getLeadCountList } from './thunk'; +import { LeadItem, LeadCountItem } from '@interfaces'; export interface LeadsState { items: LeadItem[]; loading: boolean; error: string | null; + counts: LeadCountItem[]; + countsLoading: boolean; + countsError: string | null; } const initialState: LeadsState = { items: [], loading: false, error: null, + counts: [], + countsLoading: false, + countsError: null, }; export const reducers = createReducer(initialState, builder => { @@ -31,6 +37,22 @@ export const reducers = createReducer(initialState, builder => { (action.payload as string) ?? action.error.message ?? 'Failed to load leads'; + }) + .addCase(getLeadCountList.pending, acc => { + acc.countsLoading = true; + acc.countsError = null; + }) + .addCase(getLeadCountList.fulfilled, (acc, action) => { + acc.countsLoading = false; + acc.counts = action.payload; + acc.countsError = null; + }) + .addCase(getLeadCountList.rejected, (acc, action) => { + acc.countsLoading = false; + acc.countsError = + (action.payload as string) ?? + action.error.message ?? + 'Failed to load lead counts'; }); }); diff --git a/app/features/leads/thunk.ts b/app/features/leads/thunk.ts index ddf9711..bb019e0 100644 --- a/app/features/leads/thunk.ts +++ b/app/features/leads/thunk.ts @@ -1,6 +1,6 @@ import { createAsyncThunk } from '@reduxjs/toolkit'; -import { getLeadsApi } from '@api'; -import { LeadItem } from '@interfaces'; +import { getLeadsApi, getLeadCountListApi } from '@api'; +import { LeadItem, LeadCountItem } from '@interfaces'; export const getLeads = createAsyncThunk< LeadItem[], @@ -12,3 +12,14 @@ export const getLeads = createAsyncThunk< return rejectWithValue(error.message || 'Failed to load leads'); } }); + +export const getLeadCountList = createAsyncThunk< + LeadCountItem[], + string +>('leads/getLeadCountList', async (staffid, { rejectWithValue }) => { + try { + return await getLeadCountListApi(staffid); + } catch (error: any) { + return rejectWithValue(error.message || 'Failed to load lead counts'); + } +}); diff --git a/app/features/login/login.screen.tsx b/app/features/login/login.screen.tsx index 5e57691..1d05db0 100644 --- a/app/features/login/login.screen.tsx +++ b/app/features/login/login.screen.tsx @@ -2,29 +2,37 @@ import React, { useState, useEffect } from 'react'; import { Text, View, - TextInput, TouchableOpacity, KeyboardAvoidingView, Platform, ScrollView, + ImageBackground, + ActivityIndicator, + StatusBar, } from 'react-native'; import { useNavigation } from '@react-navigation/native'; import Icon from 'react-native-vector-icons/Ionicons'; import { getStyles } from './login.styles'; -import { useTheme } from '../../theme'; -import { useAppDispatch, useAppSelector } from '../../store/store'; -import { login } from '../../store/commonReducers/auth/thunk'; +import { useTheme } from '@theme'; +import { useAppDispatch, useAppSelector, RootState, login } from '@store'; import { LoginRequest } from '@interfaces'; -import { RootState } from '../../store/rootReducer'; +import { NotificationService } from '@services'; +import { backGroundImage } from '@utils'; +import { FormInput } from '@components'; +import config from 'react-native-config'; +import AsyncStorage from '@react-native-async-storage/async-storage'; export const LoginScreen = () => { const dispatch = useAppDispatch(); const navigation = useNavigation(); - const { theme: colors } = useTheme(); - const styles = getStyles(colors); + const { theme: colors, isDark } = useTheme(); + const styles = getStyles(colors, isDark); + const [tenancy, setTenancy] = useState(''); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); + const [showPassword, setShowPassword] = useState(false); + const [focusedField, setFocusedField] = useState(null); const [localError, setLocalError] = useState(''); const { loginLoading, loginError, loginSuccess, token, user_data } = @@ -40,20 +48,69 @@ export const LoginScreen = () => { setLocalError('Email address is required'); return; } + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(email.trim())) { + setLocalError('Please enter a valid email address'); + return; + } if (!password.trim()) { setLocalError('Password is required'); return; } + const deviceToken = await NotificationService.getFCMToken(); + console.log('deviceToken-', deviceToken) + const payload: LoginRequest = { email, password, - device_token: '', + device_token: deviceToken ?? '', }; - - await dispatch(login(payload)); + console.log('payload-', payload) + console.log('tenancy-', config.BASE_URL) + if (tenancy == config.BASE_URL) { + try { + await AsyncStorage.setItem('base_url', tenancy); + await AsyncStorage.setItem('email', email); + await AsyncStorage.setItem('password', password); + } catch (e) { + console.error('Failed to save login credentials to AsyncStorage', e); + } + await dispatch(login(payload)); + } else { + setLocalError('Invalid tenancy name'); + } }; + useEffect(() => { + const loadCredentials = async () => { + try { + const savedBaseUrl = await AsyncStorage.getItem('base_url'); + const savedEmail = await AsyncStorage.getItem('email'); + const savedPassword = await AsyncStorage.getItem('password'); + + if (savedBaseUrl) setTenancy(savedBaseUrl); + if (savedEmail) setEmail(savedEmail); + if (savedPassword) setPassword(savedPassword); + } catch (e) { + console.error('Failed to load login credentials from AsyncStorage', e); + } + }; + loadCredentials(); + }, []); + + useEffect(() => { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if ( + tenancy.trim() && + email.trim() && + emailRegex.test(email.trim()) && + password.trim() + ) { + setLocalError(''); + } + }, [tenancy, email, password]); + useEffect(() => { if (loginSuccess && token && user_data) { navigation.reset({ @@ -64,117 +121,164 @@ export const LoginScreen = () => { }, [loginSuccess, token, user_data, navigation]); return ( - - - - - - - Convex CRM - - Enter details to access your workspace - - + + + + + {/* Top Area Spacer displaying background illustration */} + - - {localError || loginError ? ( - - - {localError || loginError} - - ) : null} + {/* Bottom Sheet Form Card */} + + - {/* Tenancy Field */} - - Tenancy Name - - - + Sign In + + Enter your credentials to continue + + + + {localError || loginError ? ( + + + {localError || loginError} + + ) : null} + + {/* Tenancy Field */} + setFocusedField('tenancy')} + onBlur={() => setFocusedField(null)} + leftIcon={ + + } /> - - - {/* Email Field */} - - Email Address - - - setFocusedField('email')} + onBlur={() => setFocusedField(null)} + leftIcon={ + + } /> - - - {/* Password Field */} - - Password - - - setFocusedField('password')} + onBlur={() => setFocusedField(null)} + leftIcon={ + + } + rightIcon={ + setShowPassword(!showPassword)} + style={styles.eyeIconButton} + activeOpacity={0.7} + > + + + } /> + + {/* Sign In Button */} + + {loginLoading ? ( + + ) : ( + Sign In + )} + - - - {/* Sign In Button */} - - - {loginLoading ? 'Signing In...' : 'Sign In'} - - - - - - Secure SSL encrypted workspace - - - + + + + ); }; diff --git a/app/features/login/login.styles.ts b/app/features/login/login.styles.ts index 7490beb..f512eb9 100644 --- a/app/features/login/login.styles.ts +++ b/app/features/login/login.styles.ts @@ -1,128 +1,129 @@ -import { StyleSheet } from 'react-native'; +import { StyleSheet, Platform } from 'react-native'; import { ThemeColors } from '../../theme'; -export const getStyles = (colors: ThemeColors) => +export const getStyles = (colors: ThemeColors, isDark: boolean) => StyleSheet.create({ container: { flex: 1, - backgroundColor: colors.background, + }, + backgroundImage: { + flex: 1, + width: '100%', + height: '100%', + }, + overlay: { + flex: 1, + backgroundColor: isDark + ? 'rgba(10, 12, 26, 0.75)' + : 'rgba(235, 242, 250, 0.35)', }, scrollContainer: { flexGrow: 1, - justifyContent: 'center', - padding: 24, + justifyContent: 'space-between', }, - headerContainer: { - alignItems: 'center', - marginBottom: 32, + topSpacer: { + flex: 1, + minHeight: 120, }, - logoCircle: { - width: 80, - height: 80, - borderRadius: 40, - backgroundColor: colors.border, - justifyContent: 'center', - alignItems: 'center', + bottomSheetCard: { + backgroundColor: isDark + ? 'rgba(26, 29, 58, 0.96)' + : 'rgba(255, 255, 255, 0.98)', + borderTopLeftRadius: 32, + borderTopRightRadius: 32, + borderBottomLeftRadius: 0, + borderBottomRightRadius: 0, + paddingHorizontal: 24, + paddingTop: 14, + paddingBottom: Platform.OS === 'ios' ? 36 : 24, + borderWidth: 1, + borderBottomWidth: 0, + borderColor: isDark + ? 'rgba(255, 255, 255, 0.1)' + : 'rgba(228, 232, 245, 0.9)', + shadowColor: '#000', + shadowOffset: { width: 0, height: -8 }, + shadowOpacity: isDark ? 0.4 : 0.12, + shadowRadius: 20, + elevation: 16, + }, + sheetHandle: { + width: 36, + height: 4, + borderRadius: 2, + backgroundColor: isDark ? 'rgba(255, 255, 255, 0.2)' : '#CBD5E1', + alignSelf: 'center', marginBottom: 16, - shadowColor: colors.icon, - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.3, - shadowRadius: 10, - elevation: 8, }, - title: { - fontSize: 28, + cardHeader: { + marginBottom: 18, + }, + welcomeTitle: { + fontSize: 22, fontWeight: '800', color: colors.text, - letterSpacing: 0.5, + letterSpacing: 0.2, }, - subtitle: { - fontSize: 14, + welcomeSubtitle: { + fontSize: 13, color: colors.textSecondary, - marginTop: 8, - textAlign: 'center', - }, - formCard: { - backgroundColor: colors.card, - borderRadius: 16, - padding: 24, - shadowColor: '#000', - shadowOffset: { width: 0, height: 10 }, - shadowOpacity: 0.25, - shadowRadius: 15, - elevation: 10, + marginTop: 4, + fontWeight: '400', }, errorBanner: { flexDirection: 'row', alignItems: 'center', - backgroundColor: '#451A1A', - borderRadius: 8, + backgroundColor: isDark ? 'rgba(239, 68, 68, 0.15)' : '#FEF2F2', + borderRadius: 12, padding: 12, marginBottom: 16, borderWidth: 1, - borderColor: '#7F1D1D', + borderColor: isDark ? 'rgba(239, 68, 68, 0.3)' : '#FCA5A5', }, errorText: { - color: '#FCA5A5', + color: '#EF4444', fontSize: 13, - marginLeft: 8, + fontWeight: '500', + marginLeft: 10, flex: 1, }, - inputContainer: { - marginBottom: 20, - }, - label: { - fontSize: 13, - fontWeight: '600', - color: colors.textSecondary, - marginBottom: 8, - textTransform: 'uppercase', - letterSpacing: 0.5, + fieldContainer: { + marginBottom: 14, }, inputWrapper: { - flexDirection: 'row', - alignItems: 'center', - backgroundColor: colors.background, - borderRadius: 10, - borderWidth: 1, - borderBottomColor: colors.border, + backgroundColor: isDark ? 'rgba(15, 16, 34, 0.6)' : '#F8FAFC', + borderRadius: 14, + borderWidth: 1.5, borderColor: colors.border, - paddingHorizontal: 12, + height: 50, }, - inputIcon: { - marginRight: 10, + inputWrapperFocused: { + borderColor: colors.icon, + backgroundColor: isDark ? 'rgba(15, 16, 34, 0.85)' : '#FFFFFF', }, - input: { - flex: 1, - height: 48, - color: colors.text, - fontSize: 15, + eyeIconButton: { + padding: 6, }, button: { backgroundColor: colors.icon, - borderRadius: 10, - height: 50, + borderRadius: 14, + height: 52, justifyContent: 'center', alignItems: 'center', - marginTop: 8, + marginTop: 10, shadowColor: colors.icon, - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.4, - shadowRadius: 6, - elevation: 5, + shadowOffset: { width: 0, height: 6 }, + shadowOpacity: 0.35, + shadowRadius: 12, + elevation: 6, + }, + buttonDisabled: { + opacity: 0.7, }, buttonText: { color: '#FFFFFF', fontSize: 16, fontWeight: '700', - letterSpacing: 0.5, - }, - footer: { - alignItems: 'center', - marginTop: 32, - }, - footerText: { - fontSize: 12, - color: colors.textMuted, + letterSpacing: 0.4, }, }); diff --git a/app/features/profile/profile.screen.tsx b/app/features/profile/profile.screen.tsx index 32bb906..ee063fa 100644 --- a/app/features/profile/profile.screen.tsx +++ b/app/features/profile/profile.screen.tsx @@ -1,20 +1,21 @@ import React from 'react'; import { Text, View, TouchableOpacity, ScrollView, Switch } from 'react-native'; -import AsyncStorage from '@react-native-async-storage/async-storage'; import { useNavigation } from '@react-navigation/native'; import Icon from 'react-native-vector-icons/Ionicons'; import { getStyles } from './profile.styles'; import { useTheme } from '../../theme'; +import { useAppDispatch, logout } from '@store'; + export const ProfileScreen = () => { const navigation = useNavigation(); const { theme: colors, isDark, toggleTheme } = useTheme(); const styles = getStyles(colors); + const dispatch = useAppDispatch(); const handleLogout = async () => { try { - await AsyncStorage.removeItem('userToken'); - await AsyncStorage.removeItem('tenancyName'); + dispatch(logout()); } catch (e) { console.error(e); } diff --git a/app/interfaces/index.ts b/app/interfaces/index.ts index c6ee8be..9392ca8 100644 --- a/app/interfaces/index.ts +++ b/app/interfaces/index.ts @@ -2,3 +2,4 @@ export * from './drawerItem'; export * from './auth'; export * from './leads'; export * from './leadDetails'; +export * from './list'; diff --git a/app/interfaces/leads.ts b/app/interfaces/leads.ts index 410141f..e5dfaae 100644 --- a/app/interfaces/leads.ts +++ b/app/interfaces/leads.ts @@ -46,3 +46,12 @@ export interface LeadItem { status_name: string; source_name: string; } + +export interface LeadCountItem { + id: string; + name: string; + statusorder: string | number; + color: string; + isdefault: string | number; + count: number; +} diff --git a/app/interfaces/list.ts b/app/interfaces/list.ts new file mode 100644 index 0000000..d2c0759 --- /dev/null +++ b/app/interfaces/list.ts @@ -0,0 +1,25 @@ +export interface StatusListItem { + id: string; + name: string; + statusorder: string; + color: string; + isdefault: string; +} + +export interface SourceListItem { + id: string; + name: string; +} + +export interface CountryListItem { + country_id: string; + iso2: string; + short_name: string; + long_name: string; + iso3: string; + numcode: string; + un_member: string; + calling_code: string; + cctld: string; +} + diff --git a/app/navigation/customDrawerContent.tsx b/app/navigation/customDrawerContent.tsx index 16505b3..90cc851 100644 --- a/app/navigation/customDrawerContent.tsx +++ b/app/navigation/customDrawerContent.tsx @@ -1,13 +1,14 @@ import React from 'react'; import { Text, View, TouchableOpacity, ScrollView } from 'react-native'; import { DrawerContentComponentProps } from '@react-navigation/drawer'; -import AsyncStorage from '@react-native-async-storage/async-storage'; import Icon from 'react-native-vector-icons/Ionicons'; import { menuItems } from '@mock-data'; import { useTheme } from '@theme'; import { DrawerItemProps } from '@interfaces'; import { getStyles } from './customDrawerContent.style'; +import { useAppDispatch, logout } from '@store'; + const DrawerItem = ({ label, iconName, focused, onPress }: DrawerItemProps) => { const { theme: colors } = useTheme(); const styles = getStyles(colors); @@ -35,11 +36,11 @@ export const CustomDrawerContent = (props: DrawerContentComponentProps) => { const { state, navigation } = props; const { theme: colors } = useTheme(); const styles = getStyles(colors); + const dispatch = useAppDispatch(); const handleLogout = async () => { try { - await AsyncStorage.removeItem('userToken'); - await AsyncStorage.removeItem('tenancyName'); + dispatch(logout()); } catch (e) { console.error(e); } diff --git a/app/navigation/drawerStack.tsx b/app/navigation/drawerStack.tsx index f84729c..134ef5c 100644 --- a/app/navigation/drawerStack.tsx +++ b/app/navigation/drawerStack.tsx @@ -29,6 +29,7 @@ const Drawer = createDrawerNavigator(); export const DrawerStack = () => { const { theme: colors } = useTheme(); + return ( { swipeEnabled: false, headerStyle: { backgroundColor: colors.header, - elevation: 0, - shadowOpacity: 0, }, headerTitleStyle: { - fontSize: 16, + fontSize: 17, fontWeight: '700', color: colors.text, + letterSpacing: 0.2, }, headerTitleAlign: 'center', headerTintColor: colors.text, - }} - > - {/* Tab Navigator has its own headers per tab, so hide Drawer header for home */} + headerShadowVisible: false, + }}> { backgroundColor: colors.header, }, headerTitleStyle: { - fontSize: 16, + fontSize: 17, fontWeight: '700', color: colors.text, }, headerTitleAlign: 'center', headerTintColor: colors.text, + headerShadowVisible: false, }}> (); export const RootNavigator = () => { const { token } = useAppSelector((state: RootState) => state.auth); - console.log('RootNavigator token:', token); // Debugging line to check the token value const initialRouteName = token ? 'DrawerStack' : 'AuthStack'; return ( diff --git a/app/navigation/tabStack.styles.ts b/app/navigation/tabStack.styles.ts index 14ee377..7fda12f 100644 --- a/app/navigation/tabStack.styles.ts +++ b/app/navigation/tabStack.styles.ts @@ -1,4 +1,4 @@ -import { StyleSheet } from 'react-native'; +import { StyleSheet, Platform } from 'react-native'; import { ThemeColors } from '@theme'; export const getStyles = (colors: ThemeColors) => @@ -7,30 +7,45 @@ export const getStyles = (colors: ThemeColors) => backgroundColor: colors.tabBar, borderTopWidth: 1, borderTopColor: colors.border, - height: 60, - paddingBottom: 8, - paddingTop: 8, + height: Platform.OS === 'ios' ? 84 : 64, + paddingBottom: Platform.OS === 'ios' ? 24 : 10, + // paddingTop: 8, + shadowColor: '#5B4CF5', + shadowOffset: { width: 0, height: -4 }, + shadowOpacity: 0.06, + shadowRadius: 12, + elevation: 8, }, tabBarLabel: { - fontSize: 11, + fontSize: 12, fontWeight: '600', + letterSpacing: 0.2, }, header: { backgroundColor: colors.header, borderBottomWidth: 1, borderBottomColor: colors.border, - elevation: 0, - shadowOpacity: 0, + shadowColor: '#5B4CF5', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.04, + shadowRadius: 6, + elevation: 2, }, headerTitle: { fontSize: 17, - fontWeight: '800', + fontWeight: '700', color: colors.text, + letterSpacing: 0.2, }, drawerButton: { - paddingHorizontal: 16, - height: '100%', + width: 38, + height: 38, + borderRadius: 12, justifyContent: 'center', alignItems: 'center', + marginLeft: 14, + backgroundColor: colors.surface, + borderWidth: 1, + borderColor: colors.border, }, }); diff --git a/app/navigation/tabStack.tsx b/app/navigation/tabStack.tsx index dc623a9..a8f3ec2 100644 --- a/app/navigation/tabStack.tsx +++ b/app/navigation/tabStack.tsx @@ -37,11 +37,11 @@ export const TabStack = () => { } else if (tabRoute.name === route.addLead) { iconName = focused ? 'add-circle' : 'add-circle-outline'; } else if (tabRoute.name === route.customers) { - iconName = focused ? 'business' : 'business-outline'; + iconName = focused ? 'people' : 'people-outline'; } else if (tabRoute.name === route.profile) { iconName = focused ? 'person' : 'person-outline'; } - return ; + return ; }, tabBarActiveTintColor: colors.icon, tabBarInactiveTintColor: colors.textMuted, @@ -50,28 +50,26 @@ export const TabStack = () => { headerStyle: styles.header, headerTitleStyle: styles.headerTitle, headerTitleAlign: 'center', - })} - > + })}> ({ + tabBarLabel: 'Dashboard', headerTitle: 'Convex CRM', headerLeft: () => ( { - const parentNav = - navigation.getParent>(); + const parentNav = navigation.getParent>(); if (parentNav) { parentNav.openDrawer(); } else { (navigation as any).openDrawer?.(); } - }} - > - + }}> + ), })} @@ -79,11 +77,35 @@ export const TabStack = () => { + + + - - - ); }; diff --git a/app/services/index.ts b/app/services/index.ts new file mode 100644 index 0000000..9ea5ce7 --- /dev/null +++ b/app/services/index.ts @@ -0,0 +1 @@ +export * from './notifications'; diff --git a/app/services/notifications.ts b/app/services/notifications.ts new file mode 100644 index 0000000..fd19f65 --- /dev/null +++ b/app/services/notifications.ts @@ -0,0 +1,310 @@ +import messaging, { + FirebaseMessagingTypes, +} from '@react-native-firebase/messaging'; +import notifee, { + AndroidImportance, + AndroidVisibility, + AuthorizationStatus, + EventType, +} from '@notifee/react-native'; +import { Platform } from 'react-native'; + +const CHANNEL_ID = 'convex_crm_default'; +const CHANNEL_NAME = 'Convex CRM Notifications'; +const CHANNEL_DESCRIPTION = 'General push notifications for Convex CRM'; + +export interface NotificationData { + [key: string]: string; +} + +export type MessageHandler = ( + message: FirebaseMessagingTypes.RemoteMessage, +) => void; + +export type NotificationOpenHandler = (data: NotificationData) => void; + +export async function requestPermission(): Promise { + try { + const settings = await notifee.requestPermission(); + const granted = + settings.authorizationStatus === AuthorizationStatus.AUTHORIZED || + settings.authorizationStatus === AuthorizationStatus.PROVISIONAL; + console.log('[NotificationService] Permission granted:', granted); + return granted; + } catch (error) { + console.error('[NotificationService] requestPermission error:', error); + return false; + } +} + +export async function createDefaultChannel(): Promise { + if (Platform.OS !== 'android') { + return; + } + try { + await notifee.createChannel({ + id: CHANNEL_ID, + name: CHANNEL_NAME, + description: CHANNEL_DESCRIPTION, + importance: AndroidImportance.HIGH, + visibility: AndroidVisibility.PUBLIC, + sound: 'default', + vibration: true, + vibrationPattern: [300, 500], + lights: true, + lightColor: '#4F46E5', // indigo accent + }); + console.log('[NotificationService] Default Android channel created:', CHANNEL_ID); + } catch (error) { + console.error('[NotificationService] createDefaultChannel error:', error); + } +} + +export async function getFCMToken(): Promise { + try { + // Ensure FCM is registered to receive messages + if (!messaging().isDeviceRegisteredForRemoteMessages) { + await messaging().registerDeviceForRemoteMessages(); + } + const token = await messaging().getToken(); + console.log('[NotificationService] FCM Token:', token); + return token; + } catch (error) { + console.error('[NotificationService] getFCMToken error:', error); + return null; + } +} + +/** + * Subscribe to token-refresh events. + * Call this once; the returned function unsubscribes when invoked. + */ +export function onTokenRefresh( + callback: (token: string) => void, +): () => void { + const unsubscribe = messaging().onTokenRefresh(newToken => { + console.log('[NotificationService] FCM token refreshed:', newToken); + callback(newToken); + }); + return unsubscribe; +} + +export async function displayNotification( + title: string, + body: string, + data: NotificationData = {}, +): Promise { + try { + await notifee.displayNotification({ + title, + body, + data, + android: { + channelId: CHANNEL_ID, + importance: AndroidImportance.HIGH, + smallIcon: 'ic_notification', // must exist in android/app/src/main/res/drawable + pressAction: { id: 'default' }, + color: '#4F46E5', + }, + ios: { + sound: 'default', + foregroundPresentationOptions: { + alert: true, + badge: true, + sound: true, + }, + }, + }); + } catch (error) { + console.error('[NotificationService] displayNotification error:', error); + } +} + +export function onForegroundMessage( + onOpen?: NotificationOpenHandler, +): () => void { + // FCM foreground listener + const unsubscribeFCM = messaging().onMessage( + async (remoteMessage: FirebaseMessagingTypes.RemoteMessage) => { + console.log('[NotificationService] Foreground FCM message:', remoteMessage); + + const title = + remoteMessage.notification?.title ?? 'Convex CRM'; + const body = + remoteMessage.notification?.body ?? 'You have a new notification'; + const data = (remoteMessage.data ?? {}) as NotificationData; + + await displayNotification(title, body, data); + }, + ); + + // Notifee foreground event listener (handles taps on Notifee notifications) + const unsubscribeNotifee = notifee.onForegroundEvent(({ type, detail }) => { + if (type === EventType.PRESS && detail.notification?.data) { + console.log( + '[NotificationService] Notifee notification pressed (foreground):', + detail.notification.data, + ); + onOpen?.(detail.notification.data as NotificationData); + } + }); + + return () => { + unsubscribeFCM(); + unsubscribeNotifee(); + }; +} + +export function registerBackgroundHandler(): void { + messaging().setBackgroundMessageHandler( + async (remoteMessage: FirebaseMessagingTypes.RemoteMessage) => { + console.log( + '[NotificationService] Background FCM message:', + remoteMessage, + ); + // FCM data-only messages in the background need manual display + if (remoteMessage.data && !remoteMessage.notification) { + const title = + (remoteMessage.data.title as string) ?? 'Convex CRM'; + const body = + (remoteMessage.data.body as string) ?? 'You have a new notification'; + await displayNotification(title, body, remoteMessage.data as NotificationData); + } + }, + ); + + // Notifee background event (handles taps on notifications from background) + notifee.onBackgroundEvent(async ({ type, detail }) => { + if (type === EventType.PRESS) { + console.log( + '[NotificationService] Notifee notification pressed (background):', + detail.notification?.data, + ); + } + if (type === EventType.DISMISSED) { + console.log( + '[NotificationService] Notification dismissed (background)', + ); + } + }); +} + +/** + * Subscribe to FCM notification taps when the app is in the background + * (but not fully quit). Returns an unsubscribe function. + */ +export function onNotificationOpenedApp( + callback: NotificationOpenHandler, +): () => void { + const unsubscribe = messaging().onNotificationOpenedApp(remoteMessage => { + console.log( + '[NotificationService] App opened from background notification:', + remoteMessage, + ); + callback((remoteMessage.data ?? {}) as NotificationData); + }); + return unsubscribe; +} + +/** + * Check whether the app was launched by tapping a notification + * while it was fully quit. Call once on app startup. + */ +export async function getInitialNotification( + callback: NotificationOpenHandler, +): Promise { + try { + const remoteMessage = await messaging().getInitialNotification(); + if (remoteMessage) { + console.log( + '[NotificationService] App launched from quit-state notification:', + remoteMessage, + ); + callback((remoteMessage.data ?? {}) as NotificationData); + } + } catch (error) { + console.error('[NotificationService] getInitialNotification error:', error); + } +} + +/** + * Set the app icon badge count (iOS only; no-op on Android). + */ +export async function setBadgeCount(count: number): Promise { + try { + await notifee.setBadgeCount(count); + } catch (error) { + console.error('[NotificationService] setBadgeCount error:', error); + } +} + +/** + * Clear the app icon badge (iOS only; no-op on Android). + */ +export async function clearBadge(): Promise { + try { + await notifee.setBadgeCount(0); + } catch (error) { + console.error('[NotificationService] clearBadge error:', error); + } +} + +export interface InitializeOptions { + onNotificationOpen?: NotificationOpenHandler; + onTokenRefreshed?: (token: string) => void; +} + +export async function initialize( + options: InitializeOptions = {}, +): Promise<() => void> { + const { onNotificationOpen, onTokenRefreshed } = options; + + // 1. Permission + await requestPermission(); + + // 2. Android channel + await createDefaultChannel(); + + // 3. FCM token + await getFCMToken(); + + // 4. Token refresh + const unsubscribeTokenRefresh = onTokenRefresh(token => { + onTokenRefreshed?.(token); + }); + + // 5. Foreground messages + const unsubscribeForeground = onForegroundMessage(onNotificationOpen); + + // 6. Background tap + const unsubscribeBackgroundOpen = onNotificationOpenedApp(data => { + onNotificationOpen?.(data); + }); + + // 7. Quit-state launch + await getInitialNotification(data => { + onNotificationOpen?.(data); + }); + + // Return aggregate cleanup + return () => { + unsubscribeTokenRefresh(); + unsubscribeForeground(); + unsubscribeBackgroundOpen(); + }; +} + +export const NotificationService = { + requestPermission, + createDefaultChannel, + getFCMToken, + onTokenRefresh, + displayNotification, + onForegroundMessage, + registerBackgroundHandler, + onNotificationOpenedApp, + getInitialNotification, + setBadgeCount, + clearBadge, + initialize, +}; diff --git a/app/store/commonReducers/auth/reducers.ts b/app/store/commonReducers/auth/reducers.ts index 96a3dc0..2b6bc86 100644 --- a/app/store/commonReducers/auth/reducers.ts +++ b/app/store/commonReducers/auth/reducers.ts @@ -24,7 +24,7 @@ const initialState: LoginState = { loginMessage: null, }; -export const reducers = createReducer(initialState, builder => { +export const authReducer = createReducer(initialState, builder => { builder .addCase(login.pending, acc => { acc.loginLoading = true; @@ -49,4 +49,4 @@ export const reducers = createReducer(initialState, builder => { }); }); -export default reducers; +export default authReducer; diff --git a/app/store/commonReducers/auth/thunk.ts b/app/store/commonReducers/auth/thunk.ts index 24f5f88..89b6e7e 100644 --- a/app/store/commonReducers/auth/thunk.ts +++ b/app/store/commonReducers/auth/thunk.ts @@ -1,4 +1,4 @@ -import { createAsyncThunk } from '@reduxjs/toolkit'; +import { createAction, createAsyncThunk } from '@reduxjs/toolkit'; import { LoginRequest, LoginResponse } from '@interfaces'; import { loginApi } from '@api'; @@ -12,3 +12,5 @@ export const login = createAsyncThunk( } }, ); + +export const logout = createAction('USER_LOGOUT'); diff --git a/app/store/commonReducers/countrylist/index.ts b/app/store/commonReducers/countrylist/index.ts new file mode 100644 index 0000000..40a5b4c --- /dev/null +++ b/app/store/commonReducers/countrylist/index.ts @@ -0,0 +1,2 @@ +export * from './thunk'; +export * from './reducers'; diff --git a/app/store/commonReducers/countrylist/reducers.ts b/app/store/commonReducers/countrylist/reducers.ts new file mode 100644 index 0000000..502145b --- /dev/null +++ b/app/store/commonReducers/countrylist/reducers.ts @@ -0,0 +1,37 @@ +import { createReducer } from '@reduxjs/toolkit'; +import { getCountryList } from './thunk'; +import { CountryListItem } from '@interfaces'; + +export interface CountryListState { + items: CountryListItem[]; + loading: boolean; + error: string | null; +} + +const initialState: CountryListState = { + items: [], + loading: false, + error: null, +}; + +export const countryListReducer = createReducer(initialState, builder => { + builder + .addCase(getCountryList.pending, acc => { + acc.loading = true; + acc.error = null; + }) + .addCase(getCountryList.fulfilled, (acc, action) => { + acc.loading = false; + acc.items = action.payload; + acc.error = null; + }) + .addCase(getCountryList.rejected, (acc, action) => { + acc.loading = false; + acc.error = + (action.payload as string) ?? + action.error.message ?? + 'Failed to fetch country list'; + }); +}); + +export default countryListReducer; diff --git a/app/store/commonReducers/countrylist/thunk.ts b/app/store/commonReducers/countrylist/thunk.ts new file mode 100644 index 0000000..5526817 --- /dev/null +++ b/app/store/commonReducers/countrylist/thunk.ts @@ -0,0 +1,14 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; +import { getCountryListApi } from '@api'; +import { CountryListItem } from '@interfaces'; + +export const getCountryList = createAsyncThunk( + 'countrylist/getCountryList', + async (_, { rejectWithValue }) => { + try { + return await getCountryListApi(); + } catch (error: any) { + return rejectWithValue(error.message || 'Failed to fetch country list'); + } + }, +); diff --git a/app/store/commonReducers/index.ts b/app/store/commonReducers/index.ts index 269586e..badd083 100644 --- a/app/store/commonReducers/index.ts +++ b/app/store/commonReducers/index.ts @@ -1 +1,4 @@ export * from './auth'; +export * from './statuslist'; +export * from './sourcelist'; +export * from './countrylist'; diff --git a/app/store/commonReducers/sourcelist/index.ts b/app/store/commonReducers/sourcelist/index.ts new file mode 100644 index 0000000..40a5b4c --- /dev/null +++ b/app/store/commonReducers/sourcelist/index.ts @@ -0,0 +1,2 @@ +export * from './thunk'; +export * from './reducers'; diff --git a/app/store/commonReducers/sourcelist/reducers.ts b/app/store/commonReducers/sourcelist/reducers.ts new file mode 100644 index 0000000..1e09d61 --- /dev/null +++ b/app/store/commonReducers/sourcelist/reducers.ts @@ -0,0 +1,37 @@ +import { createReducer } from '@reduxjs/toolkit'; +import { getSourceList } from './thunk'; +import { SourceListItem } from '@interfaces'; + +export interface SourceListState { + items: SourceListItem[]; + loading: boolean; + error: string | null; +} + +const initialState: SourceListState = { + items: [], + loading: false, + error: null, +}; + +export const sourceListReducer = createReducer(initialState, builder => { + builder + .addCase(getSourceList.pending, acc => { + acc.loading = true; + acc.error = null; + }) + .addCase(getSourceList.fulfilled, (acc, action) => { + acc.loading = false; + acc.items = action.payload; + acc.error = null; + }) + .addCase(getSourceList.rejected, (acc, action) => { + acc.loading = false; + acc.error = + (action.payload as string) ?? + action.error.message ?? + 'Failed to fetch source list'; + }); +}); + +export default sourceListReducer; diff --git a/app/store/commonReducers/sourcelist/thunk.ts b/app/store/commonReducers/sourcelist/thunk.ts new file mode 100644 index 0000000..06af097 --- /dev/null +++ b/app/store/commonReducers/sourcelist/thunk.ts @@ -0,0 +1,14 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; +import { getSourceListApi } from '@api'; +import { SourceListItem } from '@interfaces'; + +export const getSourceList = createAsyncThunk( + 'sourcelist/getSourceList', + async (_, { rejectWithValue }) => { + try { + return await getSourceListApi(); + } catch (error: any) { + return rejectWithValue(error.message || 'Failed to fetch source list'); + } + }, +); diff --git a/app/store/commonReducers/statuslist/index.ts b/app/store/commonReducers/statuslist/index.ts new file mode 100644 index 0000000..40a5b4c --- /dev/null +++ b/app/store/commonReducers/statuslist/index.ts @@ -0,0 +1,2 @@ +export * from './thunk'; +export * from './reducers'; diff --git a/app/store/commonReducers/statuslist/reducers.ts b/app/store/commonReducers/statuslist/reducers.ts new file mode 100644 index 0000000..8857243 --- /dev/null +++ b/app/store/commonReducers/statuslist/reducers.ts @@ -0,0 +1,37 @@ +import { createReducer } from '@reduxjs/toolkit'; +import { getStatusList } from './thunk'; +import { StatusListItem } from '@interfaces'; + +export interface StatusListState { + items: StatusListItem[]; + loading: boolean; + error: string | null; +} + +const initialState: StatusListState = { + items: [], + loading: false, + error: null, +}; + +export const statusListReducer = createReducer(initialState, builder => { + builder + .addCase(getStatusList.pending, acc => { + acc.loading = true; + acc.error = null; + }) + .addCase(getStatusList.fulfilled, (acc, action) => { + acc.loading = false; + acc.items = action.payload; + acc.error = null; + }) + .addCase(getStatusList.rejected, (acc, action) => { + acc.loading = false; + acc.error = + (action.payload as string) ?? + action.error.message ?? + 'Failed to fetch status list'; + }); +}); + +export default statusListReducer; diff --git a/app/store/commonReducers/statuslist/thunk.ts b/app/store/commonReducers/statuslist/thunk.ts new file mode 100644 index 0000000..41502a5 --- /dev/null +++ b/app/store/commonReducers/statuslist/thunk.ts @@ -0,0 +1,14 @@ +import { createAsyncThunk } from '@reduxjs/toolkit'; +import { getStatusListApi } from '@api'; +import { StatusListItem } from '@interfaces'; + +export const getStatusList = createAsyncThunk( + 'statuslist/getStatusList', + async (_, { rejectWithValue }) => { + try { + return await getStatusListApi(); + } catch (error: any) { + return rejectWithValue(error.message || 'Failed to fetch status list'); + } + }, +); diff --git a/app/store/rootReducer.ts b/app/store/rootReducer.ts index 34e510f..61b77c6 100644 --- a/app/store/rootReducer.ts +++ b/app/store/rootReducer.ts @@ -1,13 +1,26 @@ import { combineReducers } from '@reduxjs/toolkit'; import authReducer from './commonReducers/auth/reducers'; +import statusListReducer from './commonReducers/statuslist/reducers'; +import sourceListReducer from './commonReducers/sourcelist/reducers'; +import countryListReducer from './commonReducers/countrylist/reducers'; import leadsReducer from '../features/leads/reducers'; import leadDetailsReducer from '../features/leadDetails/reducers'; -const rootReducer = combineReducers({ +const appReducer = combineReducers({ auth: authReducer, + statusList: statusListReducer, + sourceList: sourceListReducer, + countryList: countryListReducer, leads: leadsReducer, leadDetails: leadDetailsReducer, }); -export type RootState = ReturnType; +const rootReducer = (state: any, action: any) => { + if (action.type === 'USER_LOGOUT') { + state = undefined; + } + return appReducer(state, action); +}; + +export type RootState = ReturnType; export default rootReducer; diff --git a/app/theme/colors.ts b/app/theme/colors.ts index 4e502d8..8b9e738 100644 --- a/app/theme/colors.ts +++ b/app/theme/colors.ts @@ -1,39 +1,44 @@ -// Palette derived from the app background (blue/navy/cyan) +// ───────────────────────────────────────────────────────────────────────────── +// Design System Palette — Modern CRM (Indigo-Violet base) +// Light: Clean whites with indigo accents, warm card surfaces +// Dark: Rich deep slate/charcoal with electric indigo/violet accents +// ───────────────────────────────────────────────────────────────────────────── export const colors = { - // Brand - primary: '#1565C0', // deep blue (dark bg accent / light primary) - primaryLight: '#1E90FF', // bright sky blue - accent: '#4FC3F7', // cyan highlight + // ── Brand tokens ──────────────────────────────────────────────────────────── + primary: '#5B4CF5', // electric indigo — primary actions + primaryLight: '#7C6FF7', // lighter indigo for hover / tint + accent: '#8B5CF6', // violet — gradient end, highlights + accentAlt: '#06B6D4', // cyan — contrast accent (badges, chips) - // Light theme + // ── Light theme ───────────────────────────────────────────────────────────── light: { - background: '#F0F6FF', - surface: '#FFFFFF', - border: '#BBDEFB', - text: '#0A1A6B', - textSecondary: '#1E3A8A', - textMuted: '#5C7BA8', - icon: '#1565C0', + background: '#F4F6FB', // soft cool white-grey — avoids pure white glare + surface: '#FFFFFF', // pure white surfaces (search, buttons) + card: '#FFFFFF', // card bg — clean white with shadow + border: '#E4E8F5', // subtle lavender-grey border + text: '#1A1D3A', // near-black with slight blue tint — excellent contrast + textSecondary: '#4B5280', // medium slate — readable secondary text + textMuted: '#9399BB', // muted lavender — placeholders, dates + icon: '#5B4CF5', // brand indigo icons tabBar: '#FFFFFF', header: '#FFFFFF', drawerBg: '#FFFFFF', - drawerHeader: '#0A237A', - card: '#FFFFFF', + drawerHeader: '#1A1D3A', }, - // Dark theme + // ── Dark theme ────────────────────────────────────────────────────────────── dark: { - background: '#07112A', - surface: '#0D1F4A', - border: '#1A3575', - text: '#E3F0FF', - textSecondary: '#A8C8F5', - textMuted: '#5C7BA8', - icon: '#4FC3F7', - tabBar: '#0D1F4A', - header: '#0D1F4A', - drawerBg: '#0A1A6B', - drawerHeader: '#07112A', - card: '#0D1F4A', + background: '#0F1022', // very deep blue-black — premium feel + surface: '#1A1D3A', // dark navy-indigo — elevated surfaces + card: '#1E2140', // slightly lighter than surface for cards + border: '#2D3260', // muted indigo border — visible but subtle + text: '#EEF0FF', // near-white with slight violet tint + textSecondary: '#A5AACF', // soft lavender — readable secondary + textMuted: '#5C6290', // deep muted indigo — placeholders, dates + icon: '#7C6FF7', // light indigo icons + tabBar: '#1A1D3A', + header: '#1A1D3A', + drawerBg: '#141630', + drawerHeader: '#0F1022', }, } as const; diff --git a/app/utils/assets/index.ts b/app/utils/assets/index.ts index 94b51e5..8067875 100644 --- a/app/utils/assets/index.ts +++ b/app/utils/assets/index.ts @@ -1,2 +1,3 @@ export const backGroundImage = require('./background.png') +export const logo = require('./logo.png') diff --git a/app/utils/assets/logo.png b/app/utils/assets/logo.png new file mode 100644 index 0000000..6992c1d Binary files /dev/null and b/app/utils/assets/logo.png differ diff --git a/app/utils/helper.ts b/app/utils/helper.ts index 19bc377..c6a1684 100644 --- a/app/utils/helper.ts +++ b/app/utils/helper.ts @@ -51,3 +51,19 @@ export const getInitials = (name?: string | null): string => { return name.substring(0, 2).toUpperCase(); }; + +export const toRgba = (hex: string, alpha: number): string => { + const h = hex.replace('#', ''); + const r = parseInt(h.slice(0, 2), 16); + const g = parseInt(h.slice(2, 4), 16); + const b = parseInt(h.slice(4, 6), 16); + return `rgba(${r},${g},${b},${alpha})`; +}; + +export const formatDate = (raw: string): string => { + if (!raw) return ''; + const d = new Date(raw); + return isNaN(d.getTime()) + ? raw + : d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); +}; diff --git a/assets/bootsplash/logo.png b/assets/bootsplash/logo.png new file mode 100644 index 0000000..0496418 Binary files /dev/null and b/assets/bootsplash/logo.png differ diff --git a/assets/bootsplash/logo@1,5x.png b/assets/bootsplash/logo@1,5x.png new file mode 100644 index 0000000..460e2ee Binary files /dev/null and b/assets/bootsplash/logo@1,5x.png differ diff --git a/assets/bootsplash/logo@2x.png b/assets/bootsplash/logo@2x.png new file mode 100644 index 0000000..cde3fef Binary files /dev/null and b/assets/bootsplash/logo@2x.png differ diff --git a/assets/bootsplash/logo@3x.png b/assets/bootsplash/logo@3x.png new file mode 100644 index 0000000..c1a644a Binary files /dev/null and b/assets/bootsplash/logo@3x.png differ diff --git a/assets/bootsplash/logo@4x.png b/assets/bootsplash/logo@4x.png new file mode 100644 index 0000000..b3e4adb Binary files /dev/null and b/assets/bootsplash/logo@4x.png differ diff --git a/assets/bootsplash/manifest.json b/assets/bootsplash/manifest.json new file mode 100644 index 0000000..db974f3 --- /dev/null +++ b/assets/bootsplash/manifest.json @@ -0,0 +1,7 @@ +{ + "background": "#ffffff", + "logo": { + "width": 100, + "height": 100 + } +} diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..6992c1d Binary files /dev/null and b/assets/logo.png differ diff --git a/index.js b/index.js index 06002ff..3619874 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,12 @@ import { AppRegistry } from 'react-native'; import App from './app/App'; import { name as appName } from './app.json'; +import { NotificationService } from '@services'; if (__DEV__) { require('./ReactotronConfig'); } +NotificationService.registerBackgroundHandler(); + AppRegistry.registerComponent(appName, () => App); diff --git a/ios/convex_CRM.xcodeproj/project.pbxproj b/ios/convex_CRM.xcodeproj/project.pbxproj index 97aa070..e95405c 100644 --- a/ios/convex_CRM.xcodeproj/project.pbxproj +++ b/ios/convex_CRM.xcodeproj/project.pbxproj @@ -11,6 +11,8 @@ 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; + 79E049D907A74F1AB0714099 /* BootSplash.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 688D1DB853AD445C91817BC9 /* BootSplash.storyboard */; }; + 1E6477E483F84C6495D52EC7 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A57FEAAA1E5B410F8EEA9BA8 /* Colors.xcassets */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -24,6 +26,8 @@ 761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = convex_CRM/AppDelegate.swift; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = convex_CRM/LaunchScreen.storyboard; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; + 688D1DB853AD445C91817BC9 /* BootSplash.storyboard */ = {isa = PBXFileReference; name = "BootSplash.storyboard"; path = "convex_CRM/BootSplash.storyboard"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; }; + A57FEAAA1E5B410F8EEA9BA8 /* Colors.xcassets */ = {isa = PBXFileReference; name = "Colors.xcassets"; path = "convex_CRM/Colors.xcassets"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = folder.assetcatalog; explicitFileType = undefined; includeInIndex = 0; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -46,6 +50,8 @@ 13B07FB61A68108700A75B9A /* Info.plist */, 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */, + 688D1DB853AD445C91817BC9 /* BootSplash.storyboard */, + A57FEAAA1E5B410F8EEA9BA8 /* Colors.xcassets */, ); name = convex_CRM; sourceTree = ""; @@ -159,6 +165,8 @@ files = ( 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, + 79E049D907A74F1AB0714099 /* BootSplash.storyboard in Resources */, + 1E6477E483F84C6495D52EC7 /* Colors.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios/convex_CRM/BootSplash.storyboard b/ios/convex_CRM/BootSplash.storyboard new file mode 100644 index 0000000..fb5bc6d --- /dev/null +++ b/ios/convex_CRM/BootSplash.storyboard @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/convex_CRM/Colors.xcassets/BootSplashBackground-15a9bf.colorset/Contents.json b/ios/convex_CRM/Colors.xcassets/BootSplashBackground-15a9bf.colorset/Contents.json new file mode 100644 index 0000000..f13e571 --- /dev/null +++ b/ios/convex_CRM/Colors.xcassets/BootSplashBackground-15a9bf.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors": [ + { + "idiom": "universal", + "color": { + "color-space": "srgb", + "components": { + "blue": "1.00000000000000", + "green": "1.00000000000000", + "red": "1.00000000000000", + "alpha": "1.000" + } + } + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/ios/convex_CRM/Images.xcassets/BootSplashLogo-15a9bf.imageset/Contents.json b/ios/convex_CRM/Images.xcassets/BootSplashLogo-15a9bf.imageset/Contents.json new file mode 100644 index 0000000..24e6206 --- /dev/null +++ b/ios/convex_CRM/Images.xcassets/BootSplashLogo-15a9bf.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images": [ + { + "idiom": "universal", + "filename": "logo-15a9bf.png", + "scale": "1x" + }, + { + "idiom": "universal", + "filename": "logo-15a9bf@2x.png", + "scale": "2x" + }, + { + "idiom": "universal", + "filename": "logo-15a9bf@3x.png", + "scale": "3x" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/ios/convex_CRM/Images.xcassets/BootSplashLogo-15a9bf.imageset/logo-15a9bf.png b/ios/convex_CRM/Images.xcassets/BootSplashLogo-15a9bf.imageset/logo-15a9bf.png new file mode 100644 index 0000000..0496418 Binary files /dev/null and b/ios/convex_CRM/Images.xcassets/BootSplashLogo-15a9bf.imageset/logo-15a9bf.png differ diff --git a/ios/convex_CRM/Images.xcassets/BootSplashLogo-15a9bf.imageset/logo-15a9bf@2x.png b/ios/convex_CRM/Images.xcassets/BootSplashLogo-15a9bf.imageset/logo-15a9bf@2x.png new file mode 100644 index 0000000..cde3fef Binary files /dev/null and b/ios/convex_CRM/Images.xcassets/BootSplashLogo-15a9bf.imageset/logo-15a9bf@2x.png differ diff --git a/ios/convex_CRM/Images.xcassets/BootSplashLogo-15a9bf.imageset/logo-15a9bf@3x.png b/ios/convex_CRM/Images.xcassets/BootSplashLogo-15a9bf.imageset/logo-15a9bf@3x.png new file mode 100644 index 0000000..c1a644a Binary files /dev/null and b/ios/convex_CRM/Images.xcassets/BootSplashLogo-15a9bf.imageset/logo-15a9bf@3x.png differ diff --git a/ios/convex_CRM/Info.plist b/ios/convex_CRM/Info.plist index 7aa4fe5..8ec5dc3 100644 --- a/ios/convex_CRM/Info.plist +++ b/ios/convex_CRM/Info.plist @@ -28,7 +28,6 @@ NSAppTransportSecurity - NSAllowsArbitraryLoads NSAllowsLocalNetworking @@ -37,7 +36,7 @@ NSLocationWhenInUseUsageDescription UILaunchStoryboardName - LaunchScreen + BootSplash UIRequiredDeviceCapabilities arm64 diff --git a/package.json b/package.json index bbbb537..d8f420e 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,10 @@ "test": "jest" }, "dependencies": { + "@notifee/react-native": "^9.1.8", "@react-native-async-storage/async-storage": "^3.1.1", + "@react-native-firebase/app": "^25.1.0", + "@react-native-firebase/messaging": "^25.1.0", "@react-navigation/bottom-tabs": "^7.18.8", "@react-navigation/drawer": "^7.12.8", "@react-navigation/native": "^7.3.8", @@ -20,6 +23,7 @@ "axios": "^1.18.1", "react": "19.2.3", "react-native": "0.86.0", + "react-native-bootsplash": "^7.3.2", "react-native-config": "^1.6.1", "react-native-gesture-handler": "^2.32.0", "react-native-reanimated": "^4.5.0", diff --git a/yarn.lock b/yarn.lock index 8852342..6fd09c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.29.0", "@babel/code-frame@^7.29.7": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.20.0", "@babel/code-frame@^7.29.0", "@babel/code-frame@^7.29.7": version "7.29.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.7.tgz#f2fbbfea87c44a21590ec515b778b2c26d8866e7" integrity sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw== @@ -1033,6 +1033,13 @@ dependencies: "@types/hammerjs" "^2.0.36" +"@emnapi/runtime@^1.11.1": + version "1.11.2" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.11.2.tgz#eb22f04d76febfdf4f87fdaff54c8a53f6bf0dbd" + integrity sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA== + dependencies: + tslib "^2.4.0" + "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.9.1": version "4.9.1" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595" @@ -1065,6 +1072,473 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== +"@expo/config-plugins@*": + version "57.0.5" + resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-57.0.5.tgz#6bbaeaef5d60218bee7afa9d05cc9427d29dfe8c" + integrity sha512-xhUGgzpFWRghDUH98+Wl4RDakYhTsbyMg6aOYiBjRzPO/THH8tKMw3vlksgFYlU2PkiAdABJN3tNPf5qmvOQhA== + dependencies: + "@expo/config-types" "^57.0.2" + "@expo/json-file" "~11.0.1" + "@expo/plist" "^0.8.1" + "@expo/require-utils" "^57.0.3" + "@expo/sdk-runtime-versions" "^1.0.0" + chalk "^4.1.2" + debug "^4.3.5" + getenv "^2.0.0" + glob "^13.0.0" + semver "^7.5.4" + slugify "^1.6.6" + xcode "^3.0.1" + xml2js "0.6.0" + +"@expo/config-types@^57.0.2": + version "57.0.2" + resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-57.0.2.tgz#127da99326399e014b63cf23d71220719f1336a1" + integrity sha512-ewW08OonrcRIsRKIlFvvcmmafE5zemb1ocu3HkNwtVPyRtj2w42pZCAkMIROYpcVBaPnc3mDT9UZDzwXWC3i6g== + +"@expo/json-file@~11.0.1": + version "11.0.1" + resolved "https://registry.yarnpkg.com/@expo/json-file/-/json-file-11.0.1.tgz#dab8f6e5ff78e7c33a8e49a48722ce972306036c" + integrity sha512-zxHWj4MKKMAL29ZQSY/Fssx4Thluk40JmuGNaeS078wy/NhlFhnVi+rHHunulE3xJAJ0CM73m8VK2+GkF9eRwQ== + dependencies: + "@babel/code-frame" "^7.20.0" + json5 "^2.2.3" + +"@expo/plist@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@expo/plist/-/plist-0.8.1.tgz#8f024f23d7b9cf45cfff4a6f8871d1c107a8833c" + integrity sha512-3gTReGIUm0oRaMClsAJYxBnVPCl6fVpsl8HS+DTVxDhW4GyVyxg9E/Znm3BvcHtUJ51RJJI14pC1wvrNilCRHw== + dependencies: + "@xmldom/xmldom" "^0.8.8" + base64-js "^1.5.1" + xmlbuilder "^15.1.1" + +"@expo/require-utils@^57.0.3": + version "57.0.3" + resolved "https://registry.yarnpkg.com/@expo/require-utils/-/require-utils-57.0.3.tgz#b2bcac04f0f227aad8412ac3a78a6a3b5c26093f" + integrity sha512-ns05X1K8tM+Qtzp6dNloUFOopSdh3J+HC61BtOR8WHhgtPFyX8TKuO2diqZUqVg9K8yfkWug7g8tBS0qRniSTA== + dependencies: + "@babel/code-frame" "^7.20.0" + "@babel/core" "^7.25.2" + "@babel/plugin-transform-modules-commonjs" "^7.24.8" + +"@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== + +"@firebase/ai@2.13.1": + version "2.13.1" + resolved "https://registry.yarnpkg.com/@firebase/ai/-/ai-2.13.1.tgz#15dc946a793bc8ea35e1aba03ec3cf50c9443ba9" + integrity sha512-RhT/VViTPBSplhQSuEp62HhLvfsV+LowMh8ZUo5MMRDzG7oFtSget4Kmg5oHP50hDVyWQuQj6to9iPFEZk08Tw== + dependencies: + "@firebase/app-check-interop-types" "0.3.4" + "@firebase/component" "0.7.3" + "@firebase/logger" "0.5.1" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/analytics-compat@0.2.28": + version "0.2.28" + resolved "https://registry.yarnpkg.com/@firebase/analytics-compat/-/analytics-compat-0.2.28.tgz#fb8f18e40a019dd9ea005859fe71ccf3d0867ee9" + integrity sha512-lIAlqUUbBu93FJMlQfslryQtBwwzdzvp23ePC6FNgymXk6Ook5v4Uvc0vdutvoIeqmyA3LfP0ZeRFK8+11kOOQ== + dependencies: + "@firebase/analytics" "0.10.22" + "@firebase/analytics-types" "0.8.4" + "@firebase/component" "0.7.3" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/analytics-types@0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@firebase/analytics-types/-/analytics-types-0.8.4.tgz#194ee289e7293e47b1bd6221147f0dcc02f92e3d" + integrity sha512-zQ+XTgkwH6CY/eUSHJRP7e4LxM30RCxlCmob5sy2axs25GE3Ny0XdgpDscMTHHQIGqWkxPXad4w2Mw9sCgT8zQ== + +"@firebase/analytics@0.10.22": + version "0.10.22" + resolved "https://registry.yarnpkg.com/@firebase/analytics/-/analytics-0.10.22.tgz#20f3a6431ed51c4590cf898455de7fd40b2163f0" + integrity sha512-8BSaq/QRGU1+xyi8L2PTLTJU7MH9aMA72RQdIxrbhWFauOZY9OXo8f2YDN/972xA8d588tlnNVEQ2Mo69pT9Ow== + dependencies: + "@firebase/component" "0.7.3" + "@firebase/installations" "0.6.22" + "@firebase/logger" "0.5.1" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/app-check-compat@0.4.5": + version "0.4.5" + resolved "https://registry.yarnpkg.com/@firebase/app-check-compat/-/app-check-compat-0.4.5.tgz#f136a07e5bcdef81aaad1b82b49557d5d1329ee8" + integrity sha512-JI17mVcZs34zO6ZeSCrw4U2iohqy+n6GIzkbmsA+TbVjmvFLkUKt3bs5M+qRBteQm/0IWzqSHYFzEQLzDTQebg== + dependencies: + "@firebase/app-check" "0.12.0" + "@firebase/app-check-types" "0.5.4" + "@firebase/component" "0.7.3" + "@firebase/logger" "0.5.1" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/app-check-interop-types@0.3.4": + version "0.3.4" + resolved "https://registry.yarnpkg.com/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.4.tgz#747bf9fe8e9db11f6a44b695f20754f2a20208a4" + integrity sha512-zz3i6e13B8BfWiLy8MABtTh8aGIACgKbf9UVnyHcWs+yQzJXgQcl8A46b0zfaiJHdQ+niF0ouAfcpuf+3LMPQg== + +"@firebase/app-check-types@0.5.4": + version "0.5.4" + resolved "https://registry.yarnpkg.com/@firebase/app-check-types/-/app-check-types-0.5.4.tgz#8001732e81332dd77d898d82fe773761cf2f023b" + integrity sha512-xV7JsIyzVr15aA7f3Pi0rB9gdBuVubs89FGA8VkRYA4g0l78poADgdfrScgf7NndSg9mm7cR7PJyY0+t22KaGw== + +"@firebase/app-check@0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@firebase/app-check/-/app-check-0.12.0.tgz#8fb9bdfa426b110169efc7446f84a9c54ef5bdd0" + integrity sha512-wMeT6HLWRAuW7Cp/5UjWBGKgjPNxWNOoNf4PRIv0weljoGMZVeqbUY7wNBWTI2/31cX1NlXx8gQruDLsUShB3Q== + dependencies: + "@firebase/component" "0.7.3" + "@firebase/logger" "0.5.1" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/app-compat@0.5.14": + version "0.5.14" + resolved "https://registry.yarnpkg.com/@firebase/app-compat/-/app-compat-0.5.14.tgz#8645e416e74aa8c559cff4c699bd37c1280b539e" + integrity sha512-rgFmiofYsdS9ZG/Bht3OBxJtPD3zWE1cffShWubEm+4+qZeyzCbmtb1q6jOEjN9fB7uufe4rQmWOPXouR3758Q== + dependencies: + "@firebase/app" "0.15.0" + "@firebase/component" "0.7.3" + "@firebase/logger" "0.5.1" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/app-types@0.9.5": + version "0.9.5" + resolved "https://registry.yarnpkg.com/@firebase/app-types/-/app-types-0.9.5.tgz#4c59391fd2559530d709a614d49f62af4ad8766b" + integrity sha512-YevqTjvo7Iujsa9Dwowmd6dSoElhzmD63ZSrq6bzjvQ6POjYgNjOFHLmNIgJs48eNO093NCERibuFnxbfOvU7A== + dependencies: + "@firebase/logger" "0.5.1" + +"@firebase/app@0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@firebase/app/-/app-0.15.0.tgz#5fbb1ed3e0f9d29f678b06ac12e3c1d5baff119b" + integrity sha512-soIskolmGgbpi0K/MfrjtdpO1220qRCbXA4Z8Qx3lM+fVwA3q40m+OM+7zBHd2nuQCrLXb33L6Oc1aBH3Y26AQ== + dependencies: + "@firebase/component" "0.7.3" + "@firebase/logger" "0.5.1" + "@firebase/util" "1.15.1" + idb "7.1.1" + tslib "^2.1.0" + +"@firebase/auth-compat@0.6.8": + version "0.6.8" + resolved "https://registry.yarnpkg.com/@firebase/auth-compat/-/auth-compat-0.6.8.tgz#09d828421a9ec1edcde60cd86785a29cad9b5720" + integrity sha512-llcBREUC4iSNKZ6rvwud7Oz9Q7aAWU6KuQLa6pdu7Q+QAQsy4JLw6yFgxwtmzabsgznHmmcsX2UjHLLzqUxi3Q== + dependencies: + "@firebase/auth" "1.13.3" + "@firebase/auth-types" "0.13.1" + "@firebase/component" "0.7.3" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/auth-interop-types@0.2.5": + version "0.2.5" + resolved "https://registry.yarnpkg.com/@firebase/auth-interop-types/-/auth-interop-types-0.2.5.tgz#828e2e160cff40fa78ecb4b6e3187c88158eb2e9" + integrity sha512-1Li/YuBDBAXcKv7BzY4U28gontUmAaw53sYiqbaVOMCFb2lFKK/c3CGMUWqtwe7+TXrl3poWnTCL5umYBg85Eg== + +"@firebase/auth-types@0.13.1": + version "0.13.1" + resolved "https://registry.yarnpkg.com/@firebase/auth-types/-/auth-types-0.13.1.tgz#eb5abafdaa40dd3b6badfab111dd94f7e196ea66" + integrity sha512-0c1Mnid0uMDfGJHeUS4zfvBa4/CedJXotGy/n/NZJnBjwiJawt0ZYU+wH2VAVLiRCEfG2ncCkAX3yd1/2nrB7g== + +"@firebase/auth@1.13.3": + version "1.13.3" + resolved "https://registry.yarnpkg.com/@firebase/auth/-/auth-1.13.3.tgz#5cad74ce656c9b4f8ffb2a026fab98d4b40c4a96" + integrity sha512-bqiq4uubDN2YyQkdvSWPQeJyXAv2O76ImF41En9b6UhV5JuBVYDoHYrrrE3NzIuGkpFMKagfhMRP4Vz6t+yQSQ== + dependencies: + "@firebase/component" "0.7.3" + "@firebase/logger" "0.5.1" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/component@0.7.3": + version "0.7.3" + resolved "https://registry.yarnpkg.com/@firebase/component/-/component-0.7.3.tgz#900c9720f7e5abb2a23975f90f96366d62d085b9" + integrity sha512-wFofIaa2879ogD/WvkjYXJxRmfnL0scen6ORgaC3na1FNOR9ASIUANQdhqQcmWu/h77/pVHY7ch5flewa5Bcew== + dependencies: + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/data-connect@0.7.1": + version "0.7.1" + resolved "https://registry.yarnpkg.com/@firebase/data-connect/-/data-connect-0.7.1.tgz#3ae9235525b5b0438968c4bf1ea1afc8816944b3" + integrity sha512-2LbUU8mmSA63HknxQMmWHjpzuNLBKflvVwQc2tpoVKg0biWleNEJX031ELks0vzFs+dDjOUkCJR72RP6mQHFOg== + dependencies: + "@firebase/auth-interop-types" "0.2.5" + "@firebase/component" "0.7.3" + "@firebase/logger" "0.5.1" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/database-compat@2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@firebase/database-compat/-/database-compat-2.1.4.tgz#8ea753bef91d2dfbd81853479799f3ab17ddbbbe" + integrity sha512-3pK35F1MAgmqFJQlf2nhQl44vtAXQO1uaCaQOEUI9kCRtLFqi7N+QRKR7lFZPg+xIZIyubgxQaxY69YgfZRZWg== + dependencies: + "@firebase/component" "0.7.3" + "@firebase/database" "1.1.3" + "@firebase/database-types" "1.0.20" + "@firebase/logger" "0.5.1" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/database-types@1.0.20": + version "1.0.20" + resolved "https://registry.yarnpkg.com/@firebase/database-types/-/database-types-1.0.20.tgz#2050c3c13a4b71d92797e1475a297c0b5e0c9f5d" + integrity sha512-kegbOk/w8iU64pr0q6k2ItyNGjnQBMHFhwS7ohdWI4W+pc0/zhhdGXTdFj6X1oxItRjPoYOsSQmERgBkn/ihxw== + dependencies: + "@firebase/app-types" "0.9.5" + "@firebase/util" "1.15.1" + +"@firebase/database@1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@firebase/database/-/database-1.1.3.tgz#ae3b1c906c45d70381359e61b4add6fc4beaa51e" + integrity sha512-XwWCa+E4TvNGpGwXrycLRNfdogADwFcvuhyow6wDWma9W54roaQIhe+4PM0KiLsIftBdSCGI7OKCXrdSRHbIhw== + dependencies: + "@firebase/app-check-interop-types" "0.3.4" + "@firebase/auth-interop-types" "0.2.5" + "@firebase/component" "0.7.3" + "@firebase/logger" "0.5.1" + "@firebase/util" "1.15.1" + faye-websocket "0.11.4" + tslib "^2.1.0" + +"@firebase/firestore-compat@0.4.11": + version "0.4.11" + resolved "https://registry.yarnpkg.com/@firebase/firestore-compat/-/firestore-compat-0.4.11.tgz#f382491377bad53e0aec888c62dfe400865266b5" + integrity sha512-W7o1WdwWq5aABK5Up2ncSvTQs/QGLR/fy7cVpFBNqhsXtxoMtflHf2xBIG6+aoptcuGAobddq4g2Sq27wqHaYw== + dependencies: + "@firebase/component" "0.7.3" + "@firebase/firestore" "4.16.0" + "@firebase/firestore-types" "3.0.4" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/firestore-types@3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@firebase/firestore-types/-/firestore-types-3.0.4.tgz#a7205638195de1d356fbd39a620fd2d93c11d792" + integrity sha512-jGn+JSS4X9zZsrfu7Yw66v5YRdOLD1oyQh4USR0xWl4CUqV/DA6bNIXRPpxH/cUl3iVTNiP6MN7g+EL42A4qfA== + +"@firebase/firestore@4.16.0": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@firebase/firestore/-/firestore-4.16.0.tgz#502b35d7ed4cdc61d62a02311a67145192c804f6" + integrity sha512-qdHMHMvMr0nRMuZyWNR/ArWa0YlPE3C4eAbmxTASJMYXAesKPL0Y54p70moggrNPzaK7MSIIq5RDJJyntQyIYA== + dependencies: + "@firebase/component" "0.7.3" + "@firebase/logger" "0.5.1" + "@firebase/util" "1.15.1" + "@firebase/webchannel-wrapper" "1.0.6" + "@grpc/grpc-js" "~1.9.0" + "@grpc/proto-loader" "^0.7.8" + re2js "^0.4.2" + tslib "^2.1.0" + +"@firebase/functions-compat@0.4.5": + version "0.4.5" + resolved "https://registry.yarnpkg.com/@firebase/functions-compat/-/functions-compat-0.4.5.tgz#5e22c71c5b11c7d49177fd7b0ff62d8583567604" + integrity sha512-10qlUXGY25G5/1g9UihqksPp2po+ZqSE7LEizsrdUP7vrTmkysXxGSZCDyojSEp6mQe/ecRDdDDI+z4XRdb4wQ== + dependencies: + "@firebase/component" "0.7.3" + "@firebase/functions" "0.13.5" + "@firebase/functions-types" "0.6.4" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/functions-types@0.6.4": + version "0.6.4" + resolved "https://registry.yarnpkg.com/@firebase/functions-types/-/functions-types-0.6.4.tgz#bb73ed93aa396419f907967202572ea55a605a40" + integrity sha512-zV6kgqtduR4rUAdC/ilS7kmb93XD7bEZoJDlVBZqlOw2uGGGCNBQBuleww2rr0Ulr3L9o2TDjumEt68/l1f9DQ== + +"@firebase/functions@0.13.5": + version "0.13.5" + resolved "https://registry.yarnpkg.com/@firebase/functions/-/functions-0.13.5.tgz#a4e099c072d7eac32b0f835438c45fa29a531e93" + integrity sha512-bWCx713f4kE/uFV7gdFOLBS7lDoiZj48MRkbAqe35gkXcCeWF4QjRNO07Jhmve7EJIoQOBczL29y2r8VRuN1kw== + dependencies: + "@firebase/app-check-interop-types" "0.3.4" + "@firebase/auth-interop-types" "0.2.5" + "@firebase/component" "0.7.3" + "@firebase/messaging-interop-types" "0.2.5" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/installations-compat@0.2.22": + version "0.2.22" + resolved "https://registry.yarnpkg.com/@firebase/installations-compat/-/installations-compat-0.2.22.tgz#d8287cef67879b3d3e796fc7e81256e1b75c8c8a" + integrity sha512-C/zpAuTP5S9OgKSPvXRupw3hoY/JZSlA1wFjD/Sb7LIQE0FNbcMdO8Y4KXVEkjVzma/DDDDIAzxEXqKMAzc88w== + dependencies: + "@firebase/component" "0.7.3" + "@firebase/installations" "0.6.22" + "@firebase/installations-types" "0.5.4" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/installations-types@0.5.4": + version "0.5.4" + resolved "https://registry.yarnpkg.com/@firebase/installations-types/-/installations-types-0.5.4.tgz#3b1e41ae7d1b89eb3b4a1c9cc583a12bed83aa27" + integrity sha512-U2eFapdHwjb43Vx9o+Pmj4dFfvcHEK1IirEFLqMtWrTHvmdrS3gBpBD1kmJk/9HjsOtoHZxJ2Paoe79e+L1ZPg== + +"@firebase/installations@0.6.22": + version "0.6.22" + resolved "https://registry.yarnpkg.com/@firebase/installations/-/installations-0.6.22.tgz#f5e7d2f51e58d22f8fd5c44901e56e3555d58837" + integrity sha512-ef6nn3GGQTdReCfotRMG77PJZu8CqEbiK5pEoBnM0gTu/Z9v0i/az2p3HABsa/1beQmmyh1OsOjf7P5+pgwdZw== + dependencies: + "@firebase/component" "0.7.3" + "@firebase/util" "1.15.1" + idb "7.1.1" + tslib "^2.1.0" + +"@firebase/logger@0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@firebase/logger/-/logger-0.5.1.tgz#da1eb266b3fa8d1375617cb64c36c9a5cb63d8e1" + integrity sha512-vZKLsqE1ABOy8OjQiE7cUTFn4gvaqlk88yp8N94Pk/sDpq61YqZGqmVFZTvOyflTwuYFcWirBdYGoJgbDaXKYQ== + dependencies: + tslib "^2.1.0" + +"@firebase/messaging-compat@0.2.27": + version "0.2.27" + resolved "https://registry.yarnpkg.com/@firebase/messaging-compat/-/messaging-compat-0.2.27.tgz#55a5d0754f54ecf9befac7c0574a2c8f6899b208" + integrity sha512-JNOiu1PPgdHzEPEtoFiNxQuu0x9bm4bfETSQCpGfcTlgWkhlSK7uh7nlsjC10TQLUNgYetLmuutaYTh8aeYLVA== + dependencies: + "@firebase/component" "0.7.3" + "@firebase/messaging" "0.13.0" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/messaging-interop-types@0.2.5": + version "0.2.5" + resolved "https://registry.yarnpkg.com/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.5.tgz#078657f9be789bfaa31156a969630ecc4c0d5e16" + integrity sha512-tUEKnaAP2Y/MNIqgnriPpV6e5l13Vs/+p2yrd6NGlncPJT9O3a8muYZtdnWe+IJ4fgKLHJVC79n/asxk/N5Msw== + +"@firebase/messaging@0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@firebase/messaging/-/messaging-0.13.0.tgz#40b0c9a564759c963527c8aef9038c438397c3c7" + integrity sha512-GZoo0uGRvEbszo83xcgbjJp4FpkmBEr4l8Z4hi8gl+P1Spn/MTK3HapanMzSX4yUHuTEiF5hasWRxOaz+o5sxQ== + dependencies: + "@firebase/component" "0.7.3" + "@firebase/installations" "0.6.22" + "@firebase/messaging-interop-types" "0.2.5" + "@firebase/util" "1.15.1" + idb "7.1.1" + tslib "^2.1.0" + +"@firebase/performance-compat@0.2.25": + version "0.2.25" + resolved "https://registry.yarnpkg.com/@firebase/performance-compat/-/performance-compat-0.2.25.tgz#2c23654e67d974b0679b39a18a7672bf886d0bb5" + integrity sha512-q6NjTXpIPoFuUmCmMN/maCdTgzT6aExs9xZo+PxfVLj6uLVGvpyAD6XWjmcrb7jChsFBYbq7E5dyNDF7Zhy9kA== + dependencies: + "@firebase/component" "0.7.3" + "@firebase/logger" "0.5.1" + "@firebase/performance" "0.7.12" + "@firebase/performance-types" "0.2.4" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/performance-types@0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@firebase/performance-types/-/performance-types-0.2.4.tgz#9aa9e531f974f4b5e5a09bffff12406e0dc11e1f" + integrity sha512-kJSEk7b0uhpcPRyL4SQ/GPujLqk52XNKcXlnsKDbWGAb9vugcLvOU3u6zfEdwd+d8hWJb5S5ZizV1JFFI0nkKg== + +"@firebase/performance@0.7.12": + version "0.7.12" + resolved "https://registry.yarnpkg.com/@firebase/performance/-/performance-0.7.12.tgz#7d46c91f4942df365669dbb32492c100d069bad6" + integrity sha512-fe7nV8teUU3OBHlMUZ9Lw4gLhCW2k4m5Uc3pfWGV+fl8uwJQBGp9Q3lqsJ+HSrFu3Q2pJyLAgrClPGSKyDeYgQ== + dependencies: + "@firebase/component" "0.7.3" + "@firebase/installations" "0.6.22" + "@firebase/logger" "0.5.1" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + web-vitals "^4.2.4" + +"@firebase/remote-config-compat@0.2.26": + version "0.2.26" + resolved "https://registry.yarnpkg.com/@firebase/remote-config-compat/-/remote-config-compat-0.2.26.tgz#2c862af744f596ea4ae6e1abbbba2d9607572afb" + integrity sha512-uC57Tc7GYYOCnMgLkGIVf999XlaYaPDONoa54c93YTKDctlvCZI89z0zQ2RbhGR8Zf+QuCbQHs/99vqoE84a7g== + dependencies: + "@firebase/component" "0.7.3" + "@firebase/logger" "0.5.1" + "@firebase/remote-config" "0.8.5" + "@firebase/remote-config-types" "0.5.1" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/remote-config-types@0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@firebase/remote-config-types/-/remote-config-types-0.5.1.tgz#2bc52831f8b52aff2b1b434158b4f7803e05f86e" + integrity sha512-cX/1LT6KQwkXzck2eSzeKnuvXZCyr8qaPpDcikoJs7jmI+oBOXixpDLeDtWj1U6GNMkIoXrEDNoyT2Ypcyp5/A== + +"@firebase/remote-config@0.8.5": + version "0.8.5" + resolved "https://registry.yarnpkg.com/@firebase/remote-config/-/remote-config-0.8.5.tgz#273ba61e07a365172abee03a6cf9c0e0ac676aa6" + integrity sha512-zb+7CDGFP2wYVF1LXQoYIFdoESIQM3p0+uiW1welw8+zvDxAL50K75PKTXXtunJADUrksTVpV7mD0pn54vzJRA== + dependencies: + "@firebase/component" "0.7.3" + "@firebase/installations" "0.6.22" + "@firebase/logger" "0.5.1" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/storage-compat@0.4.3": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@firebase/storage-compat/-/storage-compat-0.4.3.tgz#85a01de8fbd0ef4cad261d39beea5e6391e5a051" + integrity sha512-gruVqjtUGX8tEoeNbaWXZm0Zfcfcb7fvmDmBxV8yPAbWvExRnZYLO2+qw9idxNE7BvPXt5csyjSYHy//dAizxw== + dependencies: + "@firebase/component" "0.7.3" + "@firebase/storage" "0.14.3" + "@firebase/storage-types" "0.8.4" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/storage-types@0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@firebase/storage-types/-/storage-types-0.8.4.tgz#88d72c80eb9a1167da33e8c551fd3b703c2422ec" + integrity sha512-BT7cwxJOx8SWwlQfrlC+bD/Sk3Cw+1odCi8UZNFNWTVZoPsBnA5W+mqtZzVnvsdJpXCFGSGQ7R7vOR6dtM/BRA== + +"@firebase/storage@0.14.3": + version "0.14.3" + resolved "https://registry.yarnpkg.com/@firebase/storage/-/storage-0.14.3.tgz#df93f895ff6f296e9305933cfe6c387d19fa4f93" + integrity sha512-YX4/YL6P6/fufSSeGnVhjWddcIXbFq2cWIhMKFTZo1E/Rtcl2mJj/BYUQTwJfcE1Tl8un1FOya4L05jcSLN/Eg== + dependencies: + "@firebase/component" "0.7.3" + "@firebase/util" "1.15.1" + tslib "^2.1.0" + +"@firebase/util@1.15.1": + version "1.15.1" + resolved "https://registry.yarnpkg.com/@firebase/util/-/util-1.15.1.tgz#7efaf8903f50b601c06afbaffbeb48ed2ba7aab8" + integrity sha512-LUdM4Wg7YM9Pq/49nGYySJA0CSQEKnGffFzWV8+6gXN7mGxn+FL1IqvFbuZUtAQcfZgHYDwCE1wwlK7rB7gl2g== + dependencies: + tslib "^2.1.0" + +"@firebase/webchannel-wrapper@1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.6.tgz#4be6a62b8c27a6bad8cc8da41f9a12de901e965d" + integrity sha512-Vr/Mqu79dMwGRAyGbJ4uN4+BtXB3/mRTdzetD1daWNeG8QaWuzhhbG77GltO5c0yYmYls8i250iX73624GJd7Q== + +"@grpc/grpc-js@~1.9.0": + version "1.9.16" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.9.16.tgz#614f85036ac8e3c957374c1bd1ebb05934a79a1c" + integrity sha512-wE4Ut/olIzfKqp631XrG+wbF0v1vWFN4YL9FyXC2LJiG33DsV7PLzURjrCvY/6je2ntdRkeLpPDluzSRGaVltQ== + dependencies: + "@grpc/proto-loader" "^0.7.8" + "@types/node" ">=12.12.47" + +"@grpc/proto-loader@^0.7.8": + version "0.7.15" + resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.15.tgz#4cdfbf35a35461fc843abe8b9e2c0770b5095e60" + integrity sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ== + dependencies: + lodash.camelcase "^4.3.0" + long "^5.0.0" + protobufjs "^7.2.5" + yargs "^17.7.2" + "@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0": version "9.3.0" resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" @@ -1096,6 +1570,167 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== +"@img/colour@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@img/colour/-/colour-1.1.0.tgz#b0c2c2fa661adf75effd6b4964497cd80010bb9d" + integrity sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ== + +"@img/sharp-darwin-arm64@0.35.3": + version "0.35.3" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz#8b2740884bc58b127fc3020479a01294fa1095cb" + integrity sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg== + optionalDependencies: + "@img/sharp-libvips-darwin-arm64" "1.3.2" + +"@img/sharp-darwin-x64@0.35.3": + version "0.35.3" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz#92df91320faf57cc54b331185770d5a93d510049" + integrity sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w== + optionalDependencies: + "@img/sharp-libvips-darwin-x64" "1.3.2" + +"@img/sharp-freebsd-wasm32@0.35.3": + version "0.35.3" + resolved "https://registry.yarnpkg.com/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz#48018c1379a8f507d681d6cd8dbe43d9795dc809" + integrity sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg== + dependencies: + "@img/sharp-wasm32" "0.35.3" + +"@img/sharp-libvips-darwin-arm64@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz#227b41ffc6c99612bceaba56f994a1933d779573" + integrity sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg== + +"@img/sharp-libvips-darwin-x64@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz#694903ec410c00945ce5b0c26dac83d7184c8b86" + integrity sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw== + +"@img/sharp-libvips-linux-arm64@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz#49ddf156728666a21deed0716f3bb72bb351179e" + integrity sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA== + +"@img/sharp-libvips-linux-arm@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz#e60e088c806bde1ac28be648b60c3465f41c18a7" + integrity sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ== + +"@img/sharp-libvips-linux-ppc64@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz#be3161aafd659417b3e26374dfbbcd2da2bfb62c" + integrity sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw== + +"@img/sharp-libvips-linux-riscv64@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz#bf008a6779d9be2b56a4df67b753941f767c957d" + integrity sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w== + +"@img/sharp-libvips-linux-s390x@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz#9583814b8db58889c85bad9e5e0b7825257ff394" + integrity sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ== + +"@img/sharp-libvips-linux-x64@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz#abc9a3114495b705ba20b7c1568b9eecd62aebbb" + integrity sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w== + +"@img/sharp-libvips-linuxmusl-arm64@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz#e58fb14a7879f15d9e70a982870f384f39a832a2" + integrity sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw== + +"@img/sharp-libvips-linuxmusl-x64@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz#5611480fcb7465dd5316044db53ad7a843ed4af8" + integrity sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ== + +"@img/sharp-linux-arm64@0.35.3": + version "0.35.3" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz#44b65823ecc977d4585b308070fff3947256de04" + integrity sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ== + optionalDependencies: + "@img/sharp-libvips-linux-arm64" "1.3.2" + +"@img/sharp-linux-arm@0.35.3": + version "0.35.3" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz#c8da500c4016893a89d46e9832d08a06eef77f27" + integrity sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA== + optionalDependencies: + "@img/sharp-libvips-linux-arm" "1.3.2" + +"@img/sharp-linux-ppc64@0.35.3": + version "0.35.3" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz#ea1d7db818f52af1e1e057e82d55f23b2de3864c" + integrity sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA== + optionalDependencies: + "@img/sharp-libvips-linux-ppc64" "1.3.2" + +"@img/sharp-linux-riscv64@0.35.3": + version "0.35.3" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz#722bd7994658791b02d1037ea09ca5cb78030d8d" + integrity sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ== + optionalDependencies: + "@img/sharp-libvips-linux-riscv64" "1.3.2" + +"@img/sharp-linux-s390x@0.35.3": + version "0.35.3" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz#e8294817d7da495541a4a870f56e6b5d0f8643cd" + integrity sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw== + optionalDependencies: + "@img/sharp-libvips-linux-s390x" "1.3.2" + +"@img/sharp-linux-x64@0.35.3": + version "0.35.3" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz#9843c32f39aeac4b60dd60f9e3416520a4e4de46" + integrity sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA== + optionalDependencies: + "@img/sharp-libvips-linux-x64" "1.3.2" + +"@img/sharp-linuxmusl-arm64@0.35.3": + version "0.35.3" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz#9cfee4ecc3d41ab9a274e019dfe7b4062840510c" + integrity sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w== + optionalDependencies: + "@img/sharp-libvips-linuxmusl-arm64" "1.3.2" + +"@img/sharp-linuxmusl-x64@0.35.3": + version "0.35.3" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz#204d0678c8c3b15300d9a26ecb9c0dcda11ca5de" + integrity sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg== + optionalDependencies: + "@img/sharp-libvips-linuxmusl-x64" "1.3.2" + +"@img/sharp-wasm32@0.35.3": + version "0.35.3" + resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz#42f8979bdbe1a541a2e9b99d3b5be07e6b71c7c0" + integrity sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w== + dependencies: + "@emnapi/runtime" "^1.11.1" + +"@img/sharp-webcontainers-wasm32@0.35.3": + version "0.35.3" + resolved "https://registry.yarnpkg.com/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz#823aaa93d14db84999d5b5dc967ff56cf1966d9f" + integrity sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q== + dependencies: + "@img/sharp-wasm32" "0.35.3" + +"@img/sharp-win32-arm64@0.35.3": + version "0.35.3" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz#f3554d45cbe24532a0adea736ec57658f74a2911" + integrity sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w== + +"@img/sharp-win32-ia32@0.35.3": + version "0.35.3" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz#0942b2643bcb57e48419fe4119de84078ae0ac74" + integrity sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw== + +"@img/sharp-win32-x64@0.35.3": + version "0.35.3" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz#8a25cacdc75a8c26077c07fba51e8056aae474f1" + integrity sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA== + "@isaacs/ttlcache@^1.4.1": version "1.4.1" resolved "https://registry.yarnpkg.com/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz#21fb23db34e9b6220c6ba023a0118a2dd3461ea2" @@ -1379,6 +2014,58 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@notifee/react-native@^9.1.8": + version "9.1.8" + resolved "https://registry.yarnpkg.com/@notifee/react-native/-/react-native-9.1.8.tgz#3d55cb3fbcc21f9e35931e366afdf64b294da891" + integrity sha512-Az/dueoPerJsbbjRxu8a558wKY+gONUrfoy3Hs++5OqbeMsR0dYe6P+4oN6twrLFyzAhEA1tEoZRvQTFDRmvQg== + +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.5.tgz#d9315ad7cf3f30aac70bda3c068443dc6f143659" + integrity sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g== + +"@protobufjs/eventemitter@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz#d512cb26c0ae026091ee2c1167f1be6faf5c842a" + integrity sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg== + +"@protobufjs/fetch@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.1.tgz#4d6fc00c8fb64016a5c81b469d549046350f1065" + integrity sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw== + dependencies: + "@protobufjs/aspromise" "^1.1.1" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== + +"@protobufjs/utf8@^1.1.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.2.tgz#78d476333d85d5b1c792e257bca74ba080da49a4" + integrity sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug== + "@react-native-async-storage/async-storage@^3.1.1": version "3.1.1" resolved "https://registry.yarnpkg.com/@react-native-async-storage/async-storage/-/async-storage-3.1.1.tgz#051caeb93919260041e53165f1b6c8a975b15105" @@ -1538,6 +2225,18 @@ prompts "^2.4.2" semver "^7.5.2" +"@react-native-firebase/app@^25.1.0": + version "25.1.0" + resolved "https://registry.yarnpkg.com/@react-native-firebase/app/-/app-25.1.0.tgz#7260d6f8b48078b9f651ca07804b140e9787397a" + integrity sha512-IRBM0Uwrs1O+SkUF8D9g7FgC8WI5b/3xEbt3JzFTTsHWvQyXxvTflKd4rNFNegV+2mFrM53XC9cS1BNVjWSSSg== + dependencies: + firebase "12.15.0" + +"@react-native-firebase/messaging@^25.1.0": + version "25.1.0" + resolved "https://registry.yarnpkg.com/@react-native-firebase/messaging/-/messaging-25.1.0.tgz#21d46ba446a728c44e7c5bc3622099ba355f915c" + integrity sha512-YVhGWr6uthySTNHqnXsDh6DX4WtK4AittU9oDeKaBSjN4uoMDOe2X74TYnBFEUdcCQxOAsUavFNAexQx/o8dsw== + "@react-native/assets-registry@0.86.0": version "0.86.0" resolved "https://registry.yarnpkg.com/@react-native/assets-registry/-/assets-registry-0.86.0.tgz#560e78135969d1198aabfdc4ae74faae336463d0" @@ -1929,7 +2628,7 @@ expect "^29.0.0" pretty-format "^29.0.0" -"@types/node@*": +"@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0": version "26.1.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-26.1.1.tgz#bad758d601e97d6cf457d204ee76a35fce7bd119" integrity sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw== @@ -2078,6 +2777,16 @@ resolved "https://registry.yarnpkg.com/@vscode/sudo-prompt/-/sudo-prompt-9.3.2.tgz#692ba38df40bd3502ccc4e9f099fbbaedbd5f04e" integrity sha512-gcXoCN00METUNFeQOFJ+C9xUI0DKB+0EGMVg7wbVYRHBw2Eq3fKisDZOkRdOz3kqXRKOENMfShPOmypw1/8nOw== +"@xmldom/xmldom@^0.8.8": + version "0.8.13" + resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.13.tgz#00d1dd940b218dff2e49309d410d8bb212159225" + integrity sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw== + +"@xmldom/xmldom@^0.9.10": + version "0.9.10" + resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.9.10.tgz#a0ad5a26fe8aa996310870726e1704977f769dee" + integrity sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -2468,6 +3177,11 @@ baseline-browser-mapping@^2.10.42: resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.43.tgz#7b5d11590ce5acdbe4859443e3c940e81ce8c02d" integrity sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ== +big-integer@1.6.x: + version "1.6.52" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85" + integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg== + bl@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" @@ -2495,6 +3209,25 @@ body-parser@^1.20.3: type-is "~1.6.18" unpipe "~1.0.0" +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + +bplist-creator@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.1.0.tgz#018a2d1b587f769e379ef5519103730f8963ba1e" + integrity sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg== + dependencies: + stream-buffers "2.2.x" + +bplist-parser@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.3.1.tgz#e1c90b2ca2a9f9474cc72f6862bbf3fee8341fd1" + integrity sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA== + dependencies: + big-integer "1.6.x" + brace-expansion@^1.1.7: version "1.1.16" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.16.tgz#723d3a30c0558c225abc9fc479a73e14e26c3c2f" @@ -2606,7 +3339,7 @@ caniuse-lite@^1.0.30001803: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz#1bc8e502b723fa393455dfbedd5ccec0c29bb74e" integrity sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw== -chalk@^4.0.0, chalk@^4.1.0: +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -2771,6 +3504,11 @@ commander@^12.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== +commander@^14.0.3: + version "14.0.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-14.0.3.tgz#425d79b48f9af82fcd9e4fc1ea8af6c5ec07bbc2" + integrity sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw== + commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -2865,6 +3603,22 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.6: shebang-command "^2.0.0" which "^2.0.1" +css-select@^5.1.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.2.2.tgz#01b6e8d163637bb2dd6c982ca4ed65863682786e" + integrity sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw== + dependencies: + boolbase "^1.0.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" + nth-check "^2.0.1" + +css-what@^6.1.0: + version "6.2.2" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.2.2.tgz#cdcc8f9b6977719fdfbd1de7aec24abf756b9dea" + integrity sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA== + csstype@^3.2.2: version "3.2.3" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" @@ -2909,7 +3663,7 @@ debug@2.6.9, debug@^2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.4.0, debug@^4.4.3: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.5, debug@^4.4.0, debug@^4.4.3: version "4.4.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== @@ -2981,6 +3735,16 @@ destroy@1.2.0, destroy@~1.2.0: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== +detect-indent@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" + integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== + +detect-libc@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" + integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -3005,6 +3769,36 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +domutils@^3.0.1: + version "3.2.2" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.2.2.tgz#edbfe2b668b0c1d97c24baf0f1062b132221bc78" + integrity sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + dunder-proto@^1.0.0, dunder-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" @@ -3044,6 +3838,11 @@ encodeurl@~2.0.0: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== +entities@^4.2.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + env-paths@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" @@ -3483,7 +4282,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.3.2: +fast-glob@^3.3.2, fast-glob@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== @@ -3518,6 +4317,13 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +faye-websocket@0.11.4: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + fb-dotslash@0.5.8: version "0.5.8" resolved "https://registry.yarnpkg.com/fb-dotslash/-/fb-dotslash-0.5.8.tgz#c5ef3dacd75e1ddb2197c367052464ddde0115f5" @@ -3597,6 +4403,40 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" +firebase@12.15.0: + version "12.15.0" + resolved "https://registry.yarnpkg.com/firebase/-/firebase-12.15.0.tgz#30efa309ebcac219f43adf0de462351185cb76e0" + integrity sha512-p0YTLcRSTiBXMx9sGr4ZNSfLjc/RVBEw4C/TXjVMtw65+6E1Pbm47UY3F4/AqRoDobEcNX3gsbPGy7jPjxbgSQ== + dependencies: + "@firebase/ai" "2.13.1" + "@firebase/analytics" "0.10.22" + "@firebase/analytics-compat" "0.2.28" + "@firebase/app" "0.15.0" + "@firebase/app-check" "0.12.0" + "@firebase/app-check-compat" "0.4.5" + "@firebase/app-compat" "0.5.14" + "@firebase/app-types" "0.9.5" + "@firebase/auth" "1.13.3" + "@firebase/auth-compat" "0.6.8" + "@firebase/data-connect" "0.7.1" + "@firebase/database" "1.1.3" + "@firebase/database-compat" "2.1.4" + "@firebase/firestore" "4.16.0" + "@firebase/firestore-compat" "0.4.11" + "@firebase/functions" "0.13.5" + "@firebase/functions-compat" "0.4.5" + "@firebase/installations" "0.6.22" + "@firebase/installations-compat" "0.2.22" + "@firebase/messaging" "0.13.0" + "@firebase/messaging-compat" "0.2.27" + "@firebase/performance" "0.7.12" + "@firebase/performance-compat" "0.2.25" + "@firebase/remote-config" "0.8.5" + "@firebase/remote-config-compat" "0.2.26" + "@firebase/storage" "0.14.3" + "@firebase/storage-compat" "0.4.3" + "@firebase/util" "1.15.1" + flat-cache@^3.0.4: version "3.2.0" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" @@ -3746,6 +4586,11 @@ get-symbol-description@^1.1.0: es-errors "^1.3.0" get-intrinsic "^1.2.6" +getenv@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/getenv/-/getenv-2.0.0.tgz#b1698c7b0f29588f4577d06c42c73a5b475c69e0" + integrity sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ== + glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -3760,6 +4605,15 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" +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.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -3855,6 +4709,11 @@ hasown@^2.0.2, hasown@^2.0.3, hasown@^2.0.4: dependencies: function-bind "^1.1.2" +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + hermes-compiler@250829098.0.14: version "250829098.0.14" resolved "https://registry.yarnpkg.com/hermes-compiler/-/hermes-compiler-250829098.0.14.tgz#4fbe8c18d277d5b766c4650c7bc4b3dddd6fd9c1" @@ -3919,6 +4778,11 @@ http-errors@~2.0.1: statuses "~2.0.2" toidentifier "~1.0.1" +http-parser-js@>=0.5.1: + version "0.5.10" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.10.tgz#b3277bd6d7ed5588e20ea73bf724fcbe44609075" + integrity sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA== + https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" @@ -3947,6 +4811,11 @@ iconv-lite@~0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +idb@7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b" + integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ== + idb@8.0.3: version "8.0.3" resolved "https://registry.yarnpkg.com/idb/-/idb-8.0.3.tgz#c91e558f15a8d53f1d7f53a094d226fc3ad71fd9" @@ -4865,6 +5734,11 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash.camelcase@^4.3.0: + version "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: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -4902,6 +5776,11 @@ logkitty@^0.7.1: dayjs "^1.8.15" yargs "^15.1.0" +long@^5.0.0, long@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/long/-/long-5.3.2.tgz#1d84463095999262d7d7b7f8bfd4a8cc55167f83" + integrity sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA== + loose-envify@^1.0.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -4914,6 +5793,11 @@ 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.5.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.5.2.tgz#00e16665c90c620fba14a3c368732a976493f760" + integrity sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -5230,7 +6114,7 @@ minipass@^4.2.4: resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a" integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ== -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2, minipass@^7.1.3: version "7.1.3" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.3.tgz#79389b4eb1bb2d003a9bba87d492f2bd37bdc65b" integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== @@ -5295,6 +6179,14 @@ node-exports-info@^1.6.0: object.entries "^1.1.9" semver "^6.3.1" +node-html-parser@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-7.1.0.tgz#48e83bd705dd8b89415c00bf670aebfe267312d7" + integrity sha512-iJo8b2uYGT40Y8BTyy5ufL6IVbN8rbm/1QK2xffXU/1a/v3AAa0d1YAoqBNYqaS4R/HajkWIpIfdE6KcyFh1AQ== + dependencies: + css-select "^5.1.0" + he "1.2.0" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -5322,6 +6214,13 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" +nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + nullthrows@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" @@ -5570,6 +6469,14 @@ 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" + picocolors@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" @@ -5604,6 +6511,15 @@ pkg-up@^3.1.0: dependencies: find-up "^3.0.0" +plist@^3.0.5: + version "3.1.1" + resolved "https://registry.yarnpkg.com/plist/-/plist-3.1.1.tgz#fa6099e1e3cf6ea180258ebe6378ea3878c2c841" + integrity sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA== + dependencies: + "@xmldom/xmldom" "^0.9.10" + base64-js "^1.5.1" + xmlbuilder "^15.1.1" + possible-typed-array-names@^1.0.0, possible-typed-array-names@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae" @@ -5619,6 +6535,11 @@ prettier@2.8.8: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +prettier@^3.8.1: + version "3.9.6" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.9.6.tgz#b3ea5146515d40fc53f18aa63f74dfab1e10dbf6" + integrity sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g== + pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" @@ -5652,6 +6573,23 @@ prop-types@^15.7.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +protobufjs@^7.2.5: + version "7.6.5" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.6.5.tgz#7b9250cdaf4a06139a9f0fe468a40d7d4febca71" + integrity sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.5" + "@protobufjs/eventemitter" "^1.1.1" + "@protobufjs/fetch" "^1.1.1" + "@protobufjs/float" "^1.0.2" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.1" + "@types/node" ">=13.7.0" + long "^5.3.2" + proxy-from-env@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz#a7487568adad577cfaaa7e88c49cab3ab3081aba" @@ -5712,6 +6650,11 @@ raw-body@~2.5.3: iconv-lite "~0.4.24" unpipe "~1.0.0" +re2js@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/re2js/-/re2js-0.4.3.tgz#1318cd0c12aa6ed3ba56d5e012311ffbfb2aef35" + integrity sha512-EuNmh7jurhHEE8Ge/lBo9JuMLb3qf866Xjjfyovw3wPc7+hlqDkZq4LwhrCQMEI+ARWfrKrHozEndzlpNT0WDg== + 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" @@ -5740,6 +6683,23 @@ react-is@^19.1.0, react-is@^19.2.3: resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.7.tgz#57668ee86a78574a542b0a539455212b2c086df2" integrity sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A== +react-native-bootsplash@^7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/react-native-bootsplash/-/react-native-bootsplash-7.3.2.tgz#d235f8de54a3f6f38b9cf7cde0d2199433815416" + integrity sha512-tPOmyufxKUin+1cDjsJqz1dLBDBV1O7kO+5uZgATih73eDkYgyDerXwjtorwrE0U6ILvTvOJ7rA4PiW1t7PTxQ== + dependencies: + "@expo/config-plugins" "*" + commander "^14.0.3" + detect-indent "^6.1.0" + fast-glob "^3.3.3" + node-html-parser "^7.1.0" + picocolors "^1.1.1" + prettier "^3.8.1" + react-native-is-edge-to-edge "^1.3.1" + sharp "^0.35.2" + ts-dedent "^2.2.0" + xml-formatter "^3.7.0" + react-native-config@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/react-native-config/-/react-native-config-1.6.1.tgz#f8428829fda74384484d773fe36820159c559159" @@ -6093,7 +7053,7 @@ safe-array-concat@^1.1.3: has-symbols "^1.1.0" isarray "^2.0.5" -safe-buffer@5.2.1, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -6120,6 +7080,11 @@ safe-regex-test@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sax@>=0.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.6.0.tgz#da59637629307b97e7c4cb28e080a7bc38560d5b" + integrity sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA== + scheduler@0.27.0, scheduler@^0.27.0: version "0.27.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd" @@ -6130,7 +7095,7 @@ semver@^6.3.0, semver@^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.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.7.3, semver@^7.7.4: +semver@^7.1.3, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.7.3, semver@^7.7.4, semver@^7.8.5: version "7.8.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.5.tgz#39b646037dd50c14fb451e7e4cac58ed8b863f69" integrity sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA== @@ -6215,6 +7180,41 @@ sf-symbols-typescript@^2.1.0: resolved "https://registry.yarnpkg.com/sf-symbols-typescript/-/sf-symbols-typescript-2.2.0.tgz#926d6e0715e3d8784cadf7658431e36581254208" integrity sha512-TPbeg0b7ylrswdGCji8FRGFAKuqbpQlLbL8SOle3j1iHSs5Ob5mhvMAxWN2UItOjgALAB5Zp3fmMfj8mbWvXKw== +sharp@^0.35.2: + version "0.35.3" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.35.3.tgz#41ed21a46406be163eacd0be7b364b42fcf2885f" + integrity sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q== + dependencies: + "@img/colour" "^1.1.0" + detect-libc "^2.1.2" + semver "^7.8.5" + optionalDependencies: + "@img/sharp-darwin-arm64" "0.35.3" + "@img/sharp-darwin-x64" "0.35.3" + "@img/sharp-freebsd-wasm32" "0.35.3" + "@img/sharp-libvips-darwin-arm64" "1.3.2" + "@img/sharp-libvips-darwin-x64" "1.3.2" + "@img/sharp-libvips-linux-arm" "1.3.2" + "@img/sharp-libvips-linux-arm64" "1.3.2" + "@img/sharp-libvips-linux-ppc64" "1.3.2" + "@img/sharp-libvips-linux-riscv64" "1.3.2" + "@img/sharp-libvips-linux-s390x" "1.3.2" + "@img/sharp-libvips-linux-x64" "1.3.2" + "@img/sharp-libvips-linuxmusl-arm64" "1.3.2" + "@img/sharp-libvips-linuxmusl-x64" "1.3.2" + "@img/sharp-linux-arm" "0.35.3" + "@img/sharp-linux-arm64" "0.35.3" + "@img/sharp-linux-ppc64" "0.35.3" + "@img/sharp-linux-riscv64" "0.35.3" + "@img/sharp-linux-s390x" "0.35.3" + "@img/sharp-linux-x64" "0.35.3" + "@img/sharp-linuxmusl-arm64" "0.35.3" + "@img/sharp-linuxmusl-x64" "0.35.3" + "@img/sharp-webcontainers-wasm32" "0.35.3" + "@img/sharp-win32-arm64" "0.35.3" + "@img/sharp-win32-ia32" "0.35.3" + "@img/sharp-win32-x64" "0.35.3" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -6277,6 +7277,15 @@ signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +simple-plist@^1.1.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/simple-plist/-/simple-plist-1.3.1.tgz#16e1d8f62c6c9b691b8383127663d834112fb017" + integrity sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw== + dependencies: + bplist-creator "0.1.0" + bplist-parser "0.3.1" + plist "^3.0.5" + simple-swizzle@^0.2.2: version "0.2.4" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.4.tgz#a8d11a45a11600d6a1ecdff6363329e3648c3667" @@ -6303,6 +7312,11 @@ slice-ansi@^2.0.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" +slugify@^1.6.6: + version "1.6.9" + resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.6.9.tgz#610957dea21e56b65e3a153215ef7b265715c8e8" + integrity sha512-vZ7rfeehZui7wQs438JXBckYLkIIdfHOXsaVEUMyS5fHo1483l1bMdo0EDSWYclY0yZKFOipDy4KHuKs6ssvdg== + source-map-support@0.5.13: version "0.5.13" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" @@ -6381,6 +7395,11 @@ stop-iteration-iterator@^1.1.0: es-errors "^1.3.0" internal-slot "^1.1.0" +stream-buffers@2.2.x: + version "2.2.0" + resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" + integrity sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg== + strict-uri-encode@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" @@ -6587,6 +7606,16 @@ ts-api-utils@^2.5.0: resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.5.0.tgz#4acd4a155e22734990a5ed1fe9e97f113bcb37c1" integrity sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA== +ts-dedent@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.3.0.tgz#8fac36c7902b541c154ac13a27ac467997af11f8" + integrity sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg== + +tslib@^2.1.0, tslib@^2.4.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -6755,6 +7784,11 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== +uuid@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" + integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== + v8-to-istanbul@^9.0.1: version "9.3.0" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" @@ -6793,6 +7827,25 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" +web-vitals@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-4.2.4.tgz#1d20bc8590a37769bd0902b289550936069184b7" + integrity sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw== + +websocket-driver@>=0.5.1: + version "0.7.5" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.5.tgz#569d22764ab21f2de20af0e74b411e8ae5a0fa46" + integrity sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + whatwg-fetch@^3.0.0: version "3.6.20" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz#580ce6d791facec91d37c72890995a0b48d31c70" @@ -6911,6 +7964,44 @@ ws@^7, ws@^7.5.10: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.12.tgz#4ca2c04966db4dcd2f9bc4d1419d23cd1e4a18db" integrity sha512-1xGnbYN3zbog9CwuNDQULNRrTCLIn46/WmpR1f0w6PsCYQHkylZr5vkd6kfMZYV6pRnQkcPNRyiA8LsrNKyhpg== +xcode@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/xcode/-/xcode-3.0.1.tgz#3efb62aac641ab2c702458f9a0302696146aa53c" + integrity sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA== + dependencies: + simple-plist "^1.1.0" + uuid "^7.0.3" + +xml-formatter@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/xml-formatter/-/xml-formatter-3.7.0.tgz#ac6f8c02e2388759652e9d0c8a4b5b3dfc0694e5" + integrity sha512-+8qTc3zv2UcJ1v9IsSIce37Dl4MQG14Cp7tWrwmy202UaI1wqRukw5QMX1JHsV+DX64yw77EgGsj2s5wGvuMbQ== + dependencies: + xml-parser-xo "^4.1.5" + +xml-parser-xo@^4.1.5: + version "4.1.6" + resolved "https://registry.yarnpkg.com/xml-parser-xo/-/xml-parser-xo-4.1.6.tgz#22bb2d0e1f651f92de04c7b79433a9df6f1490f2" + integrity sha512-mXbSNSM+rIwndoxSwmFa83bOdeP8G/cOGr7XvxA67X7ebCzoAuVez9JYDCDub3zRDNBZxIbfjuXLSsamr7NfwQ== + +xml2js@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.0.tgz#07afc447a97d2bd6507a1f76eeadddb09f7a8282" + integrity sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xmlbuilder@^15.1.1: + version "15.1.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" + integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== + +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + y18n@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" @@ -6979,7 +8070,7 @@ yargs@^16.1.1: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.3.1, yargs@^17.6.2: +yargs@^17.3.1, yargs@^17.6.2, yargs@^17.7.2: version "17.7.3" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.3.tgz#779dffe6bcafec596a7172e983289a588647faaa" integrity sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==