work on services tab

feature/app-setup
vinay kumar 2025-07-10 20:20:29 +05:30
parent d1fbd92b2e
commit 5cde8dbbc6
8 changed files with 773 additions and 17 deletions

View File

@ -8,6 +8,7 @@ import { useSnackbar } from "@/contexts/Snackbar";
import NetInfo from "@react-native-community/netinfo"; import NetInfo from "@react-native-community/netinfo";
import { getUserDetails } from "@/store/userSlice"; import { getUserDetails } from "@/store/userSlice";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import { View } from "react-native";
export default function TabLayout() { export default function TabLayout() {
const { isLoggedIn } = useSelector((state: RootState) => state.auth); const { isLoggedIn } = useSelector((state: RootState) => state.auth);
@ -70,6 +71,9 @@ export default function TabLayout() {
const IconComponent = focused ? IconFilled : Icon; const IconComponent = focused ? IconFilled : Icon;
return <IconComponent />; return <IconComponent />;
}, },
headerBackground: () => (
<View style={{ backgroundColor: "#252A34" }} />
),
}} }}
/> />
))} ))}

View File

@ -357,11 +357,11 @@ const styles = StyleSheet.create({
paddingBottom: 110, paddingBottom: 110,
}, },
iconContainer: { iconContainer: {
backgroundColor: "#fff", backgroundColor: "#F3F5F8",
}, },
headerTitleContainer: { headerTitleContainer: {
flexDirection: "column", flexDirection: "column",
backgroundColor: "#fff", backgroundColor: "#F3F5F8",
}, },
title: { title: {
fontSize: 14, fontSize: 14,
@ -378,7 +378,7 @@ const styles = StyleSheet.create({
alignItems: "center", alignItems: "center",
paddingRight: 16, paddingRight: 16,
gap: 8, gap: 8,
backgroundColor: "#fff", backgroundColor: "#F3F5F8",
}, },
badge: { badge: {
backgroundColor: "#FEE2E2", backgroundColor: "#FEE2E2",

View File

@ -1,27 +1,477 @@
import { StyleSheet } from "react-native"; import React, { JSX, useState } from "react";
import { Text, View } from "react-native"; import {
View,
Text,
TextInput,
TouchableOpacity,
Image,
StyleSheet,
GestureResponderEvent,
ScrollView,
} from "react-native";
import { Dropdown } from "react-native-element-dropdown";
import { DateTimePickerAndroid } from "@react-native-community/datetimepicker";
import * as ImagePicker from "expo-image-picker";
import { Formik, FormikHelpers } from "formik";
import * as Yup from "yup";
import ChevronRight from "../../assets/icons/chevron_rightside.svg";
import AddPhoto from "../../assets/icons/add_photo_alternate.svg";
import IssueSelectorModal from "@/components/service/IssueSelectorModal";
interface FormValues {
serviceType: string | null;
issues: string[];
comments: string | null;
date: Date | null;
photos: string[];
}
const validationSchema = Yup.object().shape({
serviceType: Yup.string().required("Service Type is required"),
issues: Yup.array().min(1, "At least one issue is required"),
date: Yup.date().required("Date and Time is required"),
photos: Yup.array().min(1, "At least one photo is required"),
comments: Yup.string(),
});
export default function ServiceFormScreen(): JSX.Element {
const [isFocus, setIsFocus] = useState<boolean>(false);
const [isIssueSelectorVisible, setIssueSelectorVisible] = useState(false);
function toggleIssueSelector() {
setIssueSelectorVisible(!isIssueSelectorVisible);
}
const initialValues: FormValues = {
serviceType: null,
issues: [],
comments: "",
date: null,
photos: [],
};
const handlePhotoPick = async (
setFieldValue: (field: string, value: any) => void,
currentPhotos: string[]
) => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
quality: 1,
allowsMultipleSelection: true,
});
if (!result.canceled) {
const newPhotos = result.assets.map((asset) => asset.uri);
const allPhotos = [...currentPhotos, ...newPhotos];
setFieldValue("photos", allPhotos);
}
};
const showPicker = (
currentDate: Date | null,
setFieldValue: (field: string, value: any) => void
) => {
const now = currentDate || new Date();
// First, show the date picker
DateTimePickerAndroid.open({
value: now,
mode: "date",
is24Hour: true,
display: "default",
onChange: (event, selectedDate) => {
if (event.type === "set" && selectedDate) {
// When date is selected, show time picker next
DateTimePickerAndroid.open({
value: selectedDate,
mode: "time",
is24Hour: true,
display: "default",
onChange: (timeEvent, selectedTime) => {
if (timeEvent.type === "set" && selectedTime) {
// Combine date and time into one Date object
const combinedDate = new Date(
selectedDate.getFullYear(),
selectedDate.getMonth(),
selectedDate.getDate(),
selectedTime.getHours(),
selectedTime.getMinutes()
);
setFieldValue("date", combinedDate);
}
},
});
}
},
});
};
export default function ServiceTabScreen() {
return ( return (
<View style={styles.container}> <>
<Text style={styles.title}>Coming Soon</Text> <ScrollView style={styles.screen}>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={(
values: FormValues,
actions: FormikHelpers<FormValues>
) => {
console.log(values);
}}
>
{({
handleChange,
handleBlur,
handleSubmit,
values,
setFieldValue,
errors,
touched,
}) => (
<View style={styles.formContainer}>
<View style={styles.inputContainer}>
<Text style={styles.label}>
Service Type <Text style={styles.required}>*</Text>
</Text>
<Dropdown
style={[
styles.dropdown,
isFocus && { borderColor: "#00be88" },
]}
placeholderStyle={styles.placeholderStyle}
selectedTextStyle={styles.selectedTextStyle}
inputSearchStyle={styles.inputSearchStyle}
iconStyle={styles.iconStyle}
data={[
{ label: "Regular", value: "Regular" },
{ label: "On-demand", value: "On-demand" },
]}
maxHeight={200}
labelField="label"
valueField="value"
placeholder={"-Select-"}
value={values.serviceType}
onFocus={() => setIsFocus(true)}
onBlur={() => setIsFocus(false)}
onChange={(item) => {
setFieldValue("serviceType", item.value);
setIsFocus(false);
}}
renderLeftIcon={() => (
<View style={{ marginRight: 10 }}>
{/* Add your icon component here if needed */}
</View> </View>
)}
/>
{touched.serviceType && errors.serviceType && (
<Text style={styles.error}>{errors.serviceType}</Text>
)}
</View>
<View style={{ marginTop: 8 }}>
<Text style={styles.label}>
Issues <Text style={styles.required}>*</Text>
</Text>
<TouchableOpacity
style={styles.inputBox}
onPress={toggleIssueSelector}
>
<Text style={styles.issueText}>
{values.issues.length > 0
? values.issues.length + " issues selected"
: "Select Issue"}
</Text>
<ChevronRight />
</TouchableOpacity>
{touched.issues && errors.issues && (
<Text style={styles.error}>{errors.issues}</Text>
)}
</View>
<View style={{ marginTop: 8 }}>
<Text style={styles.label}>
Select Date and Time <Text style={styles.required}>*</Text>
</Text>
<TouchableOpacity
onPress={() => showPicker(values.date, setFieldValue)}
style={styles.inputBoxDate}
>
<Text style={styles.dateText}>
{values.date && values.date.toLocaleString()}
</Text>
</TouchableOpacity>
{touched.date && errors.date && (
<Text style={styles.error}>{`${errors.date}`}</Text>
)}
</View>
<TouchableOpacity
style={styles.photoBox}
onPress={() => handlePhotoPick(setFieldValue, values.photos)}
>
<AddPhoto />
<Text style={styles.addPhotoText}>Add photos</Text>
</TouchableOpacity>
<Text style={styles.helperText}>
Supported formats include JPG, JPEG and PNG.
</Text>
{/* Selected Images Preview */}
<View style={styles.photosContainer}>
{values.photos.map((uri, index) => (
<View key={index} style={styles.photoPreviewContainer}>
<Image source={{ uri }} style={styles.photoPreview} />
<TouchableOpacity
style={styles.removePhotoButton}
onPress={() => {
const updatedPhotos = [...values.photos];
updatedPhotos.splice(index, 1);
setFieldValue("photos", updatedPhotos);
}}
>
<Text style={styles.removePhotoText}>×</Text>
</TouchableOpacity>
</View>
))}
</View>
{touched.photos && errors.photos && (
<Text style={styles.error}>{errors.photos}</Text>
)}
<Text style={styles.label}>Comments</Text>
<TextInput
style={styles.commentInput}
multiline
maxLength={100}
onChangeText={handleChange("comments")}
onBlur={handleBlur("comments")}
value={values.comments}
/>
<Text style={styles.wordCount}>
{values.comments.length}/100 words
</Text>
<TouchableOpacity
style={styles.submitButton}
onPress={handleSubmit as (e?: GestureResponderEvent) => void}
>
<Text style={styles.submitText}>Submit</Text>
</TouchableOpacity>
<IssueSelectorModal
visible={isIssueSelectorVisible}
onClose={toggleIssueSelector}
onSelect={(selectedIssues) => {
setFieldValue("issues", selectedIssues);
}}
/>
</View>
)}
</Formik>
</ScrollView>
</>
); );
} }
// styles stay unchanged
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { inputContainer: {},
screen: {
flex: 1, flex: 1,
alignItems: "center", backgroundColor: "#F3F5F8",
marginBottom: 116,
},
topBar: {
height: 56,
backgroundColor: "#F3F5F8",
justifyContent: "center", justifyContent: "center",
paddingHorizontal: 16,
}, },
title: { topBarText: {
fontSize: 20, fontSize: 16,
fontWeight: "600",
color: "#252A34",
},
formContainer: {
paddingHorizontal: 16,
},
label: {
fontSize: 14,
fontWeight: "500",
color: "#252A34",
marginBottom: 4,
},
required: {
color: "#D42210",
},
inputBox: {
backgroundColor: "#FFFFFF",
borderColor: "#D8DDE7",
borderRadius: 4,
paddingHorizontal: 8,
height: 40,
marginTop: 8,
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
},
inputBoxDate: {
backgroundColor: "#FFFFFF",
borderColor: "#D8DDE7",
borderWidth: 1,
borderRadius: 4,
paddingHorizontal: 8,
height: 40,
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
marginBottom: 4,
},
picker: {
height: 40,
width: "100%",
},
issueText: {
color: "#006C4D",
},
dateText: {
color: "#949CAC",
},
photoBox: {
backgroundColor: "#FFFFFF",
borderColor: "#949CAC",
borderWidth: 1,
borderRadius: 4,
height: 64,
justifyContent: "center",
alignItems: "center",
marginBottom: 8,
borderStyle: "dashed",
marginTop: 8,
},
addPhotoText: {
fontSize: 14,
color: "#252A34",
fontWeight: "500",
},
helperText: {
fontSize: 12,
color: "#717B8F",
marginBottom: 8,
},
commentInput: {
backgroundColor: "#FFFFFF",
borderColor: "#D8DDE7",
borderWidth: 1,
borderRadius: 4,
padding: 10,
height: 80,
textAlignVertical: "top",
},
wordCount: {
textAlign: "right",
color: "#717B8F",
fontSize: 14,
marginTop: 4,
},
submitButton: {
marginTop: 24,
backgroundColor: "#00875F",
borderRadius: 4,
height: 48,
justifyContent: "center",
alignItems: "center",
},
submitText: {
color: "#FCFCFC",
fontSize: 14,
fontWeight: "500",
},
photo: {
width: 80,
height: 80,
resizeMode: "cover",
},
error: {
color: "#D42210",
fontSize: 12,
marginBottom: 8,
},
photosContainer: {
flexDirection: "row",
flexWrap: "wrap",
marginTop: 8,
gap: 8,
},
photoPreviewContainer: {
position: "relative",
},
photoPreview: {
width: 80,
height: 80,
borderRadius: 4,
},
removePhotoButton: {
position: "absolute",
top: -8,
right: -8,
backgroundColor: "#D42210",
borderRadius: 12,
width: 24,
height: 24,
justifyContent: "center",
alignItems: "center",
},
removePhotoText: {
color: "white",
fontSize: 18,
fontWeight: "bold", fontWeight: "bold",
lineHeight: 20,
}, },
separator: { dropdown: {
marginVertical: 30, height: 40,
height: 1, borderColor: "#D8DDE7",
width: "80%", borderWidth: 1,
borderRadius: 4,
paddingHorizontal: 8,
backgroundColor: "#FFFFFF",
marginTop: 8,
},
placeholderStyle: {
fontSize: 14,
color: "#252A34",
},
selectedTextStyle: {
fontSize: 14,
color: "#252A34",
},
iconStyle: {
width: 20,
height: 20,
},
inputSearchStyle: {
height: 40,
fontSize: 16,
},
dropdownMenu: {
backgroundColor: "#FCFCFC",
borderRadius: 4,
shadowColor: "#0E1118",
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.1,
shadowRadius: 32,
elevation: 4,
},
dropdownItem: {
padding: 16,
height: 36,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
},
dropdownItemText: {
fontSize: 14,
color: "#252A34",
}, },
}); });

