work on services tab
parent
d1fbd92b2e
commit
5cde8dbbc6
|
|
@ -8,6 +8,7 @@ import { useSnackbar } from "@/contexts/Snackbar";
|
|||
import NetInfo from "@react-native-community/netinfo";
|
||||
import { getUserDetails } from "@/store/userSlice";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function TabLayout() {
|
||||
const { isLoggedIn } = useSelector((state: RootState) => state.auth);
|
||||
|
|
@ -70,6 +71,9 @@ export default function TabLayout() {
|
|||
const IconComponent = focused ? IconFilled : Icon;
|
||||
return <IconComponent />;
|
||||
},
|
||||
headerBackground: () => (
|
||||
<View style={{ backgroundColor: "#252A34" }} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -357,11 +357,11 @@ const styles = StyleSheet.create({
|
|||
paddingBottom: 110,
|
||||
},
|
||||
iconContainer: {
|
||||
backgroundColor: "#fff",
|
||||
backgroundColor: "#F3F5F8",
|
||||
},
|
||||
headerTitleContainer: {
|
||||
flexDirection: "column",
|
||||
backgroundColor: "#fff",
|
||||
backgroundColor: "#F3F5F8",
|
||||
},
|
||||
title: {
|
||||
fontSize: 14,
|
||||
|
|
@ -378,7 +378,7 @@ const styles = StyleSheet.create({
|
|||
alignItems: "center",
|
||||
paddingRight: 16,
|
||||
gap: 8,
|
||||
backgroundColor: "#fff",
|
||||
backgroundColor: "#F3F5F8",
|
||||
},
|
||||
badge: {
|
||||
backgroundColor: "#FEE2E2",
|
||||
|
|
|
|||
|
|
@ -1,27 +1,477 @@
|
|||
import { StyleSheet } from "react-native";
|
||||
import { Text, View } from "react-native";
|
||||
import React, { JSX, useState } from "react";
|
||||
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 (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Coming Soon</Text>
|
||||
</View>
|
||||
<>
|
||||
<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>
|
||||
)}
|
||||
/>
|
||||
{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({
|
||||
container: {
|
||||
inputContainer: {},
|
||||
screen: {
|
||||
flex: 1,
|
||||
alignItems: "center",
|
||||
backgroundColor: "#F3F5F8",
|
||||
marginBottom: 116,
|
||||
},
|
||||
topBar: {
|
||||
height: 56,
|
||||
backgroundColor: "#F3F5F8",
|
||||
justifyContent: "center",
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
title: {
|
||||
fontSize: 20,
|
||||
topBarText: {
|
||||
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",
|
||||
lineHeight: 20,
|
||||
},
|
||||
separator: {
|
||||
marginVertical: 30,
|
||||
height: 1,
|
||||
width: "80%",
|
||||
dropdown: {
|
||||
height: 40,
|
||||
borderColor: "#D8DDE7",
|
||||
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",
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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 |
|
|
@ -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",
|
||||
},
|
||||
});
|
||||
|
|
@ -109,3 +109,69 @@ export const AWS = {
|
|||
BUCKET_NAME: "battery-as-a-service",
|
||||
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" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -10,12 +10,15 @@
|
|||
"dependencies": {
|
||||
"@expo/vector-icons": "^14.1.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-picker/picker": "^2.11.1",
|
||||
"@react-navigation/native": "^7.1.6",
|
||||
"@react-navigation/stack": "^7.4.2",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
"axios": "^1.10.0",
|
||||
"expo": "^53.0.13",
|
||||
"expo-checkbox": "^4.1.4",
|
||||
"expo-constants": "~17.1.6",
|
||||
"expo-font": "~13.3.1",
|
||||
"expo-image-picker": "~16.1.4",
|
||||
|
|
@ -3349,6 +3352,29 @@
|
|||
"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": {
|
||||
"version": "11.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-11.4.1.tgz",
|
||||
|
|
@ -3358,6 +3384,19 @@
|
|||
"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": {
|
||||
"version": "0.79.4",
|
||||
"resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.79.4.tgz",
|
||||
|
|
@ -7003,6 +7042,22 @@
|
|||
"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": {
|
||||
"version": "17.1.6",
|
||||
"resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-17.1.6.tgz",
|
||||
|
|
|
|||
|
|
@ -15,12 +15,15 @@
|
|||
"dependencies": {
|
||||
"@expo/vector-icons": "^14.1.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-picker/picker": "^2.11.1",
|
||||
"@react-navigation/native": "^7.1.6",
|
||||
"@react-navigation/stack": "^7.4.2",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
"axios": "^1.10.0",
|
||||
"expo": "^53.0.13",
|
||||
"expo-checkbox": "^4.1.4",
|
||||
"expo-constants": "~17.1.6",
|
||||
"expo-font": "~13.3.1",
|
||||
"expo-image-picker": "~16.1.4",
|
||||
|
|
|
|||
Loading…
Reference in New Issue