feat(app): improvement
This commit is contained in:
parent
e96ea0a490
commit
1f1b9aa264
@ -2,6 +2,9 @@
|
||||
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
|
||||
<!-- Opt out of Android 15+ forced edge-to-edge so the status bar
|
||||
behaves predictably with React Native's StatusBar component. -->
|
||||
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
|
||||
</style>
|
||||
|
||||
<style name="BootTheme" parent="Theme.BootSplash">
|
||||
|
||||
36
app/App.tsx
36
app/App.tsx
@ -1,5 +1,5 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { StatusBar } from 'react-native';
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { StatusBar, AppState, AppStateStatus, Platform } from 'react-native';
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
import { RootNavigator } from './navigation/rootNavigator';
|
||||
import { ThemeProvider, useTheme } from './theme';
|
||||
@ -8,6 +8,8 @@ import { store, persistor, useAppDispatch, useAppSelector, getStatusList, getSou
|
||||
import { PersistGate } from 'redux-persist/integration/react';
|
||||
import { NotificationService } from '@services';
|
||||
import BootSplash from 'react-native-bootsplash';
|
||||
import messaging from '@react-native-firebase/messaging';
|
||||
import { getStaffNotifications } from './features/notification/thunk';
|
||||
|
||||
const ThemedStatusBar = () => {
|
||||
const { theme: colors, isDark } = useTheme();
|
||||
@ -15,12 +17,16 @@ const ThemedStatusBar = () => {
|
||||
useEffect(() => {
|
||||
StatusBar.setBarStyle(isDark ? 'light-content' : 'dark-content', true);
|
||||
StatusBar.setBackgroundColor(colors.background, true);
|
||||
if (Platform.OS === 'android') {
|
||||
StatusBar.setTranslucent(false);
|
||||
}
|
||||
}, [colors, isDark]);
|
||||
|
||||
return (
|
||||
<StatusBar
|
||||
barStyle={isDark ? 'light-content' : 'dark-content'}
|
||||
backgroundColor={colors.background}
|
||||
translucent={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -40,7 +46,33 @@ const AppInit = () => {
|
||||
}
|
||||
}, [dispatch, token]);
|
||||
|
||||
// When a push notification arrives (foreground), re-fetch from the server
|
||||
// so the list and unread badge are always accurate
|
||||
useEffect(() => {
|
||||
const unsubscribe = messaging().onMessage(async () => {
|
||||
const staffId = (store.getState() as any).auth?.user_data?.staffid;
|
||||
if (staffId) {
|
||||
dispatch(getStaffNotifications(staffId));
|
||||
}
|
||||
});
|
||||
return unsubscribe;
|
||||
}, [dispatch]);
|
||||
|
||||
// When the app comes back to the foreground from background (e.g., user taps
|
||||
// a background push notification), re-fetch so count and list are up to date
|
||||
const appState = useRef<AppStateStatus>(AppState.currentState);
|
||||
useEffect(() => {
|
||||
const subscription = AppState.addEventListener('change', nextState => {
|
||||
if (appState.current.match(/inactive|background/) && nextState === 'active') {
|
||||
const staffId = (store.getState() as any).auth?.user_data?.staffid;
|
||||
if (staffId) {
|
||||
dispatch(getStaffNotifications(staffId));
|
||||
}
|
||||
}
|
||||
appState.current = nextState;
|
||||
});
|
||||
return () => subscription.remove();
|
||||
}, [dispatch]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { LoginRequest, LoginResponse } from '@interfaces';
|
||||
import { LoginRequest, LoginResponse, LogoutRequest, LogoutResponse } from '@interfaces';
|
||||
import { api } from '@utils';
|
||||
|
||||
export const loginApi = async (
|
||||
@ -7,8 +7,20 @@ export const loginApi = async (
|
||||
const formData = new FormData();
|
||||
formData.append('email', payload.email);
|
||||
formData.append('password', payload.password);
|
||||
if(payload.device_token) {
|
||||
formData.append('device_token', String(payload.device_token));
|
||||
if(payload.fcm_token) {
|
||||
formData.append('fcm_token', String(payload.fcm_token));
|
||||
}
|
||||
if(payload.device_id) {
|
||||
formData.append('device_id', String(payload.device_id));
|
||||
}
|
||||
return await api.post<LoginResponse>('/api/stafflogin', formData);
|
||||
};
|
||||
|
||||
export const logoutApi = async (
|
||||
payload: LogoutRequest,
|
||||
): Promise<LogoutResponse> => {
|
||||
const formData = new FormData();
|
||||
formData.append('id', payload.id);
|
||||
formData.append('fcm_token', payload.fcm_token);
|
||||
return await api.post<LogoutResponse>('api/stafflogin/logout', formData);
|
||||
};
|
||||
|
||||
10
app/api/dashboardLeadCountApi.ts
Normal file
10
app/api/dashboardLeadCountApi.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { DashboardLeadCountItem, DashboardLeadCountPayload } from '@interfaces';
|
||||
import { api } from '@utils';
|
||||
|
||||
export const getDashboardLeadCountApi = async (
|
||||
payload: DashboardLeadCountPayload,
|
||||
): Promise<DashboardLeadCountItem[]> => {
|
||||
return await api.get<DashboardLeadCountItem[]>(
|
||||
`/api/lead_count_dashboard/${payload.action}/${payload.staffid}`,
|
||||
);
|
||||
};
|
||||
@ -4,5 +4,8 @@ export * from './leadDetailsApi';
|
||||
export * from './listApi';
|
||||
export * from './customersApi';
|
||||
export * from './fcmTokenApi';
|
||||
export * from './notificationApi';
|
||||
export * from './dashboardLeadCountApi';
|
||||
|
||||
|
||||
|
||||
|
||||
24
app/api/notificationApi.ts
Normal file
24
app/api/notificationApi.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { NotificationItem, MarkNotificationReadResponse, ClearNotificationsResponse } from '@interfaces';
|
||||
import { api } from '@utils';
|
||||
|
||||
export const getStaffNotificationsApi = async (
|
||||
staffId: string,
|
||||
): Promise<NotificationItem[]> => {
|
||||
const url = `/api/staff_notification/${staffId}`;
|
||||
return await api.get<NotificationItem[]>(url);
|
||||
};
|
||||
|
||||
export const markNotificationReadApi = async (
|
||||
staffId: string,
|
||||
notificationId: string,
|
||||
): Promise<MarkNotificationReadResponse> => {
|
||||
const url = `/api/staff_notification_read/${staffId}/${notificationId}`;
|
||||
return await api.post<MarkNotificationReadResponse>(url, {});
|
||||
};
|
||||
|
||||
export const clearNotificationsApi = async (
|
||||
staffId: string,
|
||||
): Promise<ClearNotificationsResponse> => {
|
||||
const url = `/api/staff_notification_clear/${staffId}`;
|
||||
return await api.post<ClearNotificationsResponse>(url, {});
|
||||
};
|
||||
@ -12,6 +12,8 @@ export * from './statCard';
|
||||
export * from './profileInfoRow';
|
||||
export * from './customerItemCard';
|
||||
export * from './customerDetailHeader';
|
||||
export * from './notificationItem';
|
||||
export * from './leadDistribution';
|
||||
|
||||
|
||||
|
||||
|
||||
3
app/components/leadDistribution/index.ts
Normal file
3
app/components/leadDistribution/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './leadDistribution';
|
||||
export * from './leadDistribution.props';
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
import { DashboardLeadCountItem } from '@interfaces';
|
||||
|
||||
export interface LeadDistributionProps {
|
||||
data: DashboardLeadCountItem[];
|
||||
loading?: boolean;
|
||||
activeAction: string;
|
||||
onActionChange: (action: string) => void;
|
||||
}
|
||||
|
||||
224
app/components/leadDistribution/leadDistribution.styles.ts
Normal file
224
app/components/leadDistribution/leadDistribution.styles.ts
Normal file
@ -0,0 +1,224 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { ThemeColors, colors as globalColors } from '../../theme';
|
||||
|
||||
export const getStyles = (colors: ThemeColors, isDark: boolean) =>
|
||||
StyleSheet.create({
|
||||
card: {
|
||||
backgroundColor: colors.card,
|
||||
borderRadius: 20,
|
||||
marginBottom: 20,
|
||||
shadowColor: isDark ? '#000000' : '#0F172A',
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: isDark ? 0.25 : 0.08,
|
||||
shadowRadius: 12,
|
||||
elevation: 4,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
},
|
||||
|
||||
/* ── Header ── */
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal: 16,
|
||||
paddingTop: 16,
|
||||
paddingBottom: 14,
|
||||
},
|
||||
titleRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
},
|
||||
titleIconBadge: {
|
||||
width: 34,
|
||||
height: 34,
|
||||
borderRadius: 10,
|
||||
backgroundColor: globalColors.primary,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
shadowColor: globalColors.primary,
|
||||
shadowOffset: { width: 0, height: 3 },
|
||||
shadowOpacity: 0.4,
|
||||
shadowRadius: 6,
|
||||
elevation: 4,
|
||||
},
|
||||
title: {
|
||||
fontSize: 15,
|
||||
fontWeight: '700',
|
||||
color: colors.text,
|
||||
letterSpacing: 0.1,
|
||||
},
|
||||
subtitle: {
|
||||
fontSize: 11,
|
||||
fontWeight: '500',
|
||||
color: colors.textMuted,
|
||||
marginTop: 1,
|
||||
letterSpacing: 0.2,
|
||||
textTransform: 'lowercase',
|
||||
},
|
||||
|
||||
/* ── Dropdown Pill ── */
|
||||
dropdown: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 5,
|
||||
paddingHorizontal: 11,
|
||||
paddingVertical: 7,
|
||||
borderRadius: 20,
|
||||
borderWidth: 1.5,
|
||||
borderColor: isDark
|
||||
? `${globalColors.primary}55`
|
||||
: `${globalColors.primary}33`,
|
||||
backgroundColor: isDark
|
||||
? `${globalColors.primary}18`
|
||||
: `${globalColors.primary}0D`,
|
||||
},
|
||||
dropdownDot: {
|
||||
width: 6,
|
||||
height: 6,
|
||||
borderRadius: 3,
|
||||
backgroundColor: globalColors.primary,
|
||||
},
|
||||
dropdownText: {
|
||||
fontSize: 12,
|
||||
fontWeight: '700',
|
||||
color: globalColors.primary,
|
||||
letterSpacing: 0.2,
|
||||
},
|
||||
|
||||
/* ── Divider ── */
|
||||
divider: {
|
||||
height: 1,
|
||||
backgroundColor: colors.border,
|
||||
marginHorizontal: 16,
|
||||
marginBottom: 16,
|
||||
opacity: isDark ? 0.5 : 0.8,
|
||||
},
|
||||
|
||||
/* ── Body ── */
|
||||
body: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: 16,
|
||||
paddingBottom: 16,
|
||||
},
|
||||
chartContainer: {
|
||||
width: 140,
|
||||
height: 140,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
position: 'relative',
|
||||
},
|
||||
loaderContainer: {
|
||||
flex: 1,
|
||||
height: 140,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
centerLabelContainer: {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
centerNumber: {
|
||||
fontSize: 24,
|
||||
fontWeight: '800',
|
||||
color: colors.text,
|
||||
lineHeight: 28,
|
||||
},
|
||||
centerText: {
|
||||
fontSize: 12,
|
||||
color: colors.textMuted,
|
||||
fontWeight: '600',
|
||||
marginTop: 2,
|
||||
},
|
||||
|
||||
/* ── Legend ── */
|
||||
legendContainer: {
|
||||
flex: 1,
|
||||
marginLeft: 16,
|
||||
maxHeight: 140,
|
||||
},
|
||||
legendItem: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingVertical: 7,
|
||||
},
|
||||
legendItemLeft: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
},
|
||||
bullet: {
|
||||
width: 10,
|
||||
height: 10,
|
||||
borderRadius: 5,
|
||||
marginRight: 8,
|
||||
},
|
||||
legendLabel: {
|
||||
fontSize: 13,
|
||||
fontWeight: '600',
|
||||
color: colors.text,
|
||||
},
|
||||
legendItemRight: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
width: 76,
|
||||
},
|
||||
legendCount: {
|
||||
fontSize: 13,
|
||||
fontWeight: '700',
|
||||
color: colors.text,
|
||||
textAlign: 'right',
|
||||
width: 32,
|
||||
},
|
||||
legendPercentage: {
|
||||
fontSize: 12,
|
||||
color: colors.textMuted,
|
||||
textAlign: 'right',
|
||||
width: 44,
|
||||
},
|
||||
|
||||
/* ── Dropdown Menu ── */
|
||||
menuOverlay: {
|
||||
position: 'absolute',
|
||||
top: 52,
|
||||
right: 14,
|
||||
backgroundColor: colors.card,
|
||||
borderRadius: 12,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.border,
|
||||
zIndex: 9999,
|
||||
elevation: 20,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 6 },
|
||||
shadowOpacity: isDark ? 0.35 : 0.12,
|
||||
shadowRadius: 12,
|
||||
minWidth: 120,
|
||||
},
|
||||
menuItem: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingVertical: 11,
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
menuItemActive: {
|
||||
backgroundColor: isDark
|
||||
? `${globalColors.primary}25`
|
||||
: `${globalColors.primary}0F`,
|
||||
},
|
||||
menuItemText: {
|
||||
fontSize: 13,
|
||||
fontWeight: '600',
|
||||
color: colors.textSecondary,
|
||||
},
|
||||
menuItemTextActive: {
|
||||
color: globalColors.primary,
|
||||
fontWeight: '700',
|
||||
},
|
||||
});
|
||||
|
||||
166
app/components/leadDistribution/leadDistribution.tsx
Normal file
166
app/components/leadDistribution/leadDistribution.tsx
Normal file
@ -0,0 +1,166 @@
|
||||
import React, { useState } from 'react';
|
||||
import { View, Text, TouchableOpacity, ActivityIndicator, ScrollView } from 'react-native';
|
||||
import { PieChart } from 'react-native-gifted-charts';
|
||||
import Icon from 'react-native-vector-icons/Ionicons';
|
||||
import { useTheme } from '@theme';
|
||||
import { getStyles } from './leadDistribution.styles';
|
||||
import { LeadDistributionProps } from './leadDistribution.props';
|
||||
|
||||
export const LeadDistribution = ({
|
||||
data = [],
|
||||
loading = false,
|
||||
activeAction,
|
||||
onActionChange,
|
||||
}: LeadDistributionProps) => {
|
||||
const { theme: colors, isDark } = useTheme();
|
||||
const styles = getStyles(colors, isDark);
|
||||
const [menuVisible, setMenuVisible] = useState(false);
|
||||
|
||||
const actions = [
|
||||
{ label: 'Day', value: 'day' },
|
||||
{ label: 'Week', value: 'week' },
|
||||
{ label: 'Month', value: 'month' },
|
||||
{ label: 'Quarter', value: 'quarter' },
|
||||
{ label: 'Year', value: 'year' },
|
||||
];
|
||||
|
||||
const currentActionLabel =
|
||||
actions.find((a) => a.value === activeAction)?.label || 'Month';
|
||||
|
||||
const totalLeadItem = data.find((item) => item.name === 'Total Lead');
|
||||
const totalCount = totalLeadItem
|
||||
? totalLeadItem.count
|
||||
: data.reduce((sum, item) => sum + item.count, 0);
|
||||
|
||||
const displayData = data
|
||||
.filter((item) => item.name !== 'Total Lead' && item.count > 0)
|
||||
.map((item) => ({
|
||||
...item,
|
||||
percentage: totalCount > 0 ? Math.round((item.count / totalCount) * 100) : 0,
|
||||
}));
|
||||
|
||||
const pieData = displayData.map((item) => ({
|
||||
value: item.count,
|
||||
color: item.color,
|
||||
}));
|
||||
|
||||
const renderCenterLabel = () => {
|
||||
return (
|
||||
<View style={styles.centerLabelContainer}>
|
||||
<Text style={styles.centerNumber}>{totalCount}</Text>
|
||||
<Text style={styles.centerText}>Total</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.card}>
|
||||
{/* Header */}
|
||||
<View style={styles.header}>
|
||||
<View style={styles.titleRow}>
|
||||
<View style={styles.titleIconBadge}>
|
||||
<Icon name="pie-chart" size={15} color="#FFFFFF" />
|
||||
</View>
|
||||
<View>
|
||||
<Text style={styles.title}>Lead Distribution</Text>
|
||||
<Text style={styles.subtitle}>by status · {currentActionLabel}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<TouchableOpacity
|
||||
style={styles.dropdown}
|
||||
onPress={() => setMenuVisible(!menuVisible)}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<View style={styles.dropdownDot} />
|
||||
<Text style={styles.dropdownText}>{currentActionLabel}</Text>
|
||||
<Icon
|
||||
name={menuVisible ? 'chevron-up' : 'chevron-down'}
|
||||
size={13}
|
||||
color={colors.icon}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* Dropdown Menu */}
|
||||
{menuVisible && (
|
||||
<View style={styles.menuOverlay}>
|
||||
{actions.map((act) => {
|
||||
const isSelected = act.value === activeAction;
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={act.value}
|
||||
style={[styles.menuItem, isSelected && styles.menuItemActive]}
|
||||
onPress={() => {
|
||||
onActionChange(act.value);
|
||||
setMenuVisible(false);
|
||||
}}
|
||||
>
|
||||
<Text style={[styles.menuItemText, isSelected && styles.menuItemTextActive]}>
|
||||
{act.label}
|
||||
</Text>
|
||||
{isSelected && (
|
||||
<Icon name="checkmark" size={14} color={colors.icon} />
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* Divider */}
|
||||
<View style={styles.divider} />
|
||||
|
||||
{/* Body — fixed min-height so card never jumps */}
|
||||
<View style={styles.body}>
|
||||
{loading ? (
|
||||
<View style={styles.loaderContainer}>
|
||||
<ActivityIndicator size="large" color={colors.icon} />
|
||||
</View>
|
||||
) : displayData.length === 0 ? (
|
||||
<View style={styles.loaderContainer}>
|
||||
<Text style={{ color: colors.textSecondary, fontSize: 13 }}>
|
||||
No lead data available
|
||||
</Text>
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
<View style={styles.chartContainer}>
|
||||
<PieChart
|
||||
data={pieData}
|
||||
donut
|
||||
radius={60}
|
||||
innerRadius={45}
|
||||
innerCircleColor={colors.card}
|
||||
centerLabelComponent={renderCenterLabel}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<ScrollView
|
||||
style={styles.legendContainer}
|
||||
showsVerticalScrollIndicator={false}
|
||||
nestedScrollEnabled
|
||||
>
|
||||
{displayData.map((item, idx) => (
|
||||
<View key={idx} style={styles.legendItem}>
|
||||
<View style={styles.legendItemLeft}>
|
||||
<View style={[styles.bullet, { backgroundColor: item.color }]} />
|
||||
<Text style={styles.legendLabel} numberOfLines={1}>
|
||||
{item.name}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.legendItemRight}>
|
||||
<Text style={styles.legendCount}>{item.count}</Text>
|
||||
<Text style={styles.legendPercentage}>{item.percentage}%</Text>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
1
app/components/notificationItem/index.ts
Normal file
1
app/components/notificationItem/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './notificationItem';
|
||||
63
app/components/notificationItem/notificationItem.styles.ts
Normal file
63
app/components/notificationItem/notificationItem.styles.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { ThemeColors } from '@theme';
|
||||
|
||||
export const getStyles = (colors: ThemeColors) =>
|
||||
StyleSheet.create({
|
||||
card: {
|
||||
backgroundColor: colors.surface,
|
||||
paddingVertical: 14,
|
||||
paddingHorizontal: 16,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.border,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
unreadCard: {
|
||||
backgroundColor: `${colors.icon}06`,
|
||||
},
|
||||
iconWrapper: {
|
||||
width: 36,
|
||||
height: 36,
|
||||
borderRadius: 18,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginRight: 12,
|
||||
marginTop: 2,
|
||||
},
|
||||
profileImage: {
|
||||
width: 36,
|
||||
height: 36,
|
||||
borderRadius: 18,
|
||||
},
|
||||
contentContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
titleRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginBottom: 3,
|
||||
},
|
||||
title: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
color: colors.text,
|
||||
flex: 1,
|
||||
},
|
||||
unreadDot: {
|
||||
width: 6,
|
||||
height: 6,
|
||||
borderRadius: 3,
|
||||
backgroundColor: '#EF4444',
|
||||
marginLeft: 6,
|
||||
},
|
||||
description: {
|
||||
fontSize: 13,
|
||||
color: colors.textSecondary,
|
||||
lineHeight: 18,
|
||||
marginBottom: 6,
|
||||
},
|
||||
time: {
|
||||
fontSize: 11,
|
||||
color: colors.textMuted,
|
||||
},
|
||||
});
|
||||
56
app/components/notificationItem/notificationItem.tsx
Normal file
56
app/components/notificationItem/notificationItem.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import React from 'react';
|
||||
import { View, Text, TouchableOpacity, Image } from 'react-native';
|
||||
import Icon from 'react-native-vector-icons/Ionicons';
|
||||
import { useTheme } from '@theme';
|
||||
import { getStyles } from './notificationItem.styles';
|
||||
import { NotificationItem as NotificationItemType } from '@interfaces';
|
||||
import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
interface NotificationItemProps {
|
||||
item: NotificationItemType;
|
||||
onPress?: (item: NotificationItemType) => void;
|
||||
}
|
||||
|
||||
export const NotificationItem: React.FC<NotificationItemProps> = ({ item, onPress }) => {
|
||||
const { theme: colors } = useTheme();
|
||||
const styles = getStyles(colors);
|
||||
|
||||
const isUnread = String(item.isread) === '0';
|
||||
const cleanImageUrl = item.profile_image ? item.profile_image.replace(/"/g, '').trim() : '';
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={[styles.card, isUnread && styles.unreadCard]}
|
||||
activeOpacity={0.7}
|
||||
onPress={() => onPress && onPress(item)}
|
||||
>
|
||||
<View style={styles.iconWrapper}>
|
||||
{cleanImageUrl ? (
|
||||
<Image
|
||||
source={{ uri: cleanImageUrl }}
|
||||
style={styles.profileImage}
|
||||
resizeMode="cover"
|
||||
/>
|
||||
) : (
|
||||
<Icon name="person-circle-outline" size={38} color={colors.icon ?? '#888'} />
|
||||
)}
|
||||
</View>
|
||||
|
||||
<View style={styles.contentContainer}>
|
||||
<View style={styles.titleRow}>
|
||||
<Text style={styles.title} numberOfLines={1}>
|
||||
{item.from_fullname || 'Notification'}
|
||||
</Text>
|
||||
<Text style={styles.time}>{dayjs(item.full_date || item.date).fromNow()}</Text>
|
||||
{isUnread && <View style={styles.unreadDot} />}
|
||||
</View>
|
||||
<Text style={styles.description} numberOfLines={2}>
|
||||
{item.description}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
@ -1,124 +1,58 @@
|
||||
/* eslint-disable react-native/no-inline-styles */
|
||||
import React from 'react';
|
||||
import { Text, View, ScrollView, TouchableOpacity } 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 React, { useEffect, useState } from 'react';
|
||||
import { ScrollView, View, Text, InteractionManager } from 'react-native';
|
||||
import { getStyles } from './dashboard.styles';
|
||||
import { statCards } from '../../mock-data/dashboard';
|
||||
import { useTheme } from '../../theme';
|
||||
import { LeadDistribution } from '@components';
|
||||
import { useAppDispatch, useAppSelector, RootState } from '@store';
|
||||
import { getDashboardLeadCount } from './thunk';
|
||||
import { NotificationService } from '@services';
|
||||
|
||||
export const DashboardScreen = () => {
|
||||
const navigation = useNavigation<any>();
|
||||
const dispatch = useAppDispatch();
|
||||
const { theme: colors } = useTheme();
|
||||
const styles = getStyles(colors);
|
||||
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
await AsyncStorage.removeItem('userToken');
|
||||
await AsyncStorage.removeItem('tenancyName');
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
navigation.reset({
|
||||
index: 0,
|
||||
routes: [{ name: 'AuthStack' }], // root-level stack name stays as-is
|
||||
});
|
||||
};
|
||||
const [selectedAction, setSelectedAction] = useState<string>('year');
|
||||
|
||||
const user_data = useAppSelector((state: RootState) => state.auth.user_data);
|
||||
const { leadCountData, loading, error } = useAppSelector(
|
||||
(state: RootState) => state.dashboard,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const task = InteractionManager.runAfterInteractions(() => {
|
||||
NotificationService.requestPermission();
|
||||
});
|
||||
return () => task.cancel();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (user_data?.staffid) {
|
||||
dispatch(
|
||||
getDashboardLeadCount({
|
||||
action: selectedAction,
|
||||
staffid: user_data.staffid,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}, [dispatch, selectedAction, user_data]);
|
||||
return (
|
||||
<ScrollView style={styles.container} contentContainerStyle={styles.content}>
|
||||
{/* Hero Welcome banner */}
|
||||
<View style={styles.heroCard}>
|
||||
<View>
|
||||
<Text style={styles.heroGreeting}>Welcome Back,</Text>
|
||||
<Text style={styles.heroName}>Workspace Admin</Text>
|
||||
<Text style={styles.heroInfo}>Convex CRM • Active session</Text>
|
||||
</View>
|
||||
<TouchableOpacity style={styles.logoutButton} onPress={handleLogout}>
|
||||
<Icon name="log-out-outline" size={22} color="#EF4444" />
|
||||
</TouchableOpacity>
|
||||
{error && (
|
||||
<View style={styles.errorContainer}>
|
||||
<Text style={styles.errorText}>{error}</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* Grid of Stats */}
|
||||
<Text style={styles.sectionTitle}>Overview Metrics</Text>
|
||||
<View style={styles.statsGrid}>
|
||||
{statCards.map((stat, idx) => (
|
||||
<View key={idx} style={styles.statCard}>
|
||||
<View style={styles.statHeader}>
|
||||
<View
|
||||
style={[
|
||||
styles.statIconContainer,
|
||||
{ backgroundColor: `${stat.color}15` },
|
||||
]}
|
||||
>
|
||||
<Icon name={stat.icon} size={20} color={stat.color} />
|
||||
</View>
|
||||
<Text
|
||||
style={[
|
||||
styles.statChange,
|
||||
{
|
||||
color: stat.change.startsWith('+') ? '#10B981' : '#EF4444',
|
||||
},
|
||||
]}
|
||||
>
|
||||
{stat.change}
|
||||
</Text>
|
||||
</View>
|
||||
<Text style={styles.statCount}>{stat.count}</Text>
|
||||
<Text style={styles.statTitle}>{stat.title}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
|
||||
{/* Recent Activity Section */}
|
||||
<View style={styles.sectionHeader}>
|
||||
<Text style={styles.sectionTitle}>Recent Activity</Text>
|
||||
<TouchableOpacity>
|
||||
<Text style={styles.seeAllLink}>See All</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<View style={styles.activityList}>
|
||||
<View style={styles.activityItem}>
|
||||
<View style={[styles.activityBadge, { backgroundColor: '#10B981' }]}>
|
||||
<Icon name="person-add" size={14} color="#FFF" />
|
||||
</View>
|
||||
<View style={styles.activityDetails}>
|
||||
<Text style={styles.activityTitle}>New Lead Created</Text>
|
||||
<Text style={styles.activityDesc}>
|
||||
Sarah Jenkins added by System Import
|
||||
</Text>
|
||||
<Text style={styles.activityTime}>2 minutes ago</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.activityItem}>
|
||||
<View style={[styles.activityBadge, { backgroundColor: '#3B82F6' }]}>
|
||||
<Icon name="document-text" size={14} color="#FFF" />
|
||||
</View>
|
||||
<View style={styles.activityDetails}>
|
||||
<Text style={styles.activityTitle}>Invoice #1042 Sent</Text>
|
||||
<Text style={styles.activityDesc}>
|
||||
Sent to Acme Corp for $1,250.00
|
||||
</Text>
|
||||
<Text style={styles.activityTime}>1 hour ago</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.activityItem}>
|
||||
<View style={[styles.activityBadge, { backgroundColor: '#F59E0B' }]}>
|
||||
<Icon name="chatbubble-ellipses" size={14} color="#FFF" />
|
||||
</View>
|
||||
<View style={styles.activityDetails}>
|
||||
<Text style={styles.activityTitle}>Ticket #2045 Replied</Text>
|
||||
<Text style={styles.activityDesc}>
|
||||
Support agent responded to email query
|
||||
</Text>
|
||||
<Text style={styles.activityTime}>3 hours ago</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
{/* Lead Distribution Chart Card */}
|
||||
<LeadDistribution
|
||||
data={leadCountData}
|
||||
loading={loading}
|
||||
activeAction={selectedAction}
|
||||
onActionChange={setSelectedAction}
|
||||
/>
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -9,149 +9,21 @@ export const getStyles = (colors: ThemeColors) => StyleSheet.create({
|
||||
content: {
|
||||
padding: 16,
|
||||
},
|
||||
heroCard: {
|
||||
backgroundColor: colors.drawerHeader,
|
||||
borderRadius: 16,
|
||||
padding: 20,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: 24,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 6 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 8,
|
||||
elevation: 4,
|
||||
},
|
||||
heroGreeting: {
|
||||
fontSize: 14,
|
||||
color: '#94A3B8',
|
||||
},
|
||||
heroName: {
|
||||
fontSize: 22,
|
||||
fontWeight: '800',
|
||||
color: '#FFFFFF',
|
||||
marginTop: 2,
|
||||
},
|
||||
heroInfo: {
|
||||
fontSize: 12,
|
||||
color: colors.icon,
|
||||
marginTop: 8,
|
||||
fontWeight: '600',
|
||||
},
|
||||
logoutButton: {
|
||||
width: 44,
|
||||
height: 44,
|
||||
borderRadius: 22,
|
||||
backgroundColor: 'rgba(255,255,255,0.1)',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
sectionHeader: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: 12,
|
||||
marginTop: 8,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: '700',
|
||||
color: colors.text,
|
||||
marginBottom: 12,
|
||||
},
|
||||
seeAllLink: {
|
||||
fontSize: 14,
|
||||
color: colors.icon,
|
||||
fontWeight: '600',
|
||||
},
|
||||
statsGrid: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: 20,
|
||||
},
|
||||
statCard: {
|
||||
backgroundColor: colors.card,
|
||||
borderRadius: 14,
|
||||
padding: 16,
|
||||
width: '48%',
|
||||
errorContainer: {
|
||||
padding: 12,
|
||||
backgroundColor: 'rgba(239, 68, 68, 0.1)',
|
||||
borderRadius: 8,
|
||||
marginBottom: 16,
|
||||
shadowColor: '#0F172A',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 4,
|
||||
elevation: 2,
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(239, 68, 68, 0.3)',
|
||||
},
|
||||
statHeader: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: 12,
|
||||
},
|
||||
statIconContainer: {
|
||||
width: 36,
|
||||
height: 36,
|
||||
borderRadius: 10,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
statChange: {
|
||||
fontSize: 12,
|
||||
fontWeight: '700',
|
||||
},
|
||||
statCount: {
|
||||
fontSize: 22,
|
||||
fontWeight: '800',
|
||||
color: colors.text,
|
||||
},
|
||||
statTitle: {
|
||||
fontSize: 13,
|
||||
color: colors.textSecondary,
|
||||
marginTop: 4,
|
||||
},
|
||||
activityList: {
|
||||
backgroundColor: colors.card,
|
||||
borderRadius: 16,
|
||||
padding: 16,
|
||||
shadowColor: '#0F172A',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.05,
|
||||
shadowRadius: 4,
|
||||
elevation: 2,
|
||||
},
|
||||
activityItem: {
|
||||
flexDirection: 'row',
|
||||
marginBottom: 16,
|
||||
},
|
||||
activityBadge: {
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: 14,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginTop: 2,
|
||||
},
|
||||
activityDetails: {
|
||||
marginLeft: 12,
|
||||
flex: 1,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.border,
|
||||
paddingBottom: 12,
|
||||
},
|
||||
activityTitle: {
|
||||
errorText: {
|
||||
color: '#EF4444',
|
||||
fontSize: 14,
|
||||
fontWeight: '700',
|
||||
color: colors.text,
|
||||
},
|
||||
activityDesc: {
|
||||
fontSize: 13,
|
||||
color: colors.textSecondary,
|
||||
marginTop: 2,
|
||||
},
|
||||
activityTime: {
|
||||
fontSize: 11,
|
||||
color: colors.textMuted,
|
||||
marginTop: 6,
|
||||
textAlign: 'center',
|
||||
fontWeight: '500',
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
42
app/features/dashboard/reducers.ts
Normal file
42
app/features/dashboard/reducers.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { createReducer } from '@reduxjs/toolkit';
|
||||
import { DashboardLeadCountItem } from '@interfaces';
|
||||
import { getDashboardLeadCount, resetDashboardState } from './thunk';
|
||||
|
||||
export interface DashboardState {
|
||||
leadCountData: DashboardLeadCountItem[];
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
const initialState: DashboardState = {
|
||||
leadCountData: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
export const dashboardReducer = createReducer(initialState, builder => {
|
||||
builder
|
||||
.addCase(getDashboardLeadCount.pending, acc => {
|
||||
acc.loading = true;
|
||||
acc.error = null;
|
||||
})
|
||||
.addCase(getDashboardLeadCount.fulfilled, (acc, action) => {
|
||||
acc.loading = false;
|
||||
acc.leadCountData = action.payload;
|
||||
acc.error = null;
|
||||
})
|
||||
.addCase(getDashboardLeadCount.rejected, (acc, action) => {
|
||||
acc.loading = false;
|
||||
acc.error =
|
||||
(action.payload as string) ??
|
||||
action.error.message ??
|
||||
'Failed to load dashboard lead count';
|
||||
})
|
||||
.addCase(resetDashboardState, acc => {
|
||||
acc.leadCountData = [];
|
||||
acc.loading = false;
|
||||
acc.error = null;
|
||||
});
|
||||
});
|
||||
|
||||
export default dashboardReducer;
|
||||
19
app/features/dashboard/thunk.ts
Normal file
19
app/features/dashboard/thunk.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { createAction, createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import { getDashboardLeadCountApi } from '@api';
|
||||
import { DashboardLeadCountItem, DashboardLeadCountPayload } from '@interfaces';
|
||||
|
||||
export const resetDashboardState = createAction('dashboard/resetState');
|
||||
|
||||
export const getDashboardLeadCount = createAsyncThunk<
|
||||
DashboardLeadCountItem[],
|
||||
DashboardLeadCountPayload
|
||||
>(
|
||||
'dashboard/getLeadCount',
|
||||
async (payload, { rejectWithValue }) => {
|
||||
try {
|
||||
return await getDashboardLeadCountApi(payload);
|
||||
} catch (error: any) {
|
||||
return rejectWithValue(error.message || 'Failed to load dashboard lead count');
|
||||
}
|
||||
},
|
||||
);
|
||||
@ -9,19 +9,18 @@ import {
|
||||
ImageBackground,
|
||||
ActivityIndicator,
|
||||
StatusBar,
|
||||
Keyboard,
|
||||
} 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, RootState, login, sendFcmToken } from '@store';
|
||||
import { useAppDispatch, useAppSelector, RootState, login } from '@store';
|
||||
import { LoginRequest } from '@interfaces';
|
||||
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';
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
|
||||
export const LoginScreen = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
@ -36,7 +35,7 @@ export const LoginScreen = () => {
|
||||
const [focusedField, setFocusedField] = useState<string | null>(null);
|
||||
const scrollViewRef = useRef<ScrollView>(null);
|
||||
const [localError, setLocalError] = useState('');
|
||||
const [deviceToken, setDeviceToken] = useState<string | null>(null);
|
||||
// const [deviceToken, setDeviceToken] = useState<string | null>(null);
|
||||
|
||||
const { loginLoading, loginError, loginSuccess, token, user_data } =
|
||||
useAppSelector((state: RootState) => state.auth);
|
||||
@ -62,30 +61,32 @@ export const LoginScreen = () => {
|
||||
}
|
||||
|
||||
const fcmToken = await NotificationService.getFCMToken();
|
||||
setDeviceToken(fcmToken);
|
||||
console.log('deviceToken-', fcmToken)
|
||||
const deviceId = await DeviceInfo.getUniqueId();
|
||||
console.log('fcm_token-', fcmToken);
|
||||
console.log('device_id-', deviceId);
|
||||
|
||||
const payload: LoginRequest = {
|
||||
email,
|
||||
password,
|
||||
device_token: fcmToken ?? '',
|
||||
fcm_token: fcmToken ?? '',
|
||||
device_id: deviceId ?? '',
|
||||
};
|
||||
console.log('payload', payload)
|
||||
// Build the full tenancy base URL and persist it in AsyncStorage
|
||||
const cleanTenancy = tenancy.trim().replace(/^(https?:\/\/)?/, '');
|
||||
const fullTenancy = `https://${cleanTenancy}`;
|
||||
console.log('payload-', payload)
|
||||
console.log('tenancy-', config.BASE_URL)
|
||||
if (fullTenancy === config.BASE_URL) {
|
||||
console.log('baseUrl set to-', fullTenancy);
|
||||
|
||||
try {
|
||||
await AsyncStorage.setItem('base_url', fullTenancy);
|
||||
await AsyncStorage.setItem('tenancy_name', cleanTenancy);
|
||||
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(() => {
|
||||
@ -123,15 +124,13 @@ export const LoginScreen = () => {
|
||||
useEffect(() => {
|
||||
if (loginSuccess && token && user_data) {
|
||||
// Send FCM token to server immediately after login
|
||||
if (deviceToken && user_data.staffid) {
|
||||
dispatch(sendFcmToken({ id: user_data.staffid, fcm_token: deviceToken }));
|
||||
}
|
||||
// if (deviceToken && user_data.staffid) {
|
||||
// dispatch(sendFcmToken({ id: user_data.staffid, fcm_token: deviceToken }));
|
||||
// }
|
||||
navigation.reset({
|
||||
index: 0,
|
||||
routes: [{ name: 'DrawerStack' }],
|
||||
});
|
||||
// Show permission popup after the user has landed in the app
|
||||
setTimeout(() => NotificationService.requestPermission(), 1000);
|
||||
}
|
||||
}, [loginSuccess, token, user_data, navigation]);
|
||||
|
||||
|
||||
@ -1,105 +1,98 @@
|
||||
import React, { useLayoutEffect, useState } from 'react';
|
||||
import React, { useEffect, useLayoutEffect } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
FlatList,
|
||||
TouchableOpacity,
|
||||
StatusBar,
|
||||
} from 'react-native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import Icon from 'react-native-vector-icons/Ionicons';
|
||||
import { useTheme } from '../../theme';
|
||||
import { useTheme } from '@theme';
|
||||
import { getStyles } from './notification.styles';
|
||||
import { INITIAL_NOTIFICATIONS } from '@mock-data';
|
||||
import { useAppDispatch, useAppSelector, RootState } from '@store';
|
||||
import { getStaffNotifications, markNotificationRead, clearNotifications } from './thunk';
|
||||
import { NotificationItem as NotificationItemType } from '@interfaces';
|
||||
import { Loader, NotificationItem } from '@components';
|
||||
|
||||
export const NotificationScreen = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const navigation = useNavigation();
|
||||
const { theme: colors } = useTheme();
|
||||
const styles = getStyles(colors);
|
||||
|
||||
const [notifications, setNotifications] = useState(INITIAL_NOTIFICATIONS);
|
||||
|
||||
// Mark all as read header button action
|
||||
const handleMarkAllRead = () => {
|
||||
setNotifications(prev =>
|
||||
prev.map(item => ({ ...item, unread: false }))
|
||||
const { items, loading, error } = useAppSelector(
|
||||
(state: RootState) => state.notifications,
|
||||
);
|
||||
};
|
||||
const user_data = useAppSelector((state: RootState) => state.auth.user_data);
|
||||
|
||||
useEffect(() => {
|
||||
if (user_data?.staffid) {
|
||||
dispatch(getStaffNotifications(user_data.staffid));
|
||||
}
|
||||
}, [dispatch, user_data]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerTitle: 'Notifications',
|
||||
headerRight: () => {
|
||||
const hasUnread = notifications.some(n => n.unread);
|
||||
const hasUnread = items.some(n => String(n.isread) === '0');
|
||||
if (!hasUnread) return null;
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={styles.headerRightBtn}
|
||||
onPress={handleMarkAllRead}
|
||||
onPress={() => {
|
||||
if (user_data?.staffid) {
|
||||
dispatch(clearNotifications(user_data.staffid));
|
||||
}
|
||||
}}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Text style={styles.clearAllText}>Read All</Text>
|
||||
<Text style={styles.clearAllText}>Clear All</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
},
|
||||
});
|
||||
}, [navigation, notifications, styles]);
|
||||
}, [navigation, items, styles]);
|
||||
|
||||
const toggleSingleRead = (id: string) => {
|
||||
setNotifications(prev =>
|
||||
prev.map(item =>
|
||||
item.id === id ? { ...item, unread: !item.unread } : item
|
||||
)
|
||||
const handleNotificationPress = (item: NotificationItemType) => {
|
||||
if (String(item.isread) === '0' && user_data?.staffid) {
|
||||
dispatch(
|
||||
markNotificationRead({
|
||||
staffId: user_data.staffid,
|
||||
notificationId: item.id,
|
||||
}),
|
||||
);
|
||||
}
|
||||
// Note: You can add navigation logic here if `item.link` is provided
|
||||
};
|
||||
|
||||
const renderItem = ({ item }: { item: typeof INITIAL_NOTIFICATIONS[0] }) => {
|
||||
const renderItem = ({ item }: { item: NotificationItemType }) => {
|
||||
return <NotificationItem item={item} onPress={handleNotificationPress} />;
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <Loader message="Loading notifications..." />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={[styles.card, item.unread && styles.unreadCard]}
|
||||
activeOpacity={0.7}
|
||||
onPress={() => toggleSingleRead(item.id)}
|
||||
>
|
||||
{/* Left Side Styled Icon Circle */}
|
||||
<View style={[styles.iconWrapper, { backgroundColor: `${item.iconColor}12` }]}>
|
||||
<Icon name={item.iconName} size={18} color={item.iconColor} />
|
||||
<View style={styles.emptyContainer}>
|
||||
<Icon name="alert-circle-outline" size={48} color={colors.textMuted} />
|
||||
<Text style={styles.emptyTitle}>{error}</Text>
|
||||
{/* <Text style={styles.emptySubtitle}>{error}</Text> */}
|
||||
</View>
|
||||
|
||||
{/* Content Section */}
|
||||
<View style={styles.contentContainer}>
|
||||
<View style={styles.titleRow}>
|
||||
<Text style={styles.title} numberOfLines={1}>
|
||||
{item.title}
|
||||
</Text>
|
||||
{item.unread && <View style={styles.unreadDot} />}
|
||||
</View>
|
||||
<Text style={styles.description}>{item.description}</Text>
|
||||
<Text style={styles.time}>{item.time}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{/* <StatusBar barStyle="light-content" /> */}
|
||||
{notifications.length > 0 ? (
|
||||
<FlatList
|
||||
data={notifications}
|
||||
data={items}
|
||||
keyExtractor={item => item.id}
|
||||
renderItem={renderItem}
|
||||
contentContainerStyle={styles.listContent}
|
||||
showsVerticalScrollIndicator={false}
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.emptyContainer}>
|
||||
<Icon name="notifications-off-outline" size={48} color={colors.textMuted} />
|
||||
<Text style={styles.emptyTitle}>All caught up!</Text>
|
||||
<Text style={styles.emptySubtitle}>
|
||||
When you get new updates or alerts, they will show up here.
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
63
app/features/notification/reducers.ts
Normal file
63
app/features/notification/reducers.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { createReducer } from '@reduxjs/toolkit';
|
||||
import { getStaffNotifications, markNotificationRead, clearNotifications } from './thunk';
|
||||
import { NotificationItem } from '@interfaces';
|
||||
|
||||
export interface NotificationState {
|
||||
items: NotificationItem[];
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
hasFetched: boolean;
|
||||
}
|
||||
|
||||
const initialState: NotificationState = {
|
||||
items: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
hasFetched: false,
|
||||
};
|
||||
|
||||
export const reducers = createReducer(initialState, builder => {
|
||||
builder
|
||||
.addCase(getStaffNotifications.pending, acc => {
|
||||
acc.loading = true;
|
||||
acc.error = null;
|
||||
})
|
||||
.addCase(getStaffNotifications.fulfilled, (acc, action) => {
|
||||
acc.loading = false;
|
||||
acc.items = action.payload;
|
||||
acc.error = null;
|
||||
acc.hasFetched = true;
|
||||
})
|
||||
.addCase(getStaffNotifications.rejected, (acc, action) => {
|
||||
acc.loading = false;
|
||||
acc.hasFetched = true;
|
||||
acc.error =
|
||||
(action.payload as string) ??
|
||||
action.error.message ??
|
||||
'Failed to load notifications';
|
||||
})
|
||||
.addCase(markNotificationRead.pending, (acc, action) => {
|
||||
// Optimistic update: mark as read immediately
|
||||
const item = acc.items.find(i => i.id === action.meta.arg.notificationId);
|
||||
if (item) {
|
||||
item.isread = '1';
|
||||
}
|
||||
})
|
||||
.addCase(markNotificationRead.rejected, (acc, action) => {
|
||||
// If it fails, revert it (simple approach: you could also re-fetch)
|
||||
const item = acc.items.find(i => i.id === action.meta.arg.notificationId);
|
||||
if (item) {
|
||||
item.isread = '0';
|
||||
}
|
||||
acc.error = (action.payload as string) ?? 'Failed to mark notification as read';
|
||||
})
|
||||
.addCase(clearNotifications.pending, (acc) => {
|
||||
// Optimistic update: clear items immediately
|
||||
acc.items = [];
|
||||
})
|
||||
.addCase(clearNotifications.rejected, (acc, action) => {
|
||||
acc.error = (action.payload as string) ?? 'Failed to clear notifications';
|
||||
});
|
||||
});
|
||||
|
||||
export default reducers;
|
||||
49
app/features/notification/thunk.ts
Normal file
49
app/features/notification/thunk.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import { getStaffNotificationsApi, markNotificationReadApi, clearNotificationsApi } from '@api';
|
||||
import { NotificationItem } from '@interfaces';
|
||||
|
||||
export const getStaffNotifications = createAsyncThunk<
|
||||
NotificationItem[],
|
||||
string
|
||||
>('notification/getStaffNotifications', async (staffId, { rejectWithValue }) => {
|
||||
try {
|
||||
return await getStaffNotificationsApi(staffId);
|
||||
} catch (error: any) {
|
||||
const serverMessage =
|
||||
error?.response?.data?.message ||
|
||||
error.message ||
|
||||
'Failed to load notifications';
|
||||
return rejectWithValue(serverMessage);
|
||||
}
|
||||
});
|
||||
|
||||
export const markNotificationRead = createAsyncThunk<
|
||||
{ id: string },
|
||||
{ staffId: string; notificationId: string }
|
||||
>('notification/markNotificationRead', async (payload, { rejectWithValue }) => {
|
||||
try {
|
||||
await markNotificationReadApi(payload.staffId, payload.notificationId);
|
||||
return { id: payload.notificationId };
|
||||
} catch (error: any) {
|
||||
const serverMessage =
|
||||
error?.response?.data?.message ||
|
||||
error.message ||
|
||||
'Failed to mark notification as read';
|
||||
return rejectWithValue(serverMessage);
|
||||
}
|
||||
});
|
||||
|
||||
export const clearNotifications = createAsyncThunk<
|
||||
void,
|
||||
string
|
||||
>('notification/clearNotifications', async (staffId, { rejectWithValue }) => {
|
||||
try {
|
||||
await clearNotificationsApi(staffId);
|
||||
} catch (error: any) {
|
||||
const serverMessage =
|
||||
error?.response?.data?.message ||
|
||||
error.message ||
|
||||
'Failed to clear notifications';
|
||||
return rejectWithValue(serverMessage);
|
||||
}
|
||||
});
|
||||
@ -6,6 +6,7 @@ import {
|
||||
ScrollView,
|
||||
Switch,
|
||||
Image,
|
||||
Alert,
|
||||
} from 'react-native';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
import Icon from 'react-native-vector-icons/Ionicons';
|
||||
@ -13,6 +14,7 @@ import { getStyles } from './profile.styles';
|
||||
import { useTheme } from '../../theme';
|
||||
import { ProfileInfoRow } from '@components';
|
||||
import { useAppDispatch, useAppSelector, logout, RootState } from '@store';
|
||||
import { NotificationService } from '@services';
|
||||
|
||||
export const ProfileScreen = () => {
|
||||
const navigation = useNavigation<any>();
|
||||
@ -25,14 +27,25 @@ export const ProfileScreen = () => {
|
||||
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
dispatch(logout());
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
const fcmToken = await NotificationService.getFCMToken();
|
||||
console.log('fcmToken', fcmToken)
|
||||
await dispatch(
|
||||
logout({
|
||||
id: userData?.staffid ?? '',
|
||||
fcm_token: fcmToken ?? '',
|
||||
}),
|
||||
).unwrap();
|
||||
navigation.reset({
|
||||
index: 0,
|
||||
routes: [{ name: 'AuthStack' }],
|
||||
});
|
||||
} catch (e: any) {
|
||||
const message =
|
||||
typeof e === 'string'
|
||||
? e
|
||||
: e?.message || 'Logout failed. Please try again.';
|
||||
Alert.alert('Logout Failed', message);
|
||||
}
|
||||
};
|
||||
|
||||
// Derive user info
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
export interface LoginRequest {
|
||||
email: string;
|
||||
password: string;
|
||||
device_token?: string;
|
||||
fcm_token?: string;
|
||||
device_id?: string;
|
||||
}
|
||||
|
||||
export interface Permission {
|
||||
@ -55,3 +56,13 @@ export interface LoginResponse {
|
||||
user_data: UserData;
|
||||
token: string;
|
||||
}
|
||||
|
||||
export interface LogoutRequest {
|
||||
id: string;
|
||||
fcm_token: string;
|
||||
}
|
||||
|
||||
export interface LogoutResponse {
|
||||
status: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
15
app/interfaces/dashboardLeadCount.ts
Normal file
15
app/interfaces/dashboardLeadCount.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export interface DashboardLeadCountItem {
|
||||
id: string;
|
||||
name: string;
|
||||
statusorder: number | string;
|
||||
color: string;
|
||||
isdefault: number | string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export type DashboardLeadCountAction = 'day' | 'week' | 'month' | 'quarter' | 'year';
|
||||
|
||||
export interface DashboardLeadCountPayload {
|
||||
action: DashboardLeadCountAction | string;
|
||||
staffid: string | number;
|
||||
}
|
||||
@ -5,4 +5,7 @@ export * from './leadDetails';
|
||||
export * from './list';
|
||||
export * from './customers';
|
||||
export * from './fcmToken';
|
||||
export * from './notification';
|
||||
export * from './dashboardLeadCount';
|
||||
|
||||
|
||||
|
||||
27
app/interfaces/notification.ts
Normal file
27
app/interfaces/notification.ts
Normal file
@ -0,0 +1,27 @@
|
||||
export interface NotificationItem {
|
||||
id: string;
|
||||
isread: string;
|
||||
is_hide: string;
|
||||
isread_inline: string;
|
||||
date: string;
|
||||
description: string;
|
||||
fromuserid: string;
|
||||
fromclientid: string;
|
||||
from_fullname: string;
|
||||
touserid: string;
|
||||
fromcompany: string | null;
|
||||
link: string;
|
||||
additional_data: string;
|
||||
profile_image: string;
|
||||
full_date: string;
|
||||
}
|
||||
|
||||
export interface MarkNotificationReadResponse {
|
||||
status: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface ClearNotificationsResponse {
|
||||
status: boolean;
|
||||
message: string;
|
||||
}
|
||||
@ -5,6 +5,7 @@ import {
|
||||
TouchableOpacity,
|
||||
ScrollView,
|
||||
Image,
|
||||
Alert,
|
||||
} from 'react-native';
|
||||
import { DrawerContentComponentProps } from '@react-navigation/drawer';
|
||||
import Icon from 'react-native-vector-icons/Ionicons';
|
||||
@ -14,6 +15,7 @@ import { DrawerItemProps } from '@interfaces';
|
||||
import { route } from '@utils';
|
||||
import { getStyles } from './customDrawerContent.style';
|
||||
import { useAppDispatch, useAppSelector, logout, RootState } from '@store';
|
||||
import { NotificationService } from '@services';
|
||||
|
||||
|
||||
const DrawerItem = ({ label, iconName, focused, onPress }: DrawerItemProps) => {
|
||||
@ -49,14 +51,24 @@ export const CustomDrawerContent = (props: DrawerContentComponentProps) => {
|
||||
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
dispatch(logout());
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
const fcmToken = await NotificationService.getFCMToken();
|
||||
await dispatch(
|
||||
logout({
|
||||
id: userData?.staffid ?? '',
|
||||
fcm_token: fcmToken ?? '',
|
||||
}),
|
||||
).unwrap();
|
||||
navigation.reset({
|
||||
index: 0,
|
||||
routes: [{ name: 'AuthStack' }],
|
||||
});
|
||||
} catch (e: any) {
|
||||
const message =
|
||||
typeof e === 'string'
|
||||
? e
|
||||
: e?.message || 'Logout failed. Please try again.';
|
||||
Alert.alert('Logout Failed', message);
|
||||
}
|
||||
};
|
||||
|
||||
const activeRouteName = state.routeNames[state.index];
|
||||
@ -89,7 +101,10 @@ export const CustomDrawerContent = (props: DrawerContentComponentProps) => {
|
||||
<TouchableOpacity
|
||||
style={styles.userHeaderTouchable}
|
||||
activeOpacity={0.8}
|
||||
onPress={() => navigation.navigate('home', { screen: route.profile })}>
|
||||
onPress={() => {
|
||||
navigation.navigate('home', { screen: route.profile });
|
||||
navigation.closeDrawer();
|
||||
}}>
|
||||
<View style={styles.avatarWrapper}>
|
||||
{hasProfileImage ? (
|
||||
<Image
|
||||
@ -154,6 +169,7 @@ export const CustomDrawerContent = (props: DrawerContentComponentProps) => {
|
||||
} else {
|
||||
navigation.navigate('home', { screen: item.route });
|
||||
}
|
||||
navigation.closeDrawer();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -33,6 +33,7 @@ export const CustomersStack = () => {
|
||||
headerTitleAlign: 'center',
|
||||
headerTintColor: colors.text,
|
||||
headerShadowVisible: false,
|
||||
statusBarTranslucent: false,
|
||||
}}>
|
||||
<Stack.Screen
|
||||
name={route.customersList}
|
||||
|
||||
@ -39,6 +39,7 @@ export const LeadsStack = () => {
|
||||
headerTitleAlign: 'center',
|
||||
headerTintColor: colors.text,
|
||||
headerShadowVisible: false,
|
||||
statusBarTranslucent: false,
|
||||
}}>
|
||||
<Stack.Screen
|
||||
name={route.leadsList}
|
||||
|
||||
@ -29,7 +29,12 @@ const DashboardStack = () => {
|
||||
const { theme: colors } = useTheme();
|
||||
const styles = getStyles(colors);
|
||||
const userData = useAppSelector((state: RootState) => state.auth.user_data);
|
||||
const unreadCount = parseInt(userData?.total_unread_notifications ?? '0', 10);
|
||||
const { items: notificationItems, hasFetched } = useAppSelector((state: RootState) => state.notifications);
|
||||
|
||||
// Calculate unread count from actual items if they've been fetched, otherwise fallback to userData's initial count
|
||||
const unreadCount = hasFetched
|
||||
? notificationItems.filter(n => String(n.isread) === '0').length
|
||||
: parseInt(userData?.total_unread_notifications ?? '0', 10);
|
||||
|
||||
return (
|
||||
<DashboardStackNav.Navigator
|
||||
@ -45,6 +50,7 @@ const DashboardStack = () => {
|
||||
headerTitleAlign: 'center',
|
||||
headerTintColor: colors.text,
|
||||
headerShadowVisible: false,
|
||||
statusBarTranslucent: false,
|
||||
}}>
|
||||
<DashboardStackNav.Screen
|
||||
name="dashboardList"
|
||||
@ -115,6 +121,7 @@ const AddLeadStack = () => {
|
||||
headerTitleAlign: 'center',
|
||||
headerTintColor: colors.text,
|
||||
headerShadowVisible: false,
|
||||
statusBarTranslucent: false,
|
||||
}}>
|
||||
<AddLeadStackNav.Screen
|
||||
name="addLeadForm"
|
||||
@ -160,6 +167,7 @@ const ProfileStack = () => {
|
||||
headerTitleAlign: 'center',
|
||||
headerTintColor: colors.text,
|
||||
headerShadowVisible: false,
|
||||
statusBarTranslucent: false,
|
||||
}}>
|
||||
<ProfileStackNav.Screen
|
||||
name="profileForm"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { createAction, createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import { LoginRequest, LoginResponse } from '@interfaces';
|
||||
import { loginApi } from '@api';
|
||||
import { LoginRequest, LoginResponse, LogoutRequest, LogoutResponse } from '@interfaces';
|
||||
import { loginApi, logoutApi } from '@api';
|
||||
|
||||
export const login = createAsyncThunk<LoginResponse, LoginRequest>(
|
||||
'auth/login',
|
||||
@ -8,9 +8,30 @@ export const login = createAsyncThunk<LoginResponse, LoginRequest>(
|
||||
try {
|
||||
return await loginApi(payload);
|
||||
} catch (error: any) {
|
||||
return rejectWithValue(error.message || 'Failed to login');
|
||||
const serverMessage =
|
||||
error?.response?.data?.message ||
|
||||
error.message ||
|
||||
'Failed to login';
|
||||
return rejectWithValue(serverMessage);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export const logout = createAction('USER_LOGOUT');
|
||||
export const clearLocalAuth = createAction('USER_LOGOUT');
|
||||
|
||||
export const logout = createAsyncThunk<LogoutResponse, LogoutRequest>(
|
||||
'auth/logout',
|
||||
async (payload, { dispatch, rejectWithValue }) => {
|
||||
try {
|
||||
const response = await logoutApi(payload);
|
||||
dispatch(clearLocalAuth());
|
||||
return response;
|
||||
} catch (error: any) {
|
||||
const serverMessage =
|
||||
error?.response?.data?.message ||
|
||||
error.message ||
|
||||
'Failed to logout';
|
||||
return rejectWithValue(serverMessage);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@ -12,6 +12,8 @@ import addLeadReducer from '../features/addLead/reducers';
|
||||
import customersReducer from '../features/customers/reducers';
|
||||
import customerDetailsReducer from '../features/customerDetails/reducers';
|
||||
import addCustomerReducer from '../features/addCustomer/reducers';
|
||||
import notificationReducer from '../features/notification/reducers';
|
||||
import dashboardReducer from '../features/dashboard/reducers';
|
||||
|
||||
const appReducer = combineReducers({
|
||||
auth: authReducer,
|
||||
@ -27,6 +29,8 @@ const appReducer = combineReducers({
|
||||
customers: customersReducer,
|
||||
customerDetails: customerDetailsReducer,
|
||||
addCustomer: addCustomerReducer,
|
||||
notifications: notificationReducer,
|
||||
dashboard: dashboardReducer,
|
||||
});
|
||||
|
||||
const rootReducer = (state: any, action: any) => {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import axios, { AxiosInstance, AxiosRequestConfig, AxiosError } from 'axios';
|
||||
import config from 'react-native-config';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import {Store} from '@reduxjs/toolkit';
|
||||
|
||||
const axiosInstance: AxiosInstance = axios.create({
|
||||
baseURL: config.BASE_URL,
|
||||
timeout: 15000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@ -12,7 +12,13 @@ const axiosInstance: AxiosInstance = axios.create({
|
||||
});
|
||||
|
||||
axiosInstance.interceptors.request.use(
|
||||
request => {
|
||||
async request => {
|
||||
// Dynamically set baseURL from AsyncStorage on every request
|
||||
const storedBaseUrl = await AsyncStorage.getItem('base_url');
|
||||
if (storedBaseUrl) {
|
||||
request.baseURL = storedBaseUrl;
|
||||
}
|
||||
|
||||
if (request.data instanceof FormData) {
|
||||
delete request.headers?.['Content-Type'];
|
||||
}
|
||||
@ -28,9 +34,10 @@ axiosInstance.interceptors.response.use(
|
||||
if (error.response) {
|
||||
const status = error.response.status;
|
||||
const data = error.response.data;
|
||||
console.error('[API] response error', status, data);
|
||||
// Use warn instead of error to avoid triggering the RN dev overlay
|
||||
console.warn('[API] response error', status, data);
|
||||
} else {
|
||||
console.error('[API] network error', error.message);
|
||||
console.warn('[API] network error', error.message);
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
|
||||
@ -21,14 +21,19 @@
|
||||
"@react-navigation/stack": "^7.10.11",
|
||||
"@reduxjs/toolkit": "^2.12.0",
|
||||
"axios": "^1.18.1",
|
||||
"dayjs": "^1.11.21",
|
||||
"react": "19.2.3",
|
||||
"react-native": "0.86.0",
|
||||
"react-native-bootsplash": "^7.3.2",
|
||||
"react-native-config": "^1.6.1",
|
||||
"react-native-device-info": "^15.0.2",
|
||||
"react-native-gesture-handler": "^2.32.0",
|
||||
"react-native-gifted-charts": "^1.4.77",
|
||||
"react-native-linear-gradient": "^2.8.3",
|
||||
"react-native-reanimated": "^4.5.0",
|
||||
"react-native-safe-area-context": "^5.8.0",
|
||||
"react-native-screens": "^4.25.2",
|
||||
"react-native-svg": "^15.15.5",
|
||||
"react-native-vector-icons": "^10.3.0",
|
||||
"react-native-worklets": "^0.10.2",
|
||||
"react-redux": "^9.3.0",
|
||||
|
||||
448
yarn.lock
448
yarn.lock
@ -1034,16 +1034,16 @@
|
||||
"@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==
|
||||
version "1.11.3"
|
||||
resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.11.3.tgz#84257ae3b0531eb2aec1ffa23d70700da007ba95"
|
||||
integrity sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==
|
||||
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"
|
||||
integrity sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==
|
||||
version "4.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz#8911bd72b2c3640a543609e0400b8c4d2e7e7cb6"
|
||||
integrity sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==
|
||||
dependencies:
|
||||
eslint-visitor-keys "^3.4.3"
|
||||
|
||||
@ -1073,14 +1073,14 @@
|
||||
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==
|
||||
version "57.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-57.0.6.tgz#5ce6b6fda683133899678602aa8615209601ece5"
|
||||
integrity sha512-7CmKrS5Rnu8aSZyNlxH2qzA7Ls1HEa4EQvEVOAkHDKPr1e4Cg/nz7I7dUl09QDTVjMvkKYDH4Th0DuAsgqASaw==
|
||||
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/require-utils" "^57.0.4"
|
||||
"@expo/sdk-runtime-versions" "^1.0.0"
|
||||
chalk "^4.1.2"
|
||||
debug "^4.3.5"
|
||||
@ -1113,10 +1113,10 @@
|
||||
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==
|
||||
"@expo/require-utils@^57.0.4":
|
||||
version "57.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@expo/require-utils/-/require-utils-57.0.4.tgz#fcf4377a64a21a70c00c787a9b8fc95ac438cc79"
|
||||
integrity sha512-e7xbg/9BTQcsZE/oErafZXtI7kh5IgfasLJ97J5sFSzX2cA74pDvdlhW1KHVSaDkQyQv6h1LSLhsY7dEeOk7hw==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.20.0"
|
||||
"@babel/core" "^7.25.2"
|
||||
@ -2419,20 +2419,20 @@
|
||||
nullthrows "^1.1.1"
|
||||
|
||||
"@react-navigation/bottom-tabs@^7.18.8":
|
||||
version "7.18.8"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-7.18.8.tgz#a611371cbe01f6a84873e1725c269c8746e4510f"
|
||||
integrity sha512-7KCsBtwCRwQQSTEw9SLglZCEkxWc+EqgdRKyUOgII+6+xEnemOyg8aDlnLIM2yk9ip6uE+Oxvm0jdpQU2vsu2w==
|
||||
version "7.18.14"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-7.18.14.tgz#24fb78948fb06ef7a19b4ff0d596c1d6d643bdbf"
|
||||
integrity sha512-A3V9rDSut459TBPtkD7rb0npUUBlJBfMunyRT5nOGKqPguhuXWk1h91NfQMuqyW2DCUvvFZChzsbLbj42rXTdQ==
|
||||
dependencies:
|
||||
"@react-navigation/elements" "^2.9.30"
|
||||
"@react-navigation/elements" "^2.9.36"
|
||||
color "^4.2.3"
|
||||
sf-symbols-typescript "^2.1.0"
|
||||
|
||||
"@react-navigation/core@^7.21.5":
|
||||
version "7.21.5"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-7.21.5.tgz#2f2c7f5374c9a20d942688aa37c0e9f6665a9549"
|
||||
integrity sha512-3hpV7uR41LBW+GHDoLhztZCb/i5ySRJISZ/rez4d7DCHSZo6ej4gNxYclaS6LRguoLiKG7SOCNa6O390AQklZQ==
|
||||
"@react-navigation/core@^7.21.11":
|
||||
version "7.21.11"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-7.21.11.tgz#dfc9c85f297755fe94c69b34b94df0982c4bf45f"
|
||||
integrity sha512-bCW1PsLA/eOXDOukcJFEzlcL3Zpy8DJuDCfkDDwAQlAgoSZ/J9+ZeDRUMmCUi6xbnFgvFEEIMertaLeErOFP0Q==
|
||||
dependencies:
|
||||
"@react-navigation/routers" "^7.6.0"
|
||||
"@react-navigation/routers" "^7.6.4"
|
||||
escape-string-regexp "^4.0.0"
|
||||
fast-deep-equal "^3.1.3"
|
||||
nanoid "^3.3.11"
|
||||
@ -2442,59 +2442,59 @@
|
||||
use-sync-external-store "^1.5.0"
|
||||
|
||||
"@react-navigation/drawer@^7.12.8":
|
||||
version "7.12.8"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/drawer/-/drawer-7.12.8.tgz#aa321d5959a31db661c5c24ec5f544f5d0899d65"
|
||||
integrity sha512-EY4ItjflOGxY2d73L6+LZe4sMQSJGrqbU/pBBR32unR7KP9Crb39m0tpBdtnGHMV18XGh/I6nPVYS3+y/7LaNw==
|
||||
version "7.13.5"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/drawer/-/drawer-7.13.5.tgz#54c5fc4065fd12cf9687b0407f4d8145dc2c9e92"
|
||||
integrity sha512-e1WdwyXeCS/tUDFSTIlNLgOWyUdz4hZnxQrAF7Ia1jY8F6gLNgo+Q04kxbUvTqS4wXZYfG2DSW94WnJogR3oSQ==
|
||||
dependencies:
|
||||
"@react-navigation/elements" "^2.9.30"
|
||||
"@react-navigation/elements" "^2.9.36"
|
||||
color "^4.2.3"
|
||||
react-native-drawer-layout "^4.2.7"
|
||||
react-native-drawer-layout "^4.2.9"
|
||||
use-latest-callback "^0.2.4"
|
||||
|
||||
"@react-navigation/elements@^2.9.30":
|
||||
version "2.9.30"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-2.9.30.tgz#60702877319f06c20b15e3f98446dcc994c67cff"
|
||||
integrity sha512-2isleieiRMmP4WNMV2Q1u3qP1M47ZqsJ2hJ/Og11FeKXK8YmUTHya7PW7ecsgAh2CXKTxAcbfJDFTSvq2D++Tw==
|
||||
"@react-navigation/elements@^2.9.36":
|
||||
version "2.9.36"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-2.9.36.tgz#b060502ed97768f719b604dc9b652031babded1a"
|
||||
integrity sha512-+10x9s5v2Q7FwAYdSmPMgILtxZyC5e4hWJQu8g5o3u4p8DUToTBmGvys/UvmEr+h9xmm0Go42qw9Ff2ape53kQ==
|
||||
dependencies:
|
||||
color "^4.2.3"
|
||||
use-latest-callback "^0.2.4"
|
||||
use-sync-external-store "^1.5.0"
|
||||
|
||||
"@react-navigation/native-stack@^7.17.10":
|
||||
version "7.17.10"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/native-stack/-/native-stack-7.17.10.tgz#d838eac41f4cbd58ba18ce542397db80962378b0"
|
||||
integrity sha512-m1BWVEaOPX9k30DbmhsD7IlrUdl4J7Ogmo71rcNlbZQPMNB126av/nfwqB+qN7DIsiwTAnORnT+SwzD56qqD0Q==
|
||||
version "7.18.6"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/native-stack/-/native-stack-7.18.6.tgz#71ed9d2a28368bd46d8dded050dbffd9d818f090"
|
||||
integrity sha512-KuvvSBddHrbKC4c6yKz+UCFey2xgTuPQHgjf2wJQAvA7JM8jCQa3G1+QzhO6d44nYNKir3y6EounXZvQE/BX6w==
|
||||
dependencies:
|
||||
"@react-navigation/elements" "^2.9.30"
|
||||
"@react-navigation/elements" "^2.9.36"
|
||||
color "^4.2.3"
|
||||
sf-symbols-typescript "^2.1.0"
|
||||
warn-once "^0.1.1"
|
||||
|
||||
"@react-navigation/native@^7.3.8":
|
||||
version "7.3.8"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-7.3.8.tgz#01513734e93ac40fd3b48fd6169836fbf5678ac8"
|
||||
integrity sha512-zHmQcxWBT8GOwsofEOmHqpdM5twkwE/esa9JFGlW4hpXeQTTe/dRcPSLjwsvePtolbDKz0YZbK+I5KrW3j63LQ==
|
||||
version "7.3.14"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-7.3.14.tgz#d846d80b7036fad3a1f1a2f37aa69e020eebe145"
|
||||
integrity sha512-hcKTDNBuuAA1/xW6QeKYmMPVhk5W9dKGQpPmn5dQeeePwMpu5OZ14NOgwKH0w9D3tg2jupojTcVL0tsx5DTFXg==
|
||||
dependencies:
|
||||
"@react-navigation/core" "^7.21.5"
|
||||
"@react-navigation/core" "^7.21.11"
|
||||
escape-string-regexp "^4.0.0"
|
||||
fast-deep-equal "^3.1.3"
|
||||
nanoid "^3.3.11"
|
||||
standard-navigation "^0.0.7"
|
||||
standard-navigation "^0.0.8"
|
||||
use-latest-callback "^0.2.4"
|
||||
|
||||
"@react-navigation/routers@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/routers/-/routers-7.6.0.tgz#04ee630c4ecbdfa3c8ef65000909aa2dbf0ff78f"
|
||||
integrity sha512-lblhDXfS75jLc7G2K7BZGM+7cjqQXk13X/MA4fq/12r62zM+fBhhreLzYflSitrDDXFRJpSvJXy0ziiGU04Xow==
|
||||
"@react-navigation/routers@^7.6.4":
|
||||
version "7.6.4"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/routers/-/routers-7.6.4.tgz#7b0dfa19f9fef0ab1588c9dbfc7f7616fced3ff8"
|
||||
integrity sha512-GI7eJm8/KsZUQaYcXvEExikKurRZRgEsSzyZ7faENfi65yqJBCXjDMwyN1pF6pNW1MoLH1ErDwDivFxY6BzD3w==
|
||||
dependencies:
|
||||
nanoid "^3.3.11"
|
||||
|
||||
"@react-navigation/stack@^7.10.11":
|
||||
version "7.10.11"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/stack/-/stack-7.10.11.tgz#75788547aa8c3f43c20cdb87c06342a39f2fb734"
|
||||
integrity sha512-FNlylwYaGwGArRjmJhmL3FviTjT/j10UVx6TAaqqeLeYEircqMm2kBBz2e9FpcXH2OeCnPinAjZ0B1th2mNmsg==
|
||||
version "7.10.17"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/stack/-/stack-7.10.17.tgz#628fb539bf7460fa93c72d36cfbfcc4988752d35"
|
||||
integrity sha512-2b/AC8YxfPkFsN8DZGjrMiX6G86unbEonE0E8dA5LugzfKF/s28VBM7yBwZKRsp3ys5aVCvt/Z3zviQA/uGAIA==
|
||||
dependencies:
|
||||
"@react-navigation/elements" "^2.9.30"
|
||||
"@react-navigation/elements" "^2.9.36"
|
||||
color "^4.2.3"
|
||||
use-latest-callback "^0.2.4"
|
||||
|
||||
@ -2528,9 +2528,9 @@
|
||||
integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==
|
||||
|
||||
"@sinclair/typebox@^0.27.8":
|
||||
version "0.27.10"
|
||||
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.10.tgz#beefe675f1853f73676aecc915b2bd2ac98c4fc6"
|
||||
integrity sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==
|
||||
version "0.27.12"
|
||||
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.12.tgz#0cacd3cff047a32936b1ace47ea7c86eaab60a7f"
|
||||
integrity sha512-hhyNJ+nbR6ZR7pToHvllEFun9TL0sbL+tk/ON75lo+Xas054uez98qRbsuNt7MBCyZKK4+8Yli/OAGZhmfBZ/g==
|
||||
|
||||
"@sinonjs/commons@^3.0.0":
|
||||
version "3.0.1"
|
||||
@ -2629,9 +2629,9 @@
|
||||
pretty-format "^29.0.0"
|
||||
|
||||
"@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==
|
||||
version "26.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-26.1.2.tgz#da79708f1f9c6294f4cdec8f455a3032b028808a"
|
||||
integrity sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==
|
||||
dependencies:
|
||||
undici-types "~8.3.0"
|
||||
|
||||
@ -2672,99 +2672,99 @@
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^8.36.0":
|
||||
version "8.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.64.0.tgz#71a0c3d5f8a5e6c5dfdb4f0f04bd1bfb572d5e24"
|
||||
integrity sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==
|
||||
version "8.65.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz#0a58df6fea8c0bf6b396f518077099bc8b762bb5"
|
||||
integrity sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==
|
||||
dependencies:
|
||||
"@eslint-community/regexpp" "^4.12.2"
|
||||
"@typescript-eslint/scope-manager" "8.64.0"
|
||||
"@typescript-eslint/type-utils" "8.64.0"
|
||||
"@typescript-eslint/utils" "8.64.0"
|
||||
"@typescript-eslint/visitor-keys" "8.64.0"
|
||||
"@typescript-eslint/scope-manager" "8.65.0"
|
||||
"@typescript-eslint/type-utils" "8.65.0"
|
||||
"@typescript-eslint/utils" "8.65.0"
|
||||
"@typescript-eslint/visitor-keys" "8.65.0"
|
||||
ignore "^7.0.5"
|
||||
natural-compare "^1.4.0"
|
||||
ts-api-utils "^2.5.0"
|
||||
|
||||
"@typescript-eslint/parser@^8.36.0":
|
||||
version "8.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.64.0.tgz#c9864a1cc28a13ff29a7314fbdef0528bb122f72"
|
||||
integrity sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==
|
||||
version "8.65.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.65.0.tgz#5295c1058c0a1dd746ef28baaf9c0341dbdf03dc"
|
||||
integrity sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "8.64.0"
|
||||
"@typescript-eslint/types" "8.64.0"
|
||||
"@typescript-eslint/typescript-estree" "8.64.0"
|
||||
"@typescript-eslint/visitor-keys" "8.64.0"
|
||||
"@typescript-eslint/scope-manager" "8.65.0"
|
||||
"@typescript-eslint/types" "8.65.0"
|
||||
"@typescript-eslint/typescript-estree" "8.65.0"
|
||||
"@typescript-eslint/visitor-keys" "8.65.0"
|
||||
debug "^4.4.3"
|
||||
|
||||
"@typescript-eslint/project-service@8.64.0":
|
||||
version "8.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.64.0.tgz#14c4e29390d7325a7f8a1218c2788fd649b85da6"
|
||||
integrity sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==
|
||||
"@typescript-eslint/project-service@8.65.0":
|
||||
version "8.65.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.65.0.tgz#65fbbc9a1591abffaeab5513200f848271cb0aa5"
|
||||
integrity sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==
|
||||
dependencies:
|
||||
"@typescript-eslint/tsconfig-utils" "^8.64.0"
|
||||
"@typescript-eslint/types" "^8.64.0"
|
||||
"@typescript-eslint/tsconfig-utils" "^8.65.0"
|
||||
"@typescript-eslint/types" "^8.65.0"
|
||||
debug "^4.4.3"
|
||||
|
||||
"@typescript-eslint/scope-manager@8.64.0":
|
||||
version "8.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.64.0.tgz#d45f15304a94c85c39db317b717b158fb6259958"
|
||||
integrity sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==
|
||||
"@typescript-eslint/scope-manager@8.65.0":
|
||||
version "8.65.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz#9547202ce7e608e7b6283df585703b980a0ea70d"
|
||||
integrity sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.64.0"
|
||||
"@typescript-eslint/visitor-keys" "8.64.0"
|
||||
"@typescript-eslint/types" "8.65.0"
|
||||
"@typescript-eslint/visitor-keys" "8.65.0"
|
||||
|
||||
"@typescript-eslint/tsconfig-utils@8.64.0", "@typescript-eslint/tsconfig-utils@^8.64.0":
|
||||
version "8.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz#c62ac8ea9173c3cac8b38b8e66e30a046b548851"
|
||||
integrity sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==
|
||||
"@typescript-eslint/tsconfig-utils@8.65.0", "@typescript-eslint/tsconfig-utils@^8.65.0":
|
||||
version "8.65.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz#36f168fcdbb1295f7446ff0379667f98c3cf1bf3"
|
||||
integrity sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==
|
||||
|
||||
"@typescript-eslint/type-utils@8.64.0":
|
||||
version "8.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.64.0.tgz#106fa7d58cf9cf7758f3dd8e426ac8237eceacf3"
|
||||
integrity sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==
|
||||
"@typescript-eslint/type-utils@8.65.0":
|
||||
version "8.65.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz#d316d7522d93cff4cd14f305e02f3df2d804f9c1"
|
||||
integrity sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.64.0"
|
||||
"@typescript-eslint/typescript-estree" "8.64.0"
|
||||
"@typescript-eslint/utils" "8.64.0"
|
||||
"@typescript-eslint/types" "8.65.0"
|
||||
"@typescript-eslint/typescript-estree" "8.65.0"
|
||||
"@typescript-eslint/utils" "8.65.0"
|
||||
debug "^4.4.3"
|
||||
ts-api-utils "^2.5.0"
|
||||
|
||||
"@typescript-eslint/types@8.64.0", "@typescript-eslint/types@^8.64.0":
|
||||
version "8.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.64.0.tgz#b41f8ef5dd40616908658b991197a9d486cda60b"
|
||||
integrity sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==
|
||||
"@typescript-eslint/types@8.65.0", "@typescript-eslint/types@^8.65.0":
|
||||
version "8.65.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.65.0.tgz#3e86738416a777c8b8925ab46745f48ecf904c9f"
|
||||
integrity sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==
|
||||
|
||||
"@typescript-eslint/typescript-estree@8.64.0":
|
||||
version "8.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz#b8d51255e2d726eb4bd80d397a4fb4170c02eecc"
|
||||
integrity sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==
|
||||
"@typescript-eslint/typescript-estree@8.65.0":
|
||||
version "8.65.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz#f1f514808f6aa713e2d678ae8ff592a65e1632af"
|
||||
integrity sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==
|
||||
dependencies:
|
||||
"@typescript-eslint/project-service" "8.64.0"
|
||||
"@typescript-eslint/tsconfig-utils" "8.64.0"
|
||||
"@typescript-eslint/types" "8.64.0"
|
||||
"@typescript-eslint/visitor-keys" "8.64.0"
|
||||
"@typescript-eslint/project-service" "8.65.0"
|
||||
"@typescript-eslint/tsconfig-utils" "8.65.0"
|
||||
"@typescript-eslint/types" "8.65.0"
|
||||
"@typescript-eslint/visitor-keys" "8.65.0"
|
||||
debug "^4.4.3"
|
||||
minimatch "^10.2.2"
|
||||
semver "^7.7.3"
|
||||
tinyglobby "^0.2.15"
|
||||
ts-api-utils "^2.5.0"
|
||||
|
||||
"@typescript-eslint/utils@8.64.0", "@typescript-eslint/utils@^8.0.0":
|
||||
version "8.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.64.0.tgz#98bb2010cfb754b41985b9c93e6e8b3dcd7bd600"
|
||||
integrity sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==
|
||||
"@typescript-eslint/utils@8.65.0", "@typescript-eslint/utils@^8.0.0":
|
||||
version "8.65.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.65.0.tgz#afedd974a0c8deeef553b509df5800bafd615a72"
|
||||
integrity sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.9.1"
|
||||
"@typescript-eslint/scope-manager" "8.64.0"
|
||||
"@typescript-eslint/types" "8.64.0"
|
||||
"@typescript-eslint/typescript-estree" "8.64.0"
|
||||
"@typescript-eslint/scope-manager" "8.65.0"
|
||||
"@typescript-eslint/types" "8.65.0"
|
||||
"@typescript-eslint/typescript-estree" "8.65.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@8.64.0":
|
||||
version "8.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz#7a08421d10e54960733352cd7c95fab1784e8473"
|
||||
integrity sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==
|
||||
"@typescript-eslint/visitor-keys@8.65.0":
|
||||
version "8.65.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz#e3704c13cb4a1c22454c1abf28ff4737e15018c6"
|
||||
integrity sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.64.0"
|
||||
"@typescript-eslint/types" "8.65.0"
|
||||
eslint-visitor-keys "^5.0.0"
|
||||
|
||||
"@ungap/structured-clone@^1.2.0":
|
||||
@ -2816,9 +2816,9 @@ acorn-jsx@^5.3.2:
|
||||
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||
|
||||
acorn@^8.15.0, acorn@^8.9.0:
|
||||
version "8.17.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.17.0.tgz#1785adb84faf8d8add10369b93826fc2bd08f1fe"
|
||||
integrity sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==
|
||||
version "8.18.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.18.0.tgz#4faf01b2d6d326bfeed97aea1f52220b5f4c1940"
|
||||
integrity sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==
|
||||
|
||||
agent-base@6:
|
||||
version "6.0.2"
|
||||
@ -2901,9 +2901,9 @@ anymatch@^3.0.3:
|
||||
picomatch "^2.0.4"
|
||||
|
||||
appdirsjs@^1.2.4:
|
||||
version "1.2.7"
|
||||
resolved "https://registry.yarnpkg.com/appdirsjs/-/appdirsjs-1.2.7.tgz#50b4b7948a26ba6090d4aede2ae2dc2b051be3b3"
|
||||
integrity sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw==
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/appdirsjs/-/appdirsjs-1.2.8.tgz#8779e70bc4c5aa205d428c037dfb916e6d7c2d69"
|
||||
integrity sha512-8zl1xlxeS4a0/36CT6LOaVioPOL8TeLT1b9OHk0j9xSbzmPBuM7lUgWMSTh6SbuF8fbwjcP1rr30OCLpd1fl+A==
|
||||
|
||||
argparse@^1.0.7:
|
||||
version "1.0.10"
|
||||
@ -3028,12 +3028,12 @@ available-typed-arrays@^1.0.7:
|
||||
possible-typed-array-names "^1.0.0"
|
||||
|
||||
axios@^1.18.1:
|
||||
version "1.18.1"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.18.1.tgz#d63f9863bcd8938815c86f9e2abd380189d96dfe"
|
||||
integrity sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==
|
||||
version "1.19.0"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.19.0.tgz#ddf864d4c8233c0e6873746ab59361537d05ad39"
|
||||
integrity sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==
|
||||
dependencies:
|
||||
follow-redirects "^1.16.0"
|
||||
form-data "^4.0.5"
|
||||
form-data "^4.0.6"
|
||||
https-proxy-agent "^5.0.1"
|
||||
proxy-from-env "^2.1.0"
|
||||
|
||||
@ -3172,10 +3172,10 @@ base64-js@^1.3.1, base64-js@^1.5.1:
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||
|
||||
baseline-browser-mapping@^2.10.42:
|
||||
version "2.10.43"
|
||||
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.43.tgz#7b5d11590ce5acdbe4859443e3c940e81ce8c02d"
|
||||
integrity sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==
|
||||
baseline-browser-mapping@^2.10.44:
|
||||
version "2.11.7"
|
||||
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.11.7.tgz#65b41243f29d2cca7b5d72ca6e9b8285df1ed975"
|
||||
integrity sha512-APw5YuIQAg6L9w4sHDI6j26DGFJI6RpYOhnkMPdC9lWbkKvsyPHzDsve1yd73lk21yz7Y09Kci8B2Pp9FonzWA==
|
||||
|
||||
big-integer@1.6.x:
|
||||
version "1.6.52"
|
||||
@ -3229,24 +3229,24 @@ bplist-parser@0.3.1:
|
||||
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"
|
||||
integrity sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==
|
||||
version "1.1.17"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.17.tgz#a375e14e41d672617de69526be02d0d7751a6909"
|
||||
integrity sha512-w+aeW/mkgM4PyRMOJCgi3fOrTm5Q8QY1OSfn2TO2iuDj3ezIHqejmuxbjfPrqUkgqRew1iqkyAn0tr0ZwHD9+w==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
brace-expansion@^2.0.1:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.2.tgz#0bba2271feb7d458b0d31ad13625aaa4754431e2"
|
||||
integrity sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.3.tgz#1bf69aacdf6a4380ca17c284d9f928d4aa6401bc"
|
||||
integrity sha512-DRdx5neNsG/QXbniLFWi2YmC/68oeOOmKz6zOjVk6ZS1ZLXgLIKqVEc6hWsmkjBbgii0SwaBTcJ5XKj5gzY/4A==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
|
||||
brace-expansion@^5.0.5:
|
||||
version "5.0.7"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.7.tgz#1b0e46965b479dad65af737b4a02790a05498337"
|
||||
integrity sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==
|
||||
brace-expansion@^5.0.8:
|
||||
version "5.0.8"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.8.tgz#135ad0d8d808eb18eb5e0ec9a21f3a0b92ef18cf"
|
||||
integrity sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==
|
||||
dependencies:
|
||||
balanced-match "^4.0.2"
|
||||
|
||||
@ -3258,13 +3258,13 @@ braces@^3.0.3:
|
||||
fill-range "^7.1.1"
|
||||
|
||||
browserslist@^4.24.0, browserslist@^4.28.1:
|
||||
version "4.28.6"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.6.tgz#7cf83afcd69c55fde6fb2dcc5039ff0f4ba42610"
|
||||
integrity sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==
|
||||
version "4.28.7"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.7.tgz#409046517fccd2e51cdc20f077454b7141184028"
|
||||
integrity sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==
|
||||
dependencies:
|
||||
baseline-browser-mapping "^2.10.42"
|
||||
caniuse-lite "^1.0.30001803"
|
||||
electron-to-chromium "^1.5.389"
|
||||
baseline-browser-mapping "^2.10.44"
|
||||
caniuse-lite "^1.0.30001806"
|
||||
electron-to-chromium "^1.5.393"
|
||||
node-releases "^2.0.51"
|
||||
update-browserslist-db "^1.2.3"
|
||||
|
||||
@ -3334,7 +3334,7 @@ camelcase@^6.2.0:
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
|
||||
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
|
||||
|
||||
caniuse-lite@^1.0.30001803:
|
||||
caniuse-lite@^1.0.30001806:
|
||||
version "1.0.30001806"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz#1bc8e502b723fa393455dfbedd5ccec0c29bb74e"
|
||||
integrity sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==
|
||||
@ -3614,6 +3614,14 @@ css-select@^5.1.0:
|
||||
domutils "^3.0.1"
|
||||
nth-check "^2.0.1"
|
||||
|
||||
css-tree@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d"
|
||||
integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==
|
||||
dependencies:
|
||||
mdn-data "2.0.14"
|
||||
source-map "^0.6.1"
|
||||
|
||||
css-what@^6.1.0:
|
||||
version "6.2.2"
|
||||
resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.2.2.tgz#cdcc8f9b6977719fdfbd1de7aec24abf756b9dea"
|
||||
@ -3651,7 +3659,7 @@ data-view-byte-offset@^1.0.1:
|
||||
es-errors "^1.3.0"
|
||||
is-data-view "^1.0.1"
|
||||
|
||||
dayjs@^1.8.15:
|
||||
dayjs@^1.11.21, dayjs@^1.8.15:
|
||||
version "1.11.21"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.21.tgz#57f87562e62de76f3c704bd2b8d522fc33068eb2"
|
||||
integrity sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==
|
||||
@ -3813,10 +3821,10 @@ ee-first@1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
|
||||
|
||||
electron-to-chromium@^1.5.389:
|
||||
version "1.5.392"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.392.tgz#31e9f9af0d8df3d1489468a4242b173605617482"
|
||||
integrity sha512-1yQq3VQCZRwsnYc67Oc+1fge6Lwtn0hzi6zmEVkB61Zx21kTbwJAW4dFLadl5Rc1tKhG/kSpYXnfiAhu0f0a1g==
|
||||
electron-to-chromium@^1.5.393:
|
||||
version "1.5.398"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.398.tgz#9d4d979344ac762840293e3eacbebed34228aa16"
|
||||
integrity sha512-AsvhAxopJGh6museTDMIjn6JpDYOfgu4RLlygomt87MUwBUqTfd/1EiPtx10/LZE8xpTvkP2E9Gafq7lkLtodQ==
|
||||
|
||||
emittery@^0.13.1:
|
||||
version "0.13.1"
|
||||
@ -4060,9 +4068,9 @@ eslint-plugin-ft-flow@^2.0.1:
|
||||
string-natural-compare "^3.0.1"
|
||||
|
||||
eslint-plugin-jest@^29.0.1:
|
||||
version "29.15.4"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-29.15.4.tgz#0c5371fc87499c6c5c25d60095340d05e396f58b"
|
||||
integrity sha512-6ln5i9Nkrb27X4w91ZPt/xHDsVQnvxTS2ntgq6r32u+8gymdUrp88TdcBXSveZW0Dl+M5v2H6K75kJhMvUGhjg==
|
||||
version "29.16.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-29.16.0.tgz#0810a9eb2753287f45485865942a22e740549d6a"
|
||||
integrity sha512-0WFBxDHlT2ratGQfnFQEVIsgQJ5cfd+0IV8Kc6U3X2onB8ATLG23voD2Ch5G9fCkEpCPmCMuzW0tbS0kYb8biw==
|
||||
dependencies:
|
||||
"@typescript-eslint/utils" "^8.0.0"
|
||||
|
||||
@ -4447,9 +4455,9 @@ flat-cache@^3.0.4:
|
||||
rimraf "^3.0.2"
|
||||
|
||||
flatted@^3.2.9:
|
||||
version "3.4.2"
|
||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.2.tgz#f5c23c107f0f37de8dbdf24f13722b3b98d52726"
|
||||
integrity sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==
|
||||
version "3.4.4"
|
||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.4.tgz#aeeca2a506303f0cee61c59e6c9f2a88d2f29fc6"
|
||||
integrity sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==
|
||||
|
||||
flow-enums-runtime@^0.0.6:
|
||||
version "0.0.6"
|
||||
@ -4468,7 +4476,7 @@ for-each@^0.3.3, for-each@^0.3.5:
|
||||
dependencies:
|
||||
is-callable "^1.2.7"
|
||||
|
||||
form-data@^4.0.5:
|
||||
form-data@^4.0.6:
|
||||
version "4.0.6"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.6.tgz#28e864e1b786dbebb68db1f452f9635278665827"
|
||||
integrity sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==
|
||||
@ -4591,6 +4599,11 @@ getenv@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/getenv/-/getenv-2.0.0.tgz#b1698c7b0f29588f4577d06c42c73a5b475c69e0"
|
||||
integrity sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ==
|
||||
|
||||
gifted-charts-core@0.1.81:
|
||||
version "0.1.81"
|
||||
resolved "https://registry.yarnpkg.com/gifted-charts-core/-/gifted-charts-core-0.1.81.tgz#6e8930d73f8026ca59f80a0edf4a24849016d034"
|
||||
integrity sha512-plgJSbKB0Lxp2KQ/Fvj1qbOhiy6wxPiZ0Av60iFHpSSu6YlJjYhwczx5w2/iJQdZYb851OFMHgN/pgTNVLv6dA==
|
||||
|
||||
glob-parent@^5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
|
||||
@ -4844,9 +4857,9 @@ image-size@^1.0.2:
|
||||
queue "6.0.2"
|
||||
|
||||
immer@^11.0.0:
|
||||
version "11.1.11"
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-11.1.11.tgz#bbf825a333ae1b16fd450d8da5f61d54de6a553d"
|
||||
integrity sha512-qzXuyXAkPySAGYkfsAwodDPWT8Zm7/Uo5BNt4BjhMhG5WlWyZZ4wQqnWwdS8kjlQ1Cwu6gjw3A6+0gTQwlyYtw==
|
||||
version "11.1.15"
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-11.1.15.tgz#1b178a9338486ade5939fa76cff43f4a49001eb0"
|
||||
integrity sha512-VrNANlmnWQnh5COXIIOQXM9oOJw7naGKlBT74ZOOR6lpVXc3gFEu9FJLDFcpCJ2j+NWr8TIwtWD//T6ZX6TKiQ==
|
||||
|
||||
import-fresh@^3.2.1, import-fresh@^3.3.0:
|
||||
version "3.3.1"
|
||||
@ -5829,6 +5842,11 @@ math-intrinsics@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
|
||||
integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
|
||||
|
||||
mdn-data@2.0.14:
|
||||
version "2.0.14"
|
||||
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
|
||||
integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
|
||||
|
||||
media-typer@0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||
@ -6089,11 +6107,11 @@ mimic-fn@^2.1.0:
|
||||
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
|
||||
|
||||
minimatch@^10.2.2:
|
||||
version "10.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.5.tgz#bd48687a0be38ed2961399105600f832095861d1"
|
||||
integrity sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==
|
||||
version "10.2.6"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.6.tgz#fd956bbe0b77241e9f15ac5dccb1c638060968ef"
|
||||
integrity sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==
|
||||
dependencies:
|
||||
brace-expansion "^5.0.5"
|
||||
brace-expansion "^5.0.8"
|
||||
|
||||
minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
|
||||
version "3.1.5"
|
||||
@ -6198,9 +6216,9 @@ node-releases@^2.0.51:
|
||||
integrity sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==
|
||||
|
||||
node-stream-zip@^1.9.1:
|
||||
version "1.15.0"
|
||||
resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.15.0.tgz#158adb88ed8004c6c49a396b50a6a5de3bca33ea"
|
||||
integrity sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==
|
||||
version "1.16.0"
|
||||
resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.16.0.tgz#37d8e5d34e48cff6c2eb7ac68702ceec7aa3a7b8"
|
||||
integrity sha512-ObaRrRoR8T68wF6suxHd7R4XQNamij6ZQHrwG7Dx1D2zeHcDNLsIOBcWrIwtDm7AsCXBguaPHgXhcjxDa2szrg==
|
||||
|
||||
normalize-path@^3.0.0:
|
||||
version "3.0.0"
|
||||
@ -6366,11 +6384,12 @@ ora@^5.4.1:
|
||||
wcwidth "^1.0.1"
|
||||
|
||||
own-keys@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358"
|
||||
integrity sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.2.tgz#31448ec1f781ecb1447f6f6aa0d6534222af59de"
|
||||
integrity sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==
|
||||
dependencies:
|
||||
get-intrinsic "^1.2.6"
|
||||
call-bound "^1.0.4"
|
||||
get-intrinsic "^1.3.0"
|
||||
object-keys "^1.1.1"
|
||||
safe-push-apply "^1.0.0"
|
||||
|
||||
@ -6679,9 +6698,9 @@ react-is@^18.0.0:
|
||||
integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
|
||||
|
||||
react-is@^19.1.0, react-is@^19.2.3:
|
||||
version "19.2.7"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.7.tgz#57668ee86a78574a542b0a539455212b2c086df2"
|
||||
integrity sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==
|
||||
version "19.2.8"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.8.tgz#09826f9fbc187bc668e3e5c62edc001f804d5018"
|
||||
integrity sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==
|
||||
|
||||
react-native-bootsplash@^7.3.2:
|
||||
version "7.3.2"
|
||||
@ -6705,10 +6724,15 @@ react-native-config@^1.6.1:
|
||||
resolved "https://registry.yarnpkg.com/react-native-config/-/react-native-config-1.6.1.tgz#f8428829fda74384484d773fe36820159c559159"
|
||||
integrity sha512-HvKtxr6/Tq3iMdFx5REYZsjCtPi0RxQOMCs15+DqrUPTNFtWHuEuh+zw7fJp+dmuO79YMfdtlsPWIGTHtaXwjg==
|
||||
|
||||
react-native-drawer-layout@^4.2.7:
|
||||
version "4.2.7"
|
||||
resolved "https://registry.yarnpkg.com/react-native-drawer-layout/-/react-native-drawer-layout-4.2.7.tgz#46a7a3ec3b0c05f6bea763751bc27a6ce9e5b650"
|
||||
integrity sha512-hhD+E0QmUPkP2Sj1MsUdrvU7GeOiHChPAFPtKahroTwlBGnpgJsUVSL0GWOy5cG3oCZfnu4Pb+gIzOa4ItGNuA==
|
||||
react-native-device-info@^15.0.2:
|
||||
version "15.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-native-device-info/-/react-native-device-info-15.0.2.tgz#c7de1bb6baeb07e1bab70159ad540c5705cb8dc0"
|
||||
integrity sha512-dd71eXG2l3Cwp66IvKNadMTB8fhU3PEjyVddI97sYan+D4bgIAUmgGDhbSOFvHcGavksb2U17kiQYaDiK2WK2g==
|
||||
|
||||
react-native-drawer-layout@^4.2.9:
|
||||
version "4.2.9"
|
||||
resolved "https://registry.yarnpkg.com/react-native-drawer-layout/-/react-native-drawer-layout-4.2.9.tgz#848d36cdd5f4d92c95257ae8121ad998a567398b"
|
||||
integrity sha512-ETOxvlhhb4LmuuG3RN7A3qwt9jr9AZ2it+1G2kNE4g2fTyxxay7QQkPm1HfKi/JzmMSWC5+YTepGSgZNpJIDGg==
|
||||
dependencies:
|
||||
color "^4.2.3"
|
||||
use-latest-callback "^0.2.4"
|
||||
@ -6723,15 +6747,27 @@ react-native-gesture-handler@^2.32.0:
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
invariant "^2.2.4"
|
||||
|
||||
react-native-gifted-charts@^1.4.77:
|
||||
version "1.4.77"
|
||||
resolved "https://registry.yarnpkg.com/react-native-gifted-charts/-/react-native-gifted-charts-1.4.77.tgz#8e1195b3144f221d675729b636819e13e7e0e071"
|
||||
integrity sha512-Ul4juHO0Gicng139i61AzQ3h4kLM25dzid1rU+d3d7PuHsI4UypgeChz/luaHMZaWgXkALjq6DLqaM2Y2AEhrA==
|
||||
dependencies:
|
||||
gifted-charts-core "0.1.81"
|
||||
|
||||
react-native-is-edge-to-edge@^1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.3.1.tgz#feb9a6a8faf0874298947edd556e5af22044e139"
|
||||
integrity sha512-NIXU/iT5+ORyCc7p0z2nnlkouYKX425vuU1OEm6bMMtWWR9yvb+Xg5AZmImTKoF9abxCPqrKC3rOZsKzUYgYZA==
|
||||
|
||||
react-native-linear-gradient@^2.8.3:
|
||||
version "2.8.3"
|
||||
resolved "https://registry.yarnpkg.com/react-native-linear-gradient/-/react-native-linear-gradient-2.8.3.tgz#9a116649f86d74747304ee13db325e20b21e564f"
|
||||
integrity sha512-KflAXZcEg54PXkLyflaSZQ3PJp4uC4whM7nT/Uot9m0e/qxFV3p6uor1983D1YOBJbJN7rrWdqIjq0T42jOJyA==
|
||||
|
||||
react-native-reanimated@^4.5.0:
|
||||
version "4.5.1"
|
||||
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-4.5.1.tgz#be81ed3a96bf70baeeccdc24d3350883099b7dd3"
|
||||
integrity sha512-RnMvtDuR+68ig864gAvZCOdZehqhC5rFmMo0kn+ARfgVSTvFeF6IFLBVgMPUu0KwihaapEyW24WRi6nEyy1kSA==
|
||||
version "4.5.3"
|
||||
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-4.5.3.tgz#10ec3f1b2c8d710033d707199cab591a0d410d70"
|
||||
integrity sha512-+owIckpD4sA13XKHaLrr8V1RP4paqoxKvz/jy2wl/ULjMsfcRlfIJdKJxR1kcQr4McBZ6hAoPo01NyLnmy2ycQ==
|
||||
dependencies:
|
||||
react-native-is-edge-to-edge "^1.3.1"
|
||||
semver "^7.7.3"
|
||||
@ -6742,13 +6778,21 @@ react-native-safe-area-context@^5.8.0:
|
||||
integrity sha512-t+ZsAVzY/wWzzx34vqGbo3/as9EEESJdbyZNL7Yg5EYX+toYMtMqFoDDCvqZUi35eeGVsXc6pAaEk4edMwbuCQ==
|
||||
|
||||
react-native-screens@^4.25.2:
|
||||
version "4.26.1"
|
||||
resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.26.1.tgz#98a580bcb244f93e931391f4af43d405b557efba"
|
||||
integrity sha512-4aaCRSm1CXqjTiph37uFytz5NUxIeiOdYTnetkRoUbEIQhqUa4ShsjQUuKC42Pe+JEBw13YkM05TJXMdlrPy/A==
|
||||
version "4.26.2"
|
||||
resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.26.2.tgz#cfc007736526a0958e8e8d3ff5f77202c323a852"
|
||||
integrity sha512-2XnWsZToKj76trGtEZzx5ELD/qOICFEprEeUntImmitQFVUkea27fiWdUSITArI356Y1qynpXZINW+Unbhky/A==
|
||||
dependencies:
|
||||
react-freeze "^1.0.0"
|
||||
warn-once "^0.1.0"
|
||||
|
||||
react-native-svg@^15.15.5:
|
||||
version "15.15.5"
|
||||
resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-15.15.5.tgz#822805c14481b8ec16d5fbac1e3ce2b27a5509d7"
|
||||
integrity sha512-L4go5jA+GWutdJ/JucuN20cjAbMg1HmMtAP+wZ+3JLCf6Jd0bhXQHxciRP/AQm/FlrIEZwkMcHNZP+FXAiic0w==
|
||||
dependencies:
|
||||
css-select "^5.1.0"
|
||||
css-tree "^1.1.3"
|
||||
|
||||
react-native-vector-icons@^10.3.0:
|
||||
version "10.3.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-vector-icons/-/react-native-vector-icons-10.3.0.tgz#de440f2627a2ed1079ce3b99d5b9d4f86894df28"
|
||||
@ -6758,9 +6802,9 @@ react-native-vector-icons@^10.3.0:
|
||||
yargs "^16.1.1"
|
||||
|
||||
react-native-worklets@^0.10.2:
|
||||
version "0.10.2"
|
||||
resolved "https://registry.yarnpkg.com/react-native-worklets/-/react-native-worklets-0.10.2.tgz#c59e6314d2da4a69f20d66f58bb964ee2275328a"
|
||||
integrity sha512-LX27ejYI8veeDp59Z3rjo2pYyPa9euzSH8GUlem7cnNqfsDtGum8PQpkbzrqhLsWH0CjdeHR7p3sncCyYbwaVw==
|
||||
version "0.10.3"
|
||||
resolved "https://registry.yarnpkg.com/react-native-worklets/-/react-native-worklets-0.10.3.tgz#c4873813215207ba78cf2db9c4ff411e7be2c8cc"
|
||||
integrity sha512-NaDBNXc2l6p8ggNYJ2iMfD1Yov/PI1CTwL+d0Lczih18C0Tw37mY0sJd9DXi1zZNBeltAsWyZSSr1nOEdren3w==
|
||||
dependencies:
|
||||
"@babel/plugin-transform-arrow-functions" "^7.27.1"
|
||||
"@babel/plugin-transform-class-properties" "^7.28.6"
|
||||
@ -7081,9 +7125,9 @@ safe-regex-test@^1.1.0:
|
||||
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==
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.6.1.tgz#4c23cf608c0b693ab54b4b5888e92cfe977b9843"
|
||||
integrity sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==
|
||||
|
||||
scheduler@0.27.0, scheduler@^0.27.0:
|
||||
version "0.27.0"
|
||||
@ -7372,10 +7416,10 @@ stacktrace-parser@^0.1.10:
|
||||
dependencies:
|
||||
type-fest "^0.7.1"
|
||||
|
||||
standard-navigation@^0.0.7:
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/standard-navigation/-/standard-navigation-0.0.7.tgz#8c5265c4244446e0fb2b4bc9861a9979dd34f22f"
|
||||
integrity sha512-NCGLCNyuXrFOkGHxdNZFnpsehGtiq1oXbPhKl7ZuxFO5J//H2evqqOchmD4YwEUJnkjO4kH9Xp4hQX6hdAYCKQ==
|
||||
standard-navigation@^0.0.8:
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/standard-navigation/-/standard-navigation-0.0.8.tgz#aef79bef130ec8ba0a9c070d3a728d96d68df8a8"
|
||||
integrity sha512-TyVbo7INUDWtsUWDFn8RR7kwR87U0S4xHfLfbbnyeC581TmmyqQ+eM+nPw8rQTSD8QitRVcYfPaSHr/QJiUy1g==
|
||||
|
||||
statuses@~1.5.0:
|
||||
version "1.5.0"
|
||||
@ -7953,16 +7997,16 @@ write-file-atomic@^4.0.2:
|
||||
signal-exit "^3.0.7"
|
||||
|
||||
ws@^6.2.3:
|
||||
version "6.2.5"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.5.tgz#62a472481a10629989009e720af28431e7c2ba21"
|
||||
integrity sha512-T7pPl+DnmNrKRuttAIQwueReX5GqsedYEWp3/H//CG35+DyUOe1+/voAeE8idxgfLsQf2tO+rdtBd1FnPopgTQ==
|
||||
version "6.2.6"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.6.tgz#545a6a915b00b660937f7f2c3084d623fb2fc59f"
|
||||
integrity sha512-XTrf1gv7kXoVf1hbC3PAyAiPgR8Wz1blcrYIjEsUmr08BLksT41R8KbjmS9408C2ERx7v1JDLD/BkpLEttjfKA==
|
||||
dependencies:
|
||||
async-limiter "~1.0.0"
|
||||
|
||||
ws@^7, ws@^7.5.10:
|
||||
version "7.5.12"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.12.tgz#4ca2c04966db4dcd2f9bc4d1419d23cd1e4a18db"
|
||||
integrity sha512-1xGnbYN3zbog9CwuNDQULNRrTCLIn46/WmpR1f0w6PsCYQHkylZr5vkd6kfMZYV6pRnQkcPNRyiA8LsrNKyhpg==
|
||||
version "7.5.13"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.13.tgz#12aa507eaca76c295c278b1aebf4698ab2c1845f"
|
||||
integrity sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==
|
||||
|
||||
xcode@^3.0.1:
|
||||
version "3.0.1"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user