View File

@ -0,0 +1,8 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_14_5357" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="18" height="18">
<rect width="18" height="18" fill="#D9D9D9"/>
</mask>
<g mask="url(#mask0_14_5357)">
<path d="M4.04922 15.3C3.67797 15.3 3.36016 15.1678 3.09578 14.9035C2.83141 14.6391 2.69922 14.3213 2.69922 13.95V4.05001C2.69922 3.67876 2.83141 3.36095 3.09578 3.09658C3.36016 2.8322 3.67797 2.70001 4.04922 2.70001H9.52422C9.74922 2.70001 9.92422 2.80001 10.0492 3.00001C10.1742 3.20001 10.1867 3.40764 10.0867 3.62289C10.0242 3.79514 9.97734 3.97501 9.94609 4.16251C9.91484 4.35001 9.89922 4.53926 9.89922 4.73026C9.89922 5.66251 10.2278 6.4572 10.8849 7.11433C11.542 7.77145 12.3367 8.10001 13.269 8.10001C13.46 8.10001 13.6492 8.08439 13.8367 8.05314C14.0242 8.02189 14.2068 7.97764 14.3846 7.92039C14.6068 7.84014 14.8148 7.85939 15.0086 7.97814C15.2023 8.09689 15.2992 8.26251 15.2992 8.47501V13.95C15.2992 14.3213 15.167 14.6391 14.9027 14.9035C14.6383 15.1678 14.3205 15.3 13.9492 15.3H4.04922ZM4.94922 12.6H13.0492L10.3492 9.00001L8.32422 11.7L6.97422 9.90001L4.94922 12.6ZM13.2782 6.75001C13.088 6.75001 12.9281 6.68533 12.7983 6.55595C12.6685 6.42658 12.6035 6.26626 12.6035 6.07501V5.40001H11.9265C11.7346 5.40001 11.5737 5.33533 11.4438 5.20595C11.3141 5.07658 11.2492 4.91626 11.2492 4.72501C11.2492 4.53376 11.3141 4.37345 11.4438 4.24407C11.5737 4.1147 11.7346 4.05001 11.9265 4.05001H12.6035V3.37501C12.6035 3.18376 12.6685 3.02345 12.7983 2.89407C12.9281 2.7647 13.088 2.70001 13.2782 2.70001C13.4683 2.70001 13.6277 2.7647 13.7563 2.89407C13.8849 3.02345 13.9492 3.18376 13.9492 3.37501V4.05001H14.6203C14.8104 4.05001 14.9711 4.11432 15.1023 4.24295C15.2336 4.37157 15.2992 4.53095 15.2992 4.72107C15.2992 4.9112 15.2345 5.07095 15.1052 5.20032C14.9758 5.3297 14.8155 5.39439 14.6242 5.39439H13.9492V6.06939C13.9492 6.26064 13.8849 6.42189 13.7563 6.55314C13.6277 6.68439 13.4683 6.75001 13.2782 6.75001Z" fill="#565F70"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,170 @@
import React, { useState } from "react";
import {
View,
Text,
TextInput,
ScrollView,
TouchableOpacity,
Modal,
StyleSheet,
} from "react-native";
import Checkbox from "expo-checkbox";
import { issueConfig } from "@/constants/config";
interface IssueSelectorModalProps {
visible: boolean;
onClose: () => void;
onSelect: (selectedValues: string[]) => void;
}
export default function IssueSelectorModal({
visible,
onClose,
onSelect,
}: IssueSelectorModalProps) {
const [selectedValues, setSelectedValues] = useState<string[]>([]);
const [search, setSearch] = useState("");
const toggleValue = (value: string) => {
setSelectedValues((prev) =>
prev.includes(value) ? prev.filter((v) => v !== value) : [...prev, value]
);
};
const filteredConfig = issueConfig
.map((group) => ({
...group,
options: group.options.filter((option) =>
option.label.toLowerCase().includes(search.toLowerCase())
),
}))
.filter((group) => group.options.length > 0);
const clearSelection = () => setSelectedValues([]);
return (
<Modal visible={visible} animationType="slide">
<View style={styles.container}>
{/* Header */}
<View style={styles.header}>
<TextInput
placeholder="Search"
placeholderTextColor="#939DAE"
value={search}
onChangeText={setSearch}
style={styles.searchBar}
/>
</View>
{/* Selection Counter and Clear Button */}
<View style={styles.counterBar}>
<Text
style={styles.counterText}
>{`${selectedValues.length}/23 Selected`}</Text>
<TouchableOpacity onPress={clearSelection}>
<Text style={styles.clearText}>Clear</Text>
</TouchableOpacity>
</View>
<ScrollView style={styles.scrollArea}>
{filteredConfig.map((group) => (
<View key={group.category}>
<Text style={styles.category}>{group.category}</Text>
{group.options.map((option) => (
<TouchableOpacity
key={option.value}
style={styles.itemRow}
onPress={() => toggleValue(option.value)}
>
<Checkbox
value={selectedValues.includes(option.value)}
onValueChange={() => toggleValue(option.value)}
color={
selectedValues.includes(option.value)
? "#252A34"
: undefined
}
/>
<Text style={styles.itemLabel}>{option.label}</Text>
</TouchableOpacity>
))}
</View>
))}
</ScrollView>
{/* Done Button */}
<TouchableOpacity
style={styles.doneButton}
onPress={() => {
onSelect(selectedValues);
onClose();
}}
>
<Text style={styles.doneText}>Done</Text>
</TouchableOpacity>
</View>
</Modal>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: "#fff" },
header: {
paddingHorizontal: 16,
paddingVertical: 12,
},
searchBar: {
backgroundColor: "#fff",
borderColor: "#D8DDE6",
borderWidth: 1,
borderRadius: 4,
paddingHorizontal: 12,
height: 36,
fontSize: 14,
color: "#252A34",
},
counterBar: {
flexDirection: "row",
justifyContent: "space-between",
paddingHorizontal: 20,
paddingVertical: 12,
},
counterText: {
fontSize: 12,
color: "#555",
},
clearText: {
fontSize: 14,
color: "#ADB4BD",
},
scrollArea: { paddingHorizontal: 0 },
category: {
fontSize: 14,
color: "#252A34",
paddingVertical: 12,
paddingHorizontal: 16,
backgroundColor: "#F9F9F9",
},
itemRow: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 8,
paddingHorizontal: 16,
backgroundColor: "#FCFCFC",
},
itemLabel: {
marginLeft: 12,
fontSize: 12,
color: "#252A34",
},
doneButton: {
backgroundColor: "#00875F",
padding: 16,
alignItems: "center",
},
doneText: {
color: "#fff",
fontSize: 14,
fontWeight: "bold",
},
});

View File

@ -109,3 +109,69 @@ export const AWS = {
BUCKET_NAME: "battery-as-a-service", BUCKET_NAME: "battery-as-a-service",
REGION: "us-east-1", REGION: "us-east-1",
}; };
export const issueConfig = [
{
category: "Physical Damage",
options: [
{ label: "Top cover crack/damage", value: "top_cover_damage" },
{ label: "Chogori connector damage", value: "chogori_connector_damage" },
{ label: "Flip handle damage/missing", value: "flip_handle_damage" },
{
label: "Handle gripper damage/missing",
value: "handle_gripper_damage",
},
{ label: "TCU cover damage", value: "tcu_cover_damage" },
{ label: "Gore Vent damage", value: "gore_vent_damage" },
],
},
{
category: "Water Ingress Issues",
options: [
{ label: "BMS water ingress", value: "bms_water_ingress" },
{ label: "SD card water ingress", value: "sd_card_water_ingress" },
{ label: "Cell pack water ingress", value: "cell_pack_water_ingress" },
{
label: "Battery completely submerged in water",
value: "battery_submerged",
},
],
},
{
category: "Electrical & Functional Failures",
options: [
{ label: "BMS failure/card burnt", value: "bms_failure" },
{
label: "SD card failure/data unable to copy/SD card not detected",
value: "sd_card_failure",
},
{ label: "Deep discharge (DD)", value: "deep_discharge" },
{ label: "Cell imbalance", value: "cell_imbalance" },
{ label: "CT sensing faulty", value: "ct_sensing_faulty" },
{ label: "TCU hang", value: "tcu_hang" },
],
},
{
category: "Alarms & Warnings",
options: [
{ label: "UV1, UV2 alarms (Under Voltage)", value: "uv1_uv2_alarm" },
{ label: "EOD (End of Discharge)", value: "eod_alarm" },
{
label: "Charge & discharge OT (Over Temperature) alarm",
value: "ot_alarm",
},
],
},
{
category: "Other Issues",
options: [
{ label: "BMS full dust formed", value: "bms_dust" },
{ label: "NFF (No Fault Found)", value: "nff" },
{
label: "Under warranty (not an issue, but listed in data)",
value: "under_warranty",
},
{ label: "NA (Not Applicable)", value: "not_applicable" },
],
},
];

55
package-lock.json generated
View File

@ -10,12 +10,15 @@
"dependencies": { "dependencies": {
"@expo/vector-icons": "^14.1.0", "@expo/vector-icons": "^14.1.0",
"@react-native-async-storage/async-storage": "^2.2.0", "@react-native-async-storage/async-storage": "^2.2.0",
"@react-native-community/datetimepicker": "^8.4.2",
"@react-native-community/netinfo": "^11.4.1", "@react-native-community/netinfo": "^11.4.1",
"@react-native-picker/picker": "^2.11.1",
"@react-navigation/native": "^7.1.6", "@react-navigation/native": "^7.1.6",
"@react-navigation/stack": "^7.4.2", "@react-navigation/stack": "^7.4.2",
"@reduxjs/toolkit": "^2.8.2", "@reduxjs/toolkit": "^2.8.2",
"axios": "^1.10.0", "axios": "^1.10.0",
"expo": "^53.0.13", "expo": "^53.0.13",
"expo-checkbox": "^4.1.4",
"expo-constants": "~17.1.6", "expo-constants": "~17.1.6",
"expo-font": "~13.3.1", "expo-font": "~13.3.1",
"expo-image-picker": "~16.1.4", "expo-image-picker": "~16.1.4",
@ -3349,6 +3352,29 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/@react-native-community/datetimepicker": {
"version": "8.4.2",
"resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-8.4.2.tgz",
"integrity": "sha512-V/s+foBfjlWGV8MKdMhxugq0SPMtYqUEYlf+sMrKUUm5Gx3pA9Qoum2ZQUqBfI4A8kgaEPIGyG/YsNX7ycnNSA==",
"license": "MIT",
"dependencies": {
"invariant": "^2.2.4"
},
"peerDependencies": {
"expo": ">=52.0.0",
"react": "*",
"react-native": "*",
"react-native-windows": "*"
},
"peerDependenciesMeta": {
"expo": {
"optional": true
},
"react-native-windows": {
"optional": true
}
}
},
"node_modules/@react-native-community/netinfo": { "node_modules/@react-native-community/netinfo": {
"version": "11.4.1", "version": "11.4.1",
"resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-11.4.1.tgz", "resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-11.4.1.tgz",
@ -3358,6 +3384,19 @@
"react-native": ">=0.59" "react-native": ">=0.59"
} }
}, },
"node_modules/@react-native-picker/picker": {
"version": "2.11.1",
"resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.11.1.tgz",
"integrity": "sha512-ThklnkK4fV3yynnIIRBkxxjxR4IFbdMNJVF6tlLdOJ/zEFUEFUEdXY0KmH0iYzMwY8W4/InWsLiA7AkpAbnexA==",
"license": "MIT",
"workspaces": [
"example"
],
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/@react-native/assets-registry": { "node_modules/@react-native/assets-registry": {
"version": "0.79.4", "version": "0.79.4",
"resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.79.4.tgz", "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.79.4.tgz",
@ -7003,6 +7042,22 @@
"react-native": "*" "react-native": "*"
} }
}, },
"node_modules/expo-checkbox": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/expo-checkbox/-/expo-checkbox-4.1.4.tgz",
"integrity": "sha512-sahBTVble5/6EnHgLyGvX6fAytkZ7vmllHUbX5ko1kTQ59qTdiVmCznxqaT5DNWfxRZ0gdQVlao46dGQ3hbmeQ==",
"license": "MIT",
"peerDependencies": {
"react": "*",
"react-native": "*",
"react-native-web": "*"
},
"peerDependenciesMeta": {
"react-native-web": {
"optional": true
}
}
},
"node_modules/expo-constants": { "node_modules/expo-constants": {
"version": "17.1.6", "version": "17.1.6",
"resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-17.1.6.tgz", "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-17.1.6.tgz",

View File

@ -15,12 +15,15 @@
"dependencies": { "dependencies": {
"@expo/vector-icons": "^14.1.0", "@expo/vector-icons": "^14.1.0",
"@react-native-async-storage/async-storage": "^2.2.0", "@react-native-async-storage/async-storage": "^2.2.0",
"@react-native-community/datetimepicker": "^8.4.2",
"@react-native-community/netinfo": "^11.4.1", "@react-native-community/netinfo": "^11.4.1",
"@react-native-picker/picker": "^2.11.1",
"@react-navigation/native": "^7.1.6", "@react-navigation/native": "^7.1.6",
"@react-navigation/stack": "^7.4.2", "@react-navigation/stack": "^7.4.2",
"@reduxjs/toolkit": "^2.8.2", "@reduxjs/toolkit": "^2.8.2",
"axios": "^1.10.0", "axios": "^1.10.0",
"expo": "^53.0.13", "expo": "^53.0.13",
"expo-checkbox": "^4.1.4",
"expo-constants": "~17.1.6", "expo-constants": "~17.1.6",
"expo-font": "~13.3.1", "expo-font": "~13.3.1",
"expo-image-picker": "~16.1.4", "expo-image-picker": "~16.1.4",