648 lines
22 KiB
TypeScript
648 lines
22 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import {
|
|
View,
|
|
Text,
|
|
FlatList,
|
|
TouchableOpacity,
|
|
StyleSheet,
|
|
TextInput,
|
|
Alert,
|
|
ActivityIndicator,
|
|
} from 'react-native';
|
|
import { pick } from '@react-native-documents/picker';
|
|
import Animated, { FadeInDown, FadeOutUp, Layout } from 'react-native-reanimated';
|
|
import BouncyCheckbox from 'react-native-bouncy-checkbox';
|
|
import { InsideHeader, Skelton } from '../../Component/Index';
|
|
import EmdComplete from './Component/EmdComplete';
|
|
import NavigationString from '../../Constant/NavigationString';
|
|
import { useNavigation } from '@react-navigation/native';
|
|
import { useSelector } from 'react-redux';
|
|
import { useAuctionItem, useEmdCheck } from '../../Services/ApiCall/Hooks';
|
|
import { apiCall } from '../../Axios/Axios';
|
|
import {
|
|
AUCITON_SINGLE_EMD,
|
|
AUCTION_ITEM_SUBMIT,
|
|
CHECK_TECHNICAL_DOCUMENTAION,
|
|
GET_AUCTION_DETAILS,
|
|
GET_TECHNICAL_DOCUMENTAION,
|
|
} from '../../Services/ApiCall/ApiUrls';
|
|
import TemplateFormModal from './Component/TemplateFormModal';
|
|
import { SelectedItem } from './Type/BidType';
|
|
import { showSuccessAlert } from '../../Constant/ShowDailog';
|
|
import { useTheme } from '../../Hooks/useTheme';
|
|
import AppText from '../../components/AppText';
|
|
import {
|
|
FileText,
|
|
Upload,
|
|
CheckCircle2,
|
|
ShieldCheck,
|
|
Paperclip,
|
|
ChevronRight,
|
|
PackageCheck,
|
|
BadgeIndianRupee,
|
|
} from 'lucide-react-native';
|
|
|
|
const Emd = () => {
|
|
const Navigation: any = useNavigation();
|
|
const { colors, spacing, shadows, typography } = useTheme();
|
|
const SelectedAuction = useSelector((state: any) => state.auction.auction);
|
|
|
|
const [selectedItems, setSelectedItems] = useState<SelectedItem[]>([]);
|
|
const [acceptedPolicy, setAcceptedPolicy] = useState(false);
|
|
const [isInitialLoading, setIsInitialLoading] = useState(true);
|
|
const [allBoolean, setAllBoolean]: any = useState({
|
|
isSubmitEmdLoading: false,
|
|
isEmdComplete: false,
|
|
auctionItems: [],
|
|
technicalDocumentStatus: false,
|
|
selectTemplate: {},
|
|
technicalDocModalVisible: false,
|
|
emdType: false,
|
|
fileName: '',
|
|
file: {},
|
|
});
|
|
|
|
const { mutate } = useEmdCheck();
|
|
const { mutate: AuctionItemMutate, isPending } = useAuctionItem();
|
|
|
|
const isSelected = (id: number) => selectedItems.some(i => i.auctionItemId === id);
|
|
|
|
const toggleSelect = (item: any, checked: boolean) => {
|
|
if (!checked) {
|
|
setSelectedItems(prev => prev.filter(i => i.auctionItemId !== item.id));
|
|
} else {
|
|
setSelectedItems(prev => [
|
|
...prev,
|
|
{ auctionItemId: item.id, productImageName: '', file: null },
|
|
]);
|
|
}
|
|
};
|
|
|
|
const updateSelectedItem = (id: number, data: Partial<SelectedItem>) => {
|
|
setSelectedItems(prev =>
|
|
prev.map(i => (i.auctionItemId === id ? { ...i, ...data } : i)),
|
|
);
|
|
};
|
|
|
|
const pickFile = async (id: number) => {
|
|
try {
|
|
const res = await pick({ type: ['application/pdf'] });
|
|
const file = res[0];
|
|
updateSelectedItem(id, {
|
|
file: { name: file.name ?? 'document.pdf', uri: file.uri, type: 'application/pdf' },
|
|
});
|
|
} catch {
|
|
console.log('Pick cancelled');
|
|
}
|
|
};
|
|
|
|
const pickFileSingle = async () => {
|
|
try {
|
|
const res = await pick({ type: ['application/pdf'] });
|
|
const file = res[0];
|
|
setAllBoolean((prev: any) => ({
|
|
...prev,
|
|
file: { name: file.name ?? 'document.pdf', uri: file.uri, type: 'application/pdf' },
|
|
}));
|
|
} catch {
|
|
console.log('Pick cancelled');
|
|
}
|
|
};
|
|
|
|
/* ── Render Item ── */
|
|
const renderItem = ({ item }: any) => {
|
|
console.log(item, "item");
|
|
const selected = isSelected(item.id);
|
|
const selectedData = selectedItems.find(i => i.auctionItemId === item.id);
|
|
|
|
return (
|
|
<Animated.View
|
|
layout={Layout.springify()}
|
|
entering={FadeInDown.springify()}
|
|
style={[
|
|
styles.card,
|
|
selected && { borderColor: colors.primary, borderWidth: 1.5, backgroundColor: `${colors.primary}08` },
|
|
{ backgroundColor: selected ? `${colors.primary}08` : colors.surface },
|
|
]}
|
|
>
|
|
{/* Card Header */}
|
|
<View style={styles.cardHeader}>
|
|
<View style={styles.cardLeft}>
|
|
<View style={[styles.itemIconBox, { backgroundColor: `${colors.primary}15` }]}>
|
|
<PackageCheck size={20} color={colors.primary} strokeWidth={2} />
|
|
</View>
|
|
<View style={{ flex: 1 }}>
|
|
<Text style={{ fontSize: 14, fontWeight: '700', color: colors.text, marginBottom: 3 }}>
|
|
{item.productName}
|
|
</Text>
|
|
<View style={styles.emdRow}>
|
|
{/* <BadgeIndianRupee size={13} color={colors.primary} strokeWidth={2} /> */}
|
|
<Text style={{ fontSize: 12, fontWeight: '600', color: colors.primary }}>
|
|
EMD ₹ {item.emdAmount}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
<View style={{ width: 35 }}>
|
|
<BouncyCheckbox
|
|
size={22}
|
|
fillColor={colors.primary}
|
|
iconStyle={{ borderRadius: 7, borderWidth: 1.5, borderColor: colors.primary }}
|
|
innerIconStyle={{ borderRadius: 7 }}
|
|
isChecked={selected}
|
|
onPress={checked => toggleSelect(item, checked)}
|
|
/>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Existing doc */}
|
|
<View style={[styles.existingDocRow, { backgroundColor: colors.overlay }]}>
|
|
<Paperclip size={13} color={colors.textSecondary} strokeWidth={2} />
|
|
<Text style={{ fontSize: 12, fontWeight: '400', color: colors.textSecondary }}>
|
|
Existing:{' '}
|
|
<Text style={{ fontSize: 12, fontWeight: '600', color: colors.text }}>
|
|
{item.itemDocumentName ?? 'Not Uploaded'}
|
|
</Text>
|
|
</Text>
|
|
</View>
|
|
|
|
{/* Expanded Form */}
|
|
{selected && (
|
|
<Animated.View entering={FadeInDown.springify()} exiting={FadeOutUp.duration(200)} style={styles.expandedForm}>
|
|
<View style={[styles.separator, { backgroundColor: colors.border }]} />
|
|
|
|
<TextInput
|
|
placeholder="Document name"
|
|
value={selectedData?.productImageName}
|
|
onChangeText={text => updateSelectedItem(item.id, { productImageName: text })}
|
|
style={[styles.textInput, { borderColor: colors.border, backgroundColor: colors.background, color: colors.text }]}
|
|
placeholderTextColor={colors.textSecondary}
|
|
/>
|
|
|
|
<TouchableOpacity
|
|
style={[styles.uploadBtn, { backgroundColor: colors.primary }]}
|
|
onPress={() => pickFile(item.id)}
|
|
activeOpacity={0.85}
|
|
>
|
|
<Upload size={15} color="#fff" strokeWidth={2.5} />
|
|
<Text style={{ fontSize: 12, fontWeight: '700', color: '#fff' }}>
|
|
{selectedData?.file ? 'Change PDF' : 'Upload PDF'}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
|
|
{selectedData?.file && (
|
|
<View style={[styles.fileNameRow, { backgroundColor: `${colors.success}12` }]}>
|
|
<FileText size={13} color={colors.success} strokeWidth={2} />
|
|
<AppText variant="caption" color={colors.success} numberOfLines={1} style={{ flex: 1 } as any}>
|
|
{selectedData.file.name}
|
|
</AppText>
|
|
</View>
|
|
)}
|
|
</Animated.View>
|
|
)}
|
|
</Animated.View>
|
|
);
|
|
};
|
|
|
|
/* ── API calls (unchanged logic) ── */
|
|
const getAuctionItem = async () => {
|
|
try {
|
|
const payload = { auctionNumber: SelectedAuction.auctionNumber, status: SelectedAuction.auctionPattern };
|
|
AuctionItemMutate(payload, {
|
|
onSuccess: res => {
|
|
if (res.statusCode === 200) {
|
|
setAllBoolean((prev: any) => ({ ...prev, auctionItems: Array.isArray(res?.data) ? res.data : [res.data] }));
|
|
}
|
|
},
|
|
onError: () => { },
|
|
});
|
|
} catch (error) {
|
|
console.log('Auction fetch error', error);
|
|
}
|
|
};
|
|
|
|
const getTemplateDocumentation = async () => {
|
|
try {
|
|
if (SelectedAuction.technicalDocReq) {
|
|
const res = await apiCall<any>('get', CHECK_TECHNICAL_DOCUMENTAION, {}, { auctionId: SelectedAuction.auctionId });
|
|
if (res.data) {
|
|
setAllBoolean((prev: any) => ({ ...prev, technicalDocumentStatus: true }));
|
|
} else {
|
|
const res2 = await apiCall<any>('get', GET_TECHNICAL_DOCUMENTAION);
|
|
const auctionIdToFind = String(SelectedAuction.auctionId);
|
|
const matchedObject = res2.data.find((item: any) =>
|
|
item.auctionIds?.split(',')?.map((id: any) => id.trim())?.includes(auctionIdToFind),
|
|
);
|
|
setAllBoolean((prev: any) => ({ ...prev, selectTemplate: matchedObject, technicalDocModalVisible: true }));
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.log('Auction fetch error', error);
|
|
}
|
|
};
|
|
|
|
const getAuction = async () => {
|
|
const selectedRes = await apiCall<any>('get', GET_AUCTION_DETAILS, {}, { id: SelectedAuction.auctionId });
|
|
const emdType = String(selectedRes.data.emd).trim().replace(/\s+/g, ' ').toLowerCase();
|
|
return selectedRes.statusCode === 200 && emdType === 'auction wise';
|
|
};
|
|
|
|
const AlreadyEmdCheck = async () => {
|
|
try {
|
|
setIsInitialLoading(true);
|
|
mutate(
|
|
{ auctionNumber: SelectedAuction.auctionNumber },
|
|
{
|
|
onSuccess: async res => {
|
|
try {
|
|
if (res.statusCode === 200) {
|
|
if (res.message === 'No EMD Documents') {
|
|
const result = await getAuction();
|
|
if (result) {
|
|
setAllBoolean((prev: any) => ({ ...prev, emdType: true }));
|
|
return;
|
|
}
|
|
setAllBoolean((prev: any) => ({ ...prev, isEmdComplete: false }));
|
|
getAuctionItem();
|
|
} else {
|
|
if (!SelectedAuction.technicalDocReq) {
|
|
setAllBoolean((prev: any) => ({ ...prev, isEmdComplete: true, technicalDocumentStatus: true }));
|
|
} else {
|
|
setAllBoolean((prev: any) => ({ ...prev, isEmdComplete: true }));
|
|
getTemplateDocumentation();
|
|
}
|
|
}
|
|
}
|
|
} finally {
|
|
setIsInitialLoading(false);
|
|
}
|
|
},
|
|
onError: () => {
|
|
setIsInitialLoading(false);
|
|
},
|
|
},
|
|
);
|
|
} catch (error) {
|
|
console.log('EMD Check Error:', error);
|
|
setIsInitialLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleSubmit = async () => {
|
|
setAllBoolean((prev: any) => ({ ...prev, isSubmitEmdLoading: true }));
|
|
if (!acceptedPolicy) { Alert.alert('Policy Required', 'Please accept terms'); return; }
|
|
if (selectedItems.length === 0) { Alert.alert('Select Item', 'Please select at least one item'); return; }
|
|
for (const item of selectedItems) {
|
|
if (!item.productImageName || !item.file) {
|
|
Alert.alert('Incomplete', 'Upload documents for all selected items');
|
|
return;
|
|
}
|
|
}
|
|
try {
|
|
const formData = new FormData();
|
|
formData.append('auctionId', String(SelectedAuction.auctionId));
|
|
selectedItems.forEach((item: any) => {
|
|
formData.append('auctionItemId', String(item.auctionItemId));
|
|
formData.append('productImageName', item.productImageName);
|
|
formData.append('files', { uri: item.file.uri, name: item.file.name, type: item.file.type || 'application/pdf' } as any);
|
|
});
|
|
const res = await apiCall<any>('post', AUCTION_ITEM_SUBMIT, formData, {}, 'multipart/form-data');
|
|
if (res.statusCode === 200) {
|
|
if (SelectedAuction.technicalDocReq) {
|
|
getTemplateDocumentation();
|
|
} else {
|
|
Navigation.navigate(SelectedAuction.auctionPattern === 'Forward' ? NavigationString.StartBidding : NavigationString.StartReverseBidding);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
Alert.alert('Error', 'Failed to submit EMD');
|
|
} finally {
|
|
setAllBoolean((prev: any) => ({ ...prev, isSubmitEmdLoading: false }));
|
|
}
|
|
};
|
|
|
|
const EmdContinueHandle = async () => {
|
|
if (allBoolean.technicalDocumentStatus) {
|
|
Navigation.navigate(SelectedAuction.auctionPattern === 'Forward' ? NavigationString.StartBidding : NavigationString.StartReverseBidding);
|
|
} else {
|
|
getTemplateDocumentation();
|
|
}
|
|
};
|
|
|
|
const singleEmdSubmit = async () => {
|
|
try {
|
|
setAllBoolean((prev: any) => ({ ...prev, isSubmitEmdLoading: true }));
|
|
if (!acceptedPolicy) { Alert.alert('Policy Required', 'Please accept terms'); return; }
|
|
const formData = new FormData();
|
|
formData.append('auctionId', SelectedAuction.auctionId);
|
|
formData.append('productImageName', allBoolean.fileName);
|
|
formData.append('files', allBoolean.file);
|
|
const response = await apiCall<any>('post', AUCITON_SINGLE_EMD, formData, {}, 'multipart/form-data');
|
|
if (response?.statusCode === 200) {
|
|
showSuccessAlert(response.message || 'EMD submitted successfully');
|
|
Navigation.navigate(SelectedAuction.auctionPattern === 'Forward' ? NavigationString.StartBidding : NavigationString.StartReverseBidding);
|
|
}
|
|
} catch (error: any) {
|
|
console.log('Error:', error?.message || error);
|
|
} finally {
|
|
setAllBoolean((prev: any) => ({ ...prev, isSubmitEmdLoading: false }));
|
|
}
|
|
};
|
|
|
|
useEffect(() => { AlreadyEmdCheck(); }, []);
|
|
|
|
const styles = makeStyles(colors, spacing, shadows, typography);
|
|
|
|
/* ── Policy + Submit footer ── */
|
|
const BottomBar = ({ onSubmit }: { onSubmit: () => void }) => (
|
|
<View style={styles.bottomBar}>
|
|
<View style={styles.policyRow}>
|
|
<View style={{ width: 30 }}>
|
|
<BouncyCheckbox
|
|
size={20}
|
|
fillColor={colors.primary}
|
|
iconStyle={{ borderRadius: 6, borderWidth: 1.5, borderColor: colors.primary }}
|
|
innerIconStyle={{ borderRadius: 6 }}
|
|
isChecked={acceptedPolicy}
|
|
onPress={() => setAcceptedPolicy(p => !p)}
|
|
/>
|
|
</View>
|
|
<Text style={styles.policyText}>
|
|
I agree to{' '}
|
|
<Text style={{ fontWeight: '700', color: colors.primary }}>
|
|
Terms & Privacy Policy
|
|
</Text>
|
|
</Text>
|
|
</View>
|
|
|
|
<TouchableOpacity
|
|
style={[styles.submitBtn, !acceptedPolicy && styles.disabledBtn, { backgroundColor: acceptedPolicy ? colors.primary : colors.border }]}
|
|
onPress={onSubmit}
|
|
disabled={!acceptedPolicy}
|
|
activeOpacity={0.85}
|
|
>
|
|
{allBoolean.isSubmitEmdLoading ? (
|
|
<ActivityIndicator size="small" color="#fff" />
|
|
) : (
|
|
<>
|
|
<ShieldCheck size={18} color="#fff" strokeWidth={2.5} />
|
|
<Text style={styles.submitText}>
|
|
Submit EMD
|
|
</Text>
|
|
</>
|
|
)}
|
|
</TouchableOpacity>
|
|
</View>
|
|
);
|
|
|
|
return (
|
|
<View style={styles.root}>
|
|
<InsideHeader title="EMD Submission" showArrow />
|
|
|
|
{SelectedAuction.technicalDocReq && allBoolean.technicalDocModalVisible && (
|
|
<TemplateFormModal
|
|
visible={allBoolean.technicalDocModalVisible}
|
|
allBoolean={allBoolean}
|
|
setAllBoolean={setAllBoolean}
|
|
template={allBoolean.selectTemplate}
|
|
/>
|
|
)}
|
|
|
|
{(isPending || isInitialLoading) ? (
|
|
<Skelton />
|
|
) : allBoolean.isEmdComplete ? (
|
|
<EmdComplete onNext={EmdContinueHandle} />
|
|
) : allBoolean.emdType ? (
|
|
/* ── Single EMD (auction-wise) ── */
|
|
<View style={{ flex: 1 }}>
|
|
<View style={styles.singleEmdCard}>
|
|
{/* Icon banner */}
|
|
<View style={[styles.singleEmdBanner, { backgroundColor: `${colors.primary}10` }]}>
|
|
<ShieldCheck size={32} color={colors.primary} strokeWidth={1.5} />
|
|
<View>
|
|
<Text style={{ fontSize: 18, fontWeight: '800', color: colors.text }}>
|
|
Auction-wise EMD
|
|
</Text>
|
|
<Text style={{ fontSize: 12, fontWeight: '400', color: colors.textSecondary }}>
|
|
Upload a single document for this auction
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Document name */}
|
|
<Text style={{ fontSize: 12, fontWeight: '700', color: colors.textSecondary, letterSpacing: 1, marginBottom: spacing.xs + 2 }}>
|
|
DOCUMENT NAME
|
|
</Text>
|
|
<TextInput
|
|
style={[styles.textInput, { borderColor: colors.border, backgroundColor: colors.background, color: colors.text, marginBottom: spacing.m }]}
|
|
placeholder="Enter document name…"
|
|
value={allBoolean.fileName}
|
|
placeholderTextColor={colors.textSecondary}
|
|
onChangeText={text => setAllBoolean((prev: any) => ({ ...prev, fileName: text }))}
|
|
/>
|
|
|
|
{/* File picker */}
|
|
<TouchableOpacity
|
|
style={[styles.uploadBtn, { backgroundColor: colors.primary }]}
|
|
onPress={pickFileSingle}
|
|
activeOpacity={0.85}
|
|
>
|
|
<Upload size={15} color="#fff" strokeWidth={2.5} />
|
|
<Text style={{ fontSize: 12, fontWeight: '700', color: '#fff' }}>
|
|
{allBoolean?.file?.name ? 'Change PDF' : 'Choose PDF'}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
|
|
{allBoolean?.file?.name ? (
|
|
<View style={[styles.fileNameRow, { backgroundColor: `${colors.success}12`, marginTop: spacing.s }]}>
|
|
<FileText size={13} color={colors.success} strokeWidth={2} />
|
|
<Text style={{ fontSize: 12, fontWeight: '400', color: colors.success, flex: 1 }} numberOfLines={1}>
|
|
{allBoolean.file.name}
|
|
</Text>
|
|
</View>
|
|
) : null}
|
|
</View>
|
|
|
|
<BottomBar onSubmit={singleEmdSubmit} />
|
|
</View>
|
|
) : (
|
|
/* ── Item-wise EMD ── */
|
|
<View style={{ flex: 1 }}>
|
|
<FlatList
|
|
data={allBoolean.auctionItems}
|
|
keyExtractor={(item: any) => String(item.id)}
|
|
renderItem={renderItem}
|
|
contentContainerStyle={styles.listContent}
|
|
showsVerticalScrollIndicator={false}
|
|
/>
|
|
<BottomBar onSubmit={handleSubmit} />
|
|
</View>
|
|
)}
|
|
</View>
|
|
);
|
|
};
|
|
|
|
export default Emd;
|
|
|
|
/* ─── Styles factory ─── */
|
|
const makeStyles = (colors: any, spacing: any, shadows: any, typography?: any) =>
|
|
StyleSheet.create({
|
|
root: {
|
|
flex: 1,
|
|
backgroundColor: colors.background,
|
|
},
|
|
listContent: {
|
|
padding: spacing.m,
|
|
paddingBottom: 160,
|
|
},
|
|
|
|
/* ── Item card ── */
|
|
card: {
|
|
borderRadius: 18,
|
|
marginBottom: spacing.m,
|
|
padding: spacing.m,
|
|
borderWidth: 1,
|
|
borderColor: colors.border,
|
|
...shadows.light,
|
|
},
|
|
cardHeader: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
gap: spacing.s,
|
|
},
|
|
cardLeft: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
flex: 1,
|
|
gap: spacing.s,
|
|
},
|
|
itemIconBox: {
|
|
width: 40,
|
|
height: 40,
|
|
borderRadius: 12,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
},
|
|
itemTitle: {
|
|
fontFamily: typography?.fonts?.bold,
|
|
marginBottom: 3,
|
|
} as any,
|
|
emdRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 3,
|
|
},
|
|
emdAmount: {
|
|
fontFamily: typography?.fonts?.bold,
|
|
} as any,
|
|
existingDocRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 5,
|
|
marginTop: spacing.s,
|
|
paddingHorizontal: spacing.s + 2,
|
|
paddingVertical: 6,
|
|
borderRadius: 8,
|
|
},
|
|
expandedForm: {
|
|
marginTop: spacing.s,
|
|
},
|
|
separator: {
|
|
height: StyleSheet.hairlineWidth,
|
|
marginBottom: spacing.m,
|
|
},
|
|
textInput: {
|
|
borderWidth: 1.2,
|
|
borderRadius: 14,
|
|
padding: 12,
|
|
fontSize: 14,
|
|
marginBottom: spacing.s,
|
|
},
|
|
uploadBtn: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
gap: 7,
|
|
paddingVertical: 13,
|
|
borderRadius: 14,
|
|
},
|
|
uploadText: {
|
|
fontFamily: typography?.fonts?.bold,
|
|
} as any,
|
|
fileNameRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 6,
|
|
marginTop: spacing.s,
|
|
paddingHorizontal: spacing.s + 4,
|
|
paddingVertical: 8,
|
|
borderRadius: 10,
|
|
},
|
|
|
|
/* ── Single EMD card ── */
|
|
singleEmdCard: {
|
|
margin: spacing.m,
|
|
backgroundColor: colors.surface,
|
|
borderRadius: 20,
|
|
padding: spacing.m,
|
|
...shadows.light,
|
|
},
|
|
singleEmdBanner: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: spacing.m,
|
|
borderRadius: 14,
|
|
padding: spacing.m,
|
|
marginBottom: spacing.m,
|
|
},
|
|
fieldLabel: {
|
|
fontFamily: typography?.fonts?.bold,
|
|
letterSpacing: 1,
|
|
marginBottom: spacing.xs + 2,
|
|
} as any,
|
|
|
|
/* ── Bottom bar ── */
|
|
bottomBar: {
|
|
height: 160,
|
|
padding: spacing.m,
|
|
paddingBottom: spacing.m + 60,
|
|
backgroundColor: colors.surface,
|
|
borderTopWidth: StyleSheet.hairlineWidth,
|
|
borderTopColor: colors.border,
|
|
...shadows.large,
|
|
},
|
|
policyRow: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: spacing.s,
|
|
marginBottom: spacing.m,
|
|
},
|
|
policyText: {
|
|
flex: 1,
|
|
lineHeight: 20,
|
|
fontSize: 13,
|
|
fontWeight: '500',
|
|
color: colors.text,
|
|
} as any,
|
|
submitBtn: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
gap: 8,
|
|
paddingVertical: 15,
|
|
borderRadius: 16,
|
|
},
|
|
disabledBtn: {
|
|
opacity: 0.6,
|
|
},
|
|
submitText: {
|
|
fontSize: 14,
|
|
fontWeight: '800',
|
|
color: '#fff',
|
|
letterSpacing: 0.5,
|
|
textTransform: 'uppercase',
|
|
} as any,
|
|
});
|