import React, { JSX, useState } from "react"; import { View, Text, TextInput, TouchableOpacity, Image, StyleSheet, GestureResponderEvent, ScrollView, KeyboardAvoidingView, Platform, ActivityIndicator, } 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"; import { uploadImage } from "@/utils/User"; import api from "@/services/axiosClient"; import { useSnackbar } from "@/contexts/Snackbar"; import { BASE_URL } from "@/constants/config"; import { Overlay } from "@/components/common/Overlay"; import CrossIcon from "@/assets/icons/close_white.svg"; import { useTranslation } from "react-i18next"; import CalendarIcon from "@/assets/icons/calendar.svg"; interface FormValues { serviceType: string | null; issues: number[]; comments: string | null; date: Date | null; photos: string[]; } interface Issue { id: number; name: string; } const validationSchema = Yup.object().shape({ serviceType: Yup.string().required("Service Type is required"), issues: Yup.array().when("serviceType", { is: (val: string) => val !== "Regular", then: (schema) => schema.min(1, "At least one issue is required"), otherwise: (schema) => schema.notRequired(), }), 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(false); const [isIssueSelectorVisible, setIssueSelectorVisible] = useState(false); const [issues, setIssues] = useState([]); const [isLoadingIssues, setIsLoadingIssues] = useState(false); const { showSnackbar } = useSnackbar(); function toggleIssueSelector() { setIssueSelectorVisible(!isIssueSelectorVisible); } const fetchIssues = async () => { try { setIsLoadingIssues(true); const response = await api.get(`${BASE_URL}/api/v1/service-issue-list`); if (response.data.success) { setIssues(response.data.data); } else { throw new Error(response.data?.message || "Failed to fetch issues"); } } catch (error: any) { console.error("Error fetching issues:", error); showSnackbar(`${t("service.something-went-wrong")}`, "error"); setIssues([]); } finally { setIsLoadingIssues(false); } }; 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: 0.5, 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: false, display: "default", minimumDate: now, onChange: (event, selectedDate) => { if (event.type === "set" && selectedDate) { // When date is selected, show time picker next DateTimePickerAndroid.open({ value: selectedDate, mode: "time", is24Hour: false, 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() ); if (combinedDate < now) { showSnackbar(`${t("service.select-valid-time")}`, "error"); return; } setFieldValue("date", combinedDate); } }, }); } }, }); }; const { t } = useTranslation(); const handleServiceTypeChange = async ( item: { label: string; value: string }, setFieldValue: (field: string, value: any) => void, setFieldTouched: (field: string, touched: boolean) => void ) => { setFieldValue("serviceType", item.value); if (item.value === "Regular") { setFieldValue("issues", []); setFieldTouched("issues", false); setIssues([]); } else if (item.value === "On-demand") { await fetchIssues(); setFieldValue("issues", []); setFieldTouched("issues", false); } setIsFocus(false); }; const getSelectedIssuesText = (selectedIssueIds: number[]) => { if (selectedIssueIds.length === 0) return `${t("service.select-issue")}`; if (selectedIssueIds.length === 1) return "1 issue selected"; return `${selectedIssueIds.length} issues selected`; }; return ( ) => { try { const uploadedPhotoUrls: string[] = []; for (const uri of values.photos) { const uploadedUrl = await uploadImage(uri); uploadedPhotoUrls.push(uploadedUrl); } console.log("IMAGES UPLOADED"); const payload = { service_type: values.serviceType, issue_types: values.issues, scheduled_time: values.date?.toISOString(), photos: uploadedPhotoUrls, comments: values.comments, }; const response = await api.post( `${BASE_URL}/api/v1/schedule-maintenance`, payload ); if (!response.data.success) { console.log(response.data?.message || "Submission failed"); throw new Error(response.data?.message || "Submission failed"); } console.log("Submission successful:", response.data); showSnackbar( `${t("service.service-request-success")}`, "success" ); actions.resetForm(); } catch (error: any) { console.error("Error during submission:", error); showSnackbar(`${t("service.something-went-wrong")}`, "error"); } finally { actions.setSubmitting(false); } }} > {({ handleChange, handleBlur, handleSubmit, values, setFieldValue, errors, touched, isSubmitting, setFieldTouched, }) => ( {t("service.service-type")}{" "} * setIsFocus(true)} onBlur={() => setIsFocus(false)} onChange={(item) => handleServiceTypeChange( item, setFieldValue, setFieldTouched ) } renderLeftIcon={() => ( {/* Add your icon component here if needed */} )} /> {touched.serviceType && errors.serviceType && ( {errors.serviceType} )} {t("service.issue")} * {isLoadingIssues ? ( ) : ( getSelectedIssuesText(values.issues) )} {touched.issues && errors.issues && ( {errors.issues} )} {t("service.select-datetime")}{" "} * showPicker(values.date, setFieldValue)} style={styles.inputBoxDate} > {values.date ? values.date.toLocaleString() : `${t("service.select")}`} {touched.date && errors.date && ( {`${errors.date}`} )} handlePhotoPick(setFieldValue, values.photos)} > {t("service.add-photos")}{" "} {t("service.supported-formats")} {/* Selected Images Preview */} {values.photos.map((uri, index) => ( { const updatedPhotos = [...values.photos]; updatedPhotos.splice(index, 1); setFieldValue("photos", updatedPhotos); }} > ))} {touched.photos && errors.photos && ( {errors.photos} )} {t("service.comments")} {values.comments?.length || 0}/100 {t("service.words")} void} > {t("service.submit")} { setFieldValue("issues", selectedIssues); }} initialSelectedValues={values.issues} issues={issues} /> {isSubmitting && } )} ); } const styles = StyleSheet.create({ issueTextDisabled: { color: "#949CAC", }, container: { flex: 1, }, inputContainer: {}, screen: { flex: 1, backgroundColor: "#F3F5F8", }, scrollContent: { paddingBottom: 116, }, topBar: { height: 56, backgroundColor: "#F3F5F8", justifyContent: "center", paddingHorizontal: 16, }, 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", marginTop: 8, }, 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: 4, right: 4, backgroundColor: "#252A345C", borderRadius: 12, width: 24, height: 24, justifyContent: "center", alignItems: "center", }, removePhotoText: { color: "white", fontSize: 18, fontWeight: "bold", lineHeight: 20, }, 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", }, });