Files
ProcureLinc-Mobile-App/src/Screens/Bidding/Component/TemplateFormModal.tsx
T

396 lines
15 KiB
TypeScript

import React, { useState, useMemo } from 'react'
import { Modal, View, StyleSheet, TextInput, TouchableOpacity, ScrollView, Platform } from 'react-native'
import { Formik } from 'formik'
import * as Yup from 'yup'
import { pick } from '@react-native-documents/picker'
import { useSelector } from 'react-redux'
import { apiCall } from '../../../Axios/Axios'
import { TECHNICAL_DOCUMENT_SUBMIT, TECHNICAL_VALUE_SUBMIT } from '../../../Services/ApiCall/ApiUrls'
import { Props } from '../Type/BidType'
import { Loader } from '../../../Component/Index'
import { useNavigation } from '@react-navigation/native'
import NavigationString from '../../../Constant/NavigationString'
import { useTheme } from '../../../Hooks/useTheme'
import AppText from '../../../components/AppText'
import { Upload, FileText, X } from 'lucide-react-native'
const TemplateFormModal: React.FC<Props> = ({ visible, template, setAllBoolean }) => {
if (!template) return null
const SelectedAuction = useSelector((state: any) => state.auction.auction)
const Navigation: any = useNavigation()
const { colors, spacing, typography } = useTheme()
const styles = useMemo(() => makeStyles(colors, spacing, typography), [colors, spacing, typography])
const [loading, setLoading] = useState(false)
const initialValues = template?.labels?.reduce((acc: any, label: any) => {
acc[label.labelName] = ''
return acc
}, {})
const validationSchema = Yup.object(
template.labels.reduce((acc: any, label: any) => {
if (label.mandatory) {
acc[label.labelName] =
label.labelType === '3'
? Yup.mixed().required(`${label.labelName} is required`)
: Yup.string().required(`${label.labelName} is required`)
}
return acc
}, {})
)
const pickFile = async (fieldName: string, setFieldValue: any) => {
try {
const result = await pick({
type: ['application/pdf'],
allowMultiSelection: false,
})
const file = result[0]
setFieldValue(fieldName, {
name: file.name ?? 'document.pdf',
uri: file.uri,
type: 'application/pdf',
size: file.size,
})
} catch (e) {
console.log(e)
}
}
const renderInput = (
label: any,
value: any,
setFieldValue: any,
error?: string,
touched?: boolean
) => {
const showError = touched && error
if (label.labelType === '3') {
return (
<View key={label.labelName} style={styles.field}>
<AppText variant="caption" color={colors.textSecondary} style={styles.label as any}>
{label.labelName}
{label.mandatory && <AppText variant="caption" color={colors.error}> *</AppText>}
</AppText>
<TouchableOpacity
style={[
styles.filePicker,
showError && styles.errorBorder,
]}
onPress={() =>
pickFile(label.labelName, setFieldValue)
}
>
{value?.name ? (
<View style={styles.fileSelectedRow}>
<FileText size={14} color={colors.success} strokeWidth={2} />
<AppText variant="caption" color={colors.text} bold style={{ flex: 1 } as any} numberOfLines={1}>
{value.name}
</AppText>
</View>
) : (
<View style={styles.fileSelectedRow}>
<Upload size={14} color={colors.textSecondary} strokeWidth={2} />
<AppText variant="caption" color={colors.textSecondary}>
Upload PDF
</AppText>
</View>
)}
</TouchableOpacity>
{showError && (
<AppText variant="caption" color={colors.error} style={styles.errorText as any}>{error}</AppText>
)}
</View>
)
}
return (
<View key={label.labelName} style={styles.field}>
<AppText variant="caption" color={colors.textSecondary} style={styles.label as any}>
{label.labelName}
{label.mandatory && <AppText variant="caption" color={colors.error}> *</AppText>}
</AppText>
<TextInput
style={[
styles.input,
showError && styles.errorBorder,
]}
placeholder={`Enter ${label.labelName}`}
placeholderTextColor={colors.textSecondary}
value={value}
onChangeText={(text) =>
setFieldValue(label.labelName, text)
}
keyboardType={
label.labelType === '1' || label.labelType === '4'
? 'numeric'
: 'default'
}
/>
{showError && (
<AppText variant="caption" color={colors.error} style={styles.errorText as any}>{error}</AppText>
)}
</View>
)
}
const onClose = () => {
setAllBoolean((prev: any) => ({
...prev,
technicalDocModalVisible: false,
}))
}
const onSubmit = async (payload: any) => {
try {
const jsonBody = {
auctiontId: SelectedAuction.auctionId,
technicalDocumentTemplateId: payload.templateNameId,
labelValues: [] as any[],
}
const fileFormData = new FormData()
let hasFile = false
Object.entries(payload.values).forEach(
([labelName, labelValue]: any) => {
if (typeof labelValue === 'object' && labelValue?.uri) {
hasFile = true
fileFormData.append('files', {
uri: labelValue.uri,
name: labelValue.name,
type: labelValue.type,
} as any)
} else {
jsonBody.labelValues.push({
labelName,
labelValue: String(labelValue),
})
}
}
)
const rs1: any = await apiCall('post', TECHNICAL_VALUE_SUBMIT, jsonBody)
if (hasFile) {
fileFormData.append(
'auctionTemplateValueIds',
String(rs1?.data[0]?.valueId)
)
fileFormData.append(
'templateNameId',
String(payload.templateNameId)
)
const res2: any = await apiCall(
'post',
TECHNICAL_DOCUMENT_SUBMIT,
fileFormData,
{},
'multipart/form-data'
)
if (rs1.statusCode == 200 && res2.statusCode == 200) {
Navigation.navigate(NavigationString.StartBidding)
setAllBoolean((prev: any) => ({ ...prev, technicalDocModalVisible: false }))
}
} else {
if (rs1.statusCode == 200) {
Navigation.navigate(NavigationString.StartBidding)
setAllBoolean((prev: any) => ({ ...prev, technicalDocModalVisible: false }))
}
}
} catch (e) {
console.log(e)
} finally {
setLoading(false)
}
}
return (
<Modal visible={visible} animationType="fade" transparent>
<View style={styles.screen}>
<View style={styles.header}>
<View style={styles.headerLeft}>
<AppText variant="h4" color={colors.text}>Technical Documents</AppText>
<AppText variant="caption" color={colors.textSecondary}>
Fill the form, Click save when you're done.
</AppText>
</View>
<TouchableOpacity onPress={onClose} style={styles.closeBtn}>
<X size={20} color={colors.textSecondary} />
</TouchableOpacity>
</View>
<ScrollView contentContainerStyle={styles.content}>
<View style={styles.card}>
<AppText variant="h3" color={colors.text} centered style={{ marginBottom: 16 } as any}>Template</AppText>
<View style={styles.templateBox}>
<AppText variant="bodySmall" color={colors.text} bold style={{ marginBottom: 12 } as any}>
{template.templateName}
</AppText>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={(values) => {
setLoading(true)
onSubmit({
templateNameId: template.templateNameId,
auctionIds: template.auctionIds,
values,
})
}}
>
{({ handleSubmit, values, errors, touched, setFieldValue }) => (
<>
{template.labels.map((label: any) =>
renderInput(
label,
values[label.labelName],
setFieldValue,
// errors[label.labelName],
// touched[label.labelName]
)
)}
<View style={styles.submitRow}>
<TouchableOpacity
style={styles.submitBtn}
onPress={() => handleSubmit()}
disabled={loading}
>
{loading ? (
<Loader
size="small"
color="#fff"
/>
) : (
<AppText variant="button" color="#fff" bold style={styles.submitText as any}>
Submit
</AppText>
)}
</TouchableOpacity>
</View>
</>
)}
</Formik>
</View>
</View>
</ScrollView>
</View>
</Modal>
)
}
export default TemplateFormModal
const makeStyles = (colors: any, spacing: any, typography: any) =>
StyleSheet.create({
screen: {
flex: 1,
backgroundColor: colors.background,
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 16,
paddingVertical: 14,
backgroundColor: colors.surface,
borderBottomWidth: 1,
borderColor: colors.border,
},
headerLeft: {
flex: 1,
},
closeBtn: {
padding: 8,
backgroundColor: colors.overlay,
borderRadius: 10,
},
content: {
paddingHorizontal: 16,
},
card: {
backgroundColor: colors.surface,
borderRadius: 18,
marginTop: spacing.m,
padding: spacing.m,
...Platform.select({
ios: { shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.06, shadowRadius: 12 },
android: { elevation: 3 },
}),
},
templateBox: {
borderWidth: 1,
borderColor: colors.border,
borderRadius: 14,
padding: 16,
},
field: {
marginBottom: 14,
},
label: {
letterSpacing: 0.5,
textTransform: 'uppercase',
marginBottom: 6,
} as any,
input: {
backgroundColor: colors.background,
borderWidth: 1.2,
borderColor: colors.border,
borderRadius: 12,
padding: 12,
fontSize: 14,
color: colors.text,
},
filePicker: {
borderWidth: 1.2,
borderColor: colors.border,
backgroundColor: colors.background,
borderRadius: 12,
padding: 12,
},
fileSelectedRow: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
},
errorText: {
marginTop: 4,
} as any,
errorBorder: {
borderColor: colors.error,
},
submitRow: {
marginTop: 20,
alignItems: 'flex-end',
height: 50,
},
submitBtn: {
backgroundColor: colors.primary,
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 14,
minWidth: 120,
alignItems: 'center',
},
submitText: {
letterSpacing: 0.5,
} as any,
})