396 lines
15 KiB
TypeScript
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,
|
|
})
|