pre merge
This commit is contained in:
Generated
+12583
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,7 @@
|
||||
"react-native-blob-util": "^0.24.6",
|
||||
"react-native-bouncy-checkbox": "^4.1.2",
|
||||
"react-native-date-picker": "^5.0.13",
|
||||
"react-native-element-dropdown": "^2.12.4",
|
||||
"react-native-fs": "^2.20.0",
|
||||
"react-native-gesture-handler": "^2.30.0",
|
||||
"react-native-gifted-charts": "^1.4.70",
|
||||
|
||||
+3
-22
@@ -11,39 +11,20 @@ export const apiCall = async <R, D = {}>(
|
||||
): Promise<R> => {
|
||||
try {
|
||||
const token: any = store.getState().token.token;
|
||||
console.log("url : ", url)
|
||||
console.log("token : ", token)
|
||||
|
||||
// Generate curl command
|
||||
let curlCmd = `curl -X ${method.toUpperCase()} "${url}"`;
|
||||
if (token?.token) curlCmd += ` -H "Authentication: Bearer ${token.token}"`;
|
||||
curlCmd += ` -H "Content-Type: ${contentType || 'application/json'}"`;
|
||||
if (data) {
|
||||
if (contentType === 'multipart/form-data') {
|
||||
curlCmd += ` --form "${JSON.stringify(data).slice(1,-1).replace(/"/g, '\\"')}"`;
|
||||
} else {
|
||||
curlCmd += ` -d '${JSON.stringify(data)}'`;
|
||||
}
|
||||
}
|
||||
if (params) {
|
||||
curlCmd += ` ${Object.entries(params).map(([k,v]) => `-${k}=${v}`).join(' ')}`;
|
||||
}
|
||||
console.log("=== CURL ===");
|
||||
console.log(curlCmd);
|
||||
console.log("============");
|
||||
|
||||
console.log("abin-------",url)
|
||||
const response = await axios({
|
||||
method,
|
||||
url,
|
||||
data,
|
||||
params,
|
||||
// withCredentials: true,
|
||||
// baseURL: Variable.Main_Base,
|
||||
headers: {
|
||||
Authentication: `Bearer ${token?.token}`,
|
||||
'Content-Type': contentType || 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
console.log("abin-------",response)
|
||||
|
||||
return response.data;
|
||||
} catch (error: any) {
|
||||
|
||||
@@ -12,7 +12,7 @@ import * as LucideIcons from 'lucide-react-native';
|
||||
import Screens from '../Screens';
|
||||
import colors from '../Constant/Color';
|
||||
import NavigationString from '../Constant/NavigationString';
|
||||
import EditableExcel from '../Screens/Tender/Component/EditableExcel';
|
||||
import EditableExcel from '../Screens/Tender/Component/EnvelopeCExcelPreview';
|
||||
|
||||
const { width } = Dimensions.get('window');
|
||||
const TAB_BAR_HEIGHT = 60;
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useEffect, useState } from 'react';
|
||||
import { View, Text, FlatList, TouchableOpacity, StyleSheet, TextInput, Alert } 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 BouncyCheckbox from 'react-native-bouncy-checkbox';
|
||||
import { InsideHeader, Loader, Skelton } from '../../Component/Index';
|
||||
import colors from '../../Constant/Color';
|
||||
@@ -94,14 +93,14 @@ const Emd = () => {
|
||||
<Text style={styles.subText}>EMD ₹ {item.emdAmount}</Text>
|
||||
</View>
|
||||
|
||||
{/* <BouncyCheckbox
|
||||
<BouncyCheckbox
|
||||
size={20}
|
||||
fillColor={colors.primary}
|
||||
iconStyle={{ borderRadius: 5, borderWidth: 1.5, borderColor: colors.primary }}
|
||||
innerIconStyle={{ borderRadius: 6 }}
|
||||
isChecked={selected}
|
||||
onPress={(checked) => toggleSelect(item, checked)}
|
||||
/> */}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Text style={styles.meta}>
|
||||
|
||||
@@ -24,6 +24,7 @@ const Splash: React.FC = () => {
|
||||
const isFocused = useIsFocused();
|
||||
const fadeAnim = useRef(new Animated.Value(0)).current;
|
||||
const reduxToken = useSelector((state: any) => state.token?.token);
|
||||
console.log("user Token",reduxToken)
|
||||
const store = useStore();
|
||||
const { mutateAsync: registrationStateMutate } = useRagistrationState();
|
||||
const [isHydrated, setIsHydrated] = useState(false);
|
||||
@@ -49,7 +50,6 @@ const Splash: React.FC = () => {
|
||||
|
||||
const routeByOrgType = (params: { otherId: any; orgType: any }) => {
|
||||
const orgType = String(params.orgType);
|
||||
console.log("hitting here ")
|
||||
if (orgType === '4' || orgType === '5' || orgType === '6') {
|
||||
navigation.replace('StageThree', params);
|
||||
return;
|
||||
@@ -66,8 +66,7 @@ const Splash: React.FC = () => {
|
||||
const handleGetData = async () => {
|
||||
try {
|
||||
const res = await registrationStateMutate();
|
||||
console.log('Registration status response:', res);
|
||||
|
||||
console.log("ragister",res)
|
||||
if (res?.statusCode === 404 || res?.statusCode === 401) {
|
||||
navigation.replace('Registration');
|
||||
return;
|
||||
@@ -96,7 +95,6 @@ const Splash: React.FC = () => {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
console.error('API error:', res);
|
||||
showToast(res?.message || 'Something went wrong');
|
||||
navigation.replace('Registration');
|
||||
|
||||
@@ -1,243 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { View, Text, TextInput, ScrollView, TouchableOpacity, StyleSheet, ActivityIndicator, Alert, } from 'react-native';
|
||||
import RNFS from 'react-native-fs';
|
||||
import * as XLSX from 'xlsx';
|
||||
import axios from 'axios';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { InsideHeader } from '../../../Component/Index';
|
||||
import colors from '../../../Constant/Color';
|
||||
|
||||
const EXCEL_API_URL = 'https://procurelinc.in/EProcurementSB/eTendering/getEnvelopeCTemplate/tenderId?tenderId=2479';
|
||||
|
||||
// Excel column keys
|
||||
const QTY_KEY = '__EMPTY_1';
|
||||
const RATE_KEY = '__EMPTY_2';
|
||||
const VALUE_KEY = '__EMPTY_3';
|
||||
const TOTAL_LABEL = 'TOTAL';
|
||||
|
||||
const EnvelopeCExcelPreview = () => {
|
||||
const [data, setData] = useState<any[]>([]);
|
||||
const [columns, setColumns] = useState<string[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const token = useSelector((state: any) => state.token.token);
|
||||
|
||||
useEffect(() => {
|
||||
loadExcelFromApi();
|
||||
}, []);
|
||||
|
||||
// ArrayBuffer → Base64
|
||||
const arrayBufferToBase64 = (buffer: ArrayBuffer) => {
|
||||
let binary = '';
|
||||
const bytes = new Uint8Array(buffer);
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
binary += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
return global.btoa(binary);
|
||||
};
|
||||
|
||||
// Calculate TOTAL VALUE
|
||||
const calculateTotal = (rows: any[]) =>
|
||||
rows.reduce((sum, row) => {
|
||||
if (typeof row['DEMO DEPARTEMENT'] !== 'number') return sum;
|
||||
const val = Number(row[VALUE_KEY]);
|
||||
return sum + (isNaN(val) ? 0 : val);
|
||||
}, 0);
|
||||
|
||||
// LOAD EXCEL
|
||||
const loadExcelFromApi = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
const response = await axios.get(EXCEL_API_URL, {
|
||||
responseType: 'arraybuffer',
|
||||
headers: {
|
||||
Authentication: `Bearer ${token.token}`,
|
||||
},
|
||||
});
|
||||
|
||||
const base64 = arrayBufferToBase64(response.data);
|
||||
const workbook = XLSX.read(base64, { type: 'base64' });
|
||||
const sheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
const rows: any[] = XLSX.utils.sheet_to_json(sheet, { defval: '' });
|
||||
|
||||
setColumns(Object.keys(rows[0] || {}));
|
||||
setData(rows);
|
||||
|
||||
await RNFS.writeFile(
|
||||
`${RNFS.DocumentDirectoryPath}/envelopeC.xlsx`,
|
||||
base64,
|
||||
'base64'
|
||||
);
|
||||
} catch (e) {
|
||||
Alert.alert('Error', 'Failed to load Excel');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// UPDATE CELL (QTY / RATE) + AUTO VALUE + AUTO TOTAL
|
||||
const updateCell = (rowIndex: number, key: string, value: string) => {
|
||||
const updated = [...data];
|
||||
const row = updated[rowIndex];
|
||||
|
||||
// TOTAL row locked
|
||||
if (row['DEMO DEPARTEMENT'] === TOTAL_LABEL) return;
|
||||
|
||||
// Allow only QTY & RATE edit
|
||||
if (key === QTY_KEY || key === RATE_KEY) {
|
||||
row[key] = value;
|
||||
|
||||
const qty = Number(row[QTY_KEY]);
|
||||
const rate = Number(row[RATE_KEY]);
|
||||
|
||||
// 🔥 Auto VALUE = QTY × RATE
|
||||
row[VALUE_KEY] =
|
||||
!isNaN(qty) && !isNaN(rate) ? qty * rate : 0;
|
||||
}
|
||||
|
||||
// 🔥 Update TOTAL
|
||||
const totalRowIndex = updated.findIndex(
|
||||
r => r['DEMO DEPARTEMENT'] === TOTAL_LABEL
|
||||
);
|
||||
|
||||
if (totalRowIndex !== -1) {
|
||||
updated[totalRowIndex][VALUE_KEY] = calculateTotal(updated);
|
||||
}
|
||||
|
||||
setData(updated);
|
||||
};
|
||||
|
||||
// SAVE UPDATED EXCEL
|
||||
const saveUpdatedExcel = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
const worksheet = XLSX.utils.json_to_sheet(data);
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
|
||||
|
||||
const base64 = XLSX.write(workbook, {
|
||||
bookType: 'xlsx',
|
||||
type: 'base64',
|
||||
});
|
||||
|
||||
await RNFS.writeFile(
|
||||
`${RNFS.DocumentDirectoryPath}/envelopeC_updated.xlsx`,
|
||||
base64,
|
||||
'base64'
|
||||
);
|
||||
|
||||
Alert.alert('Success', 'Excel saved with auto-calculation');
|
||||
} catch (e) {
|
||||
Alert.alert('Error', 'Save failed');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<View style={styles.loader}>
|
||||
<ActivityIndicator size="large" />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<InsideHeader title='Envelope-C Excel Preview' />
|
||||
|
||||
<ScrollView horizontal contentContainerStyle={{ paddingHorizontal: 10, paddingTop: 20, backgroundColor: colors.white }}>
|
||||
<View>
|
||||
{/* HEADER */}
|
||||
<View style={styles.row}>
|
||||
{columns.map(col => (
|
||||
<Text key={col} style={styles.headerCell}>
|
||||
{col}
|
||||
</Text>
|
||||
))}
|
||||
</View>
|
||||
|
||||
{/* DATA */}
|
||||
<ScrollView style={{ maxHeight: 420 }}>
|
||||
{data.map((row, rowIndex) => (
|
||||
<View key={rowIndex} style={styles.row}>
|
||||
{columns.map(key => {
|
||||
const isTotal = row['DEMO DEPARTEMENT'] === TOTAL_LABEL;
|
||||
const isEditable =
|
||||
!isTotal && (key === QTY_KEY || key === RATE_KEY);
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
key={key}
|
||||
value={String(row[key])}
|
||||
editable={isEditable}
|
||||
keyboardType={
|
||||
key === QTY_KEY || key === RATE_KEY
|
||||
? 'numeric'
|
||||
: 'default'
|
||||
}
|
||||
style={[
|
||||
styles.cell,
|
||||
isTotal && styles.totalCell,
|
||||
key === VALUE_KEY && styles.valueCell,
|
||||
]}
|
||||
onChangeText={text =>
|
||||
updateCell(rowIndex, key, text)
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
||||
<TouchableOpacity style={styles.button} onPress={saveUpdatedExcel}>
|
||||
<Text style={styles.buttonText}>Save Updated Excel</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default EnvelopeCExcelPreview;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: { flex: 1, backgroundColor: '#fff' },
|
||||
loader: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
||||
title: { fontSize: 18, fontWeight: '700', marginBottom: 10 },
|
||||
row: { flexDirection: 'row' },
|
||||
headerCell: {
|
||||
minWidth: 140,
|
||||
padding: 10,
|
||||
backgroundColor: '#f1f5f9',
|
||||
borderWidth: 1,
|
||||
fontWeight: '700',
|
||||
},
|
||||
cell: {
|
||||
minWidth: 140,
|
||||
padding: 10,
|
||||
borderWidth: 1,
|
||||
borderColor: '#cbd5e1',
|
||||
},
|
||||
valueCell: {
|
||||
backgroundColor: '#f8fafc',
|
||||
},
|
||||
totalCell: {
|
||||
backgroundColor: '#e5e7eb',
|
||||
fontWeight: '700',
|
||||
},
|
||||
button: {
|
||||
marginTop: 15,
|
||||
backgroundColor: '#2563eb',
|
||||
padding: 14,
|
||||
borderRadius: 8,
|
||||
},
|
||||
buttonText: {
|
||||
color: '#fff',
|
||||
textAlign: 'center',
|
||||
fontWeight: '600',
|
||||
},
|
||||
});
|
||||
@@ -13,8 +13,11 @@ import LiveTenderTile from './LiveTenderTile';
|
||||
const LiveTender = () => {
|
||||
const navigation: any = useNavigation();
|
||||
const Dispatch = useDispatch()
|
||||
const user = useSelector((state: any) => state?.token.token);
|
||||
console.log("userdata",user)
|
||||
const SelectedPurchaseList = useSelector((state: any) => state.selectPurchaseTender);
|
||||
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, isRefetching, refetch }: any = useFetchLiveTender();
|
||||
|
||||
const [showRefreshBanner, setShowRefreshBanner] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -48,6 +51,7 @@ const LiveTender = () => {
|
||||
({ item, index }: any) => {
|
||||
const isPurchase = SelectedPurchaseList.findIndex((prev: any) => prev.tenderId === item.tenderId)
|
||||
return <LiveTenderTile
|
||||
contractorId={user?.id}
|
||||
data={item}
|
||||
index={index}
|
||||
tenderId={item.tenderId}
|
||||
|
||||
@@ -1,18 +1,459 @@
|
||||
import { StyleSheet, Text, View } from 'react-native'
|
||||
import React from 'react'
|
||||
import { useRoute } from '@react-navigation/native';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Alert,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
StatusBar,
|
||||
SafeAreaView,
|
||||
} from 'react-native';
|
||||
import { useRoute, useNavigation } from '@react-navigation/native';
|
||||
import {
|
||||
useFetchLiveTenderById,
|
||||
} from '../../Services/BBPS/Hooks';
|
||||
import { InsideHeader } from '../../Component/Index';
|
||||
import colors from '../../Constant/Color';
|
||||
import ReactNativeBlobUtil from 'react-native-blob-util';
|
||||
import { GET_DOCID } from '../../Services/BBPS/ApiUrls';
|
||||
import {
|
||||
Layers,
|
||||
Briefcase,
|
||||
FileText,
|
||||
Calendar,
|
||||
ArrowLeft,
|
||||
Download,
|
||||
} from 'lucide-react-native';
|
||||
import moment from 'moment';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
const InfoRow = ({ label, value }: any) => (
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.label}>{label}</Text>
|
||||
<Text style={styles.value}>{value || '-'}</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
const Section = ({ title, icon: Icon, children }: any) => (
|
||||
<View style={styles.sectionContainer}>
|
||||
<View style={styles.sectionHeader}>
|
||||
<Icon size={18} color={colors.primary} />
|
||||
<Text style={styles.sectionTitle}>{title}</Text>
|
||||
</View>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
|
||||
const LiveTenderDetail = () => {
|
||||
const route = useRoute<any>();
|
||||
const { tender } = route.params;
|
||||
console.log("live tender details : ", tender)
|
||||
return (
|
||||
<View>
|
||||
<Text>LiveTenderDetail</Text>
|
||||
const route = useRoute<any>();
|
||||
const navigation = useNavigation<any>();
|
||||
const tender = route?.params;
|
||||
console.log("object---",tender)
|
||||
const { data } = useFetchLiveTenderById(tender?.tenderId);
|
||||
const [downloadingId, setDownloadingId] = useState<string | number | null>(null);
|
||||
const insets = useSafeAreaInsets()
|
||||
if (!data?.data?.[0]) return null;
|
||||
|
||||
const tenderData = data.data[0];
|
||||
|
||||
const handleDownload = async (doc: any) => {
|
||||
try {
|
||||
setDownloadingId(doc?.docId);
|
||||
const { dirs } = ReactNativeBlobUtil.fs;
|
||||
const path = `${dirs.DownloadDir}/${doc?.fileName}.pdf`;
|
||||
const downloadUrl = `${GET_DOCID}${doc?.docId}`;
|
||||
const response = await ReactNativeBlobUtil.config({
|
||||
fileCache: false,
|
||||
addAndroidDownloads: {
|
||||
useDownloadManager: true,
|
||||
notification: true,
|
||||
path,
|
||||
mime: 'application/pdf',
|
||||
title: `${doc?.fileName}.pdf`,
|
||||
description: 'Downloading document',
|
||||
mediaScannable: true,
|
||||
},
|
||||
}).fetch('GET', downloadUrl, {
|
||||
Accept: 'application/pdf',
|
||||
});
|
||||
const stat = await ReactNativeBlobUtil.fs.stat(path);
|
||||
Alert.alert('Download complete', `File saved to: ${path}`);
|
||||
setDownloadingId(null);
|
||||
} catch (error: any) {
|
||||
setDownloadingId(null);
|
||||
Alert.alert('Error', error?.message || 'Something went wrong while downloading.');
|
||||
}
|
||||
};
|
||||
|
||||
const isLive =
|
||||
tenderData?.tenderStatus?.toLowerCase() === 'live' ||
|
||||
tenderData?.tenderStatus?.toLowerCase() === 'open';
|
||||
|
||||
const formatDateTime = (val?: string) => {
|
||||
if (!val) return '-';
|
||||
return moment(val, 'DD-MM-YYYY HH:mm:ss').isValid()
|
||||
? moment(val, 'DD-MM-YYYY HH:mm:ss').format('DD MMM YYYY, hh:mm A')
|
||||
: val;
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.safeArea}>
|
||||
<StatusBar barStyle="dark-content" />
|
||||
<View style={[styles.header,{marginTop:insets.top}]}>
|
||||
<TouchableOpacity
|
||||
onPress={() => navigation.goBack()}
|
||||
style={styles.backBtn}
|
||||
>
|
||||
<ArrowLeft size={24} color="#1e293b" />
|
||||
</TouchableOpacity>
|
||||
<View style={{ flex: 1 }}>
|
||||
<Text style={styles.headerTitle}>Tender Overview</Text>
|
||||
<Text style={styles.headerSub}>
|
||||
Ref: {tenderData?.tenderNumber || '-'}
|
||||
</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
<View style={styles.liveBox}>
|
||||
<View
|
||||
style={[
|
||||
styles.dot,
|
||||
{ backgroundColor: isLive ? '#22c55e' : '#94a3b8' },
|
||||
]}
|
||||
/>
|
||||
<Text style={styles.liveText}>{isLive ? 'LIVE' : 'CLOSED'}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
export default LiveTenderDetail
|
||||
<ScrollView contentContainerStyle={styles.content}>
|
||||
<Section title="Basic Details" icon={Layers}>
|
||||
<View style={styles.card}>
|
||||
<InfoRow label="Tender ID" value={tenderData?.tenderId} />
|
||||
<InfoRow label="Tender Number" value={tenderData?.tenderNumber} />
|
||||
<InfoRow label="NIT Number" value={tenderData?.nitNumber} />
|
||||
<InfoRow label="NIT Date" value={tenderData?.nitDate} />
|
||||
<InfoRow label="Name of Work" value={tenderData?.nameOfWork} />
|
||||
<InfoRow label="Description" value={tenderData?.descriptionOfWork} />
|
||||
<InfoRow label="Tender Stage" value={tenderData?.tenderStage} />
|
||||
<InfoRow label="Tender Status" value={tenderData?.tenderStatus} />
|
||||
</View>
|
||||
</Section>
|
||||
|
||||
const styles = StyleSheet.create({})
|
||||
<Section title="Category & Type" icon={Briefcase}>
|
||||
<View style={styles.card}>
|
||||
<InfoRow label="Tender Category" value={tenderData?.tenderCategory} />
|
||||
<InfoRow label="Type of Tender" value={tenderData?.typeOfTender} />
|
||||
<InfoRow label="Selective Tender" value={tenderData?.selectiveTender} />
|
||||
<InfoRow label="Area of Work" value={tenderData?.areaOfWork} />
|
||||
</View>
|
||||
</Section>
|
||||
|
||||
<Section title="Financial Details" icon={FileText}>
|
||||
<View style={styles.card}>
|
||||
<InfoRow
|
||||
label="Estimated Value"
|
||||
value={tenderData?.estimateValueInFig ? `₹ ${tenderData.estimateValueInFig}` : '-'}
|
||||
/>
|
||||
<InfoRow
|
||||
label="Estimated Value (Words)"
|
||||
value={tenderData?.estimateValueInWord}
|
||||
/>
|
||||
<InfoRow
|
||||
label="Tender Fee"
|
||||
value={tenderData?.tenderFeeInFig ? `₹ ${tenderData.tenderFeeInFig}` : '-'}
|
||||
/>
|
||||
<InfoRow
|
||||
label="EMD"
|
||||
value={tenderData?.emdInFig ? `₹ ${tenderData.emdInFig}` : '-'}
|
||||
/>
|
||||
<InfoRow label="GST %" value={tenderData?.gst} />
|
||||
<InfoRow label="GST Amount" value={tenderData?.gstAmmount} />
|
||||
</View>
|
||||
</Section>
|
||||
|
||||
<Section title="Work & Validity" icon={Briefcase}>
|
||||
<View style={styles.card}>
|
||||
<InfoRow
|
||||
label="Bid Validity Type"
|
||||
value={tenderData?.bidValidateType}
|
||||
/>
|
||||
<InfoRow
|
||||
label="Bid Validity Period"
|
||||
value={tenderData?.bidValidatePeriod}
|
||||
/>
|
||||
<InfoRow
|
||||
label="Work Completion Type"
|
||||
value={tenderData?.workCompletionType}
|
||||
/>
|
||||
<InfoRow
|
||||
label="Work Completion Period"
|
||||
value={tenderData?.workCompletionPeriod}
|
||||
/>
|
||||
<InfoRow label="Rainy Season" value={tenderData?.rainySeason} />
|
||||
</View>
|
||||
</Section>
|
||||
|
||||
<Section title="Key Dates" icon={Calendar}>
|
||||
<View style={styles.card}>
|
||||
<InfoRow
|
||||
label="Purchase Start"
|
||||
value={formatDateTime(
|
||||
tenderData?.keydates?.tenderPurchaseDate?.startDateTime,
|
||||
)}
|
||||
/>
|
||||
<InfoRow
|
||||
label="Purchase End"
|
||||
value={formatDateTime(
|
||||
tenderData?.keydates?.tenderPurchaseDate?.endDateTime,
|
||||
)}
|
||||
/>
|
||||
<InfoRow
|
||||
label="Bid Submission Start"
|
||||
value={formatDateTime(
|
||||
tenderData?.keydates?.bidSubmissionDate?.startDateTime,
|
||||
)}
|
||||
/>
|
||||
<InfoRow
|
||||
label="Bid Submission End"
|
||||
value={formatDateTime(
|
||||
tenderData?.keydates?.bidSubmissionDate?.endDateTime,
|
||||
)}
|
||||
/>
|
||||
<InfoRow
|
||||
label="Opening Envelope A"
|
||||
value={formatDateTime(tenderData?.keydates?.openingEA)}
|
||||
/>
|
||||
<InfoRow
|
||||
label="Opening Envelope B"
|
||||
value={formatDateTime(tenderData?.keydates?.openingEB)}
|
||||
/>
|
||||
<InfoRow
|
||||
label="Opening Envelope C"
|
||||
value={formatDateTime(tenderData?.keydates?.openingEC)}
|
||||
/>
|
||||
</View>
|
||||
</Section>
|
||||
|
||||
<Section title="Envelope Configuration" icon={Layers}>
|
||||
<View style={styles.card}>
|
||||
<InfoRow
|
||||
label="Envelope Type"
|
||||
value={tenderData?.envelopeType}
|
||||
/>
|
||||
<InfoRow
|
||||
label="Envelope A Enabled"
|
||||
value={tenderData?.envelopeA ? 'Yes' : 'No'}
|
||||
/>
|
||||
<InfoRow
|
||||
label="Envelope B Enabled"
|
||||
value={tenderData?.envelopeB ? 'Yes' : 'No'}
|
||||
/>
|
||||
<InfoRow
|
||||
label="Envelope C Enabled"
|
||||
value={tenderData?.envelopeC ? 'Yes' : 'No'}
|
||||
/>
|
||||
</View>
|
||||
</Section>
|
||||
|
||||
<Section title="Department & Location" icon={Layers}>
|
||||
<View style={styles.card}>
|
||||
<InfoRow label="Department" value={tenderData?.deptName} />
|
||||
<InfoRow label="Circle" value={tenderData?.circle} />
|
||||
<InfoRow
|
||||
label="Division"
|
||||
value={tenderData?.tenderSelectedDivision}
|
||||
/>
|
||||
<InfoRow
|
||||
label="Sub Division"
|
||||
value={tenderData?.tenderSelectedSubDivision}
|
||||
/>
|
||||
</View>
|
||||
</Section>
|
||||
|
||||
<Section title="Officers" icon={Layers}>
|
||||
<View style={styles.card}>
|
||||
<InfoRow label="Tender Master" value={tenderData?.tenderMaster} />
|
||||
{tenderData?.approvalOfficerNameEmail?.map((o: any, i: number) => (
|
||||
<InfoRow
|
||||
key={i}
|
||||
label={`Approval Officer ${i + 1}`}
|
||||
value={`${o.name} (${o.email})`}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</Section>
|
||||
|
||||
<Section title="Documents" icon={Download}>
|
||||
<View style={styles.card}>
|
||||
{tenderData?.tenderDocuments?.map((doc: any, i: number) => (
|
||||
<View key={i} style={styles.documentRow}>
|
||||
<View style={styles.docInfo}>
|
||||
<View style={styles.fileIcon}>
|
||||
<FileText size={18} color={colors.primary} />
|
||||
</View>
|
||||
<Text style={styles.docName}>{doc.fileName}</Text>
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
style={styles.downloadBtn}
|
||||
onPress={() => handleDownload(doc)}
|
||||
disabled={downloadingId === doc?.docId}
|
||||
>
|
||||
<Download
|
||||
size={18}
|
||||
color={
|
||||
downloadingId === doc?.docId ? '#9ca3af' : colors.primary
|
||||
}
|
||||
/>
|
||||
<Text style={styles.downloadText}>
|
||||
{downloadingId === doc?.docId ? 'Downloading...' : 'Download'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</Section>
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default LiveTenderDetail;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
safeArea: {
|
||||
flex: 1,
|
||||
backgroundColor: '#f8fafc',
|
||||
},
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingHorizontal:10,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: '#f1f5f9',
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
backBtn: {
|
||||
padding: 8,
|
||||
marginRight: 8,
|
||||
},
|
||||
headerTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: '800',
|
||||
color: '#0f172a',
|
||||
},
|
||||
headerSub: {
|
||||
fontSize: 13,
|
||||
color: '#64748b',
|
||||
},
|
||||
liveBox: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 5,
|
||||
borderRadius: 20,
|
||||
borderWidth: 1,
|
||||
borderColor: '#e2e8f0',
|
||||
},
|
||||
dot: {
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: 4,
|
||||
},
|
||||
liveText: {
|
||||
fontSize: 11,
|
||||
fontWeight: '800',
|
||||
color: '#475569',
|
||||
},
|
||||
content: {
|
||||
padding: 16,
|
||||
paddingBottom: 32,
|
||||
},
|
||||
sectionContainer: {
|
||||
backgroundColor: '#ffffff',
|
||||
borderRadius: 16,
|
||||
padding: 16,
|
||||
marginBottom: 16,
|
||||
borderWidth: 1,
|
||||
borderColor: '#e2e8f0',
|
||||
},
|
||||
sectionHeader: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
marginBottom: 12,
|
||||
paddingBottom: 6,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: '#f8fafc',
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 14,
|
||||
fontWeight: '800',
|
||||
color: '#1e293b',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.4,
|
||||
},
|
||||
card: {
|
||||
backgroundColor: '#ffffff',
|
||||
borderRadius: 12,
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: 8,
|
||||
paddingVertical: 8,
|
||||
borderBottomWidth: 0.5,
|
||||
borderBottomColor: '#e5e7eb',
|
||||
},
|
||||
label: {
|
||||
fontSize: 13,
|
||||
color: '#64748b',
|
||||
flex: 1,
|
||||
paddingRight: 8,
|
||||
fontWeight: '600',
|
||||
},
|
||||
value: {
|
||||
fontSize: 13,
|
||||
color: '#111827',
|
||||
flex: 1,
|
||||
textAlign: 'right',
|
||||
fontWeight: '500',
|
||||
},
|
||||
documentRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingVertical: 10,
|
||||
gap: 10,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: '#f1f5f9',
|
||||
},
|
||||
docInfo: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
flex: 1.4,
|
||||
},
|
||||
fileIcon: {
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 8,
|
||||
backgroundColor: '#e0f2fe',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginRight: 8,
|
||||
},
|
||||
docName: {
|
||||
flex: 1,
|
||||
color: '#111827',
|
||||
fontSize: 13,
|
||||
fontWeight: '600',
|
||||
},
|
||||
downloadBtn: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 4,
|
||||
},
|
||||
downloadText: {
|
||||
fontSize: 13,
|
||||
fontWeight: '700',
|
||||
color: colors.primary,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ import TenderFullDetail from './Modal/TenderFullDetail';
|
||||
import { useNavigation } from '@react-navigation/native';
|
||||
|
||||
const LiveTenderTile: React.FC<LiveTenderTileProps> = ({
|
||||
contractorId,
|
||||
data,
|
||||
isPurchased = false,
|
||||
onPurchasePress,
|
||||
@@ -22,14 +23,21 @@ const LiveTenderTile: React.FC<LiveTenderTileProps> = ({
|
||||
const navigation: any = useNavigation();
|
||||
const purchaseEnded = moment().isAfter(moment(purchaseEnd, 'DD-MM-YYYY HH:mm:ss'));
|
||||
|
||||
console.log("live tender data : ", data)
|
||||
const contractorIds = typeof data?.contractorId === 'string'
|
||||
? data.contractorId.split(',').map((id:any) => id.trim())
|
||||
: Array.isArray(data?.contractorId)
|
||||
? data.contractorId
|
||||
: [];
|
||||
|
||||
const purchased = contractorIds.includes(String(contractorId));
|
||||
|
||||
return (
|
||||
<View style={styles.cardWrapper}>
|
||||
{/* Status Tag */}
|
||||
<View
|
||||
style={[
|
||||
styles.statusTag,
|
||||
{ backgroundColor: isPurchased ? colors.error : purchaseEnded ? '#94a3b8' : '#4ade80' },
|
||||
{ backgroundColor: isPurchased ? colors.error : purchaseEnded ? '#94a3b8' : (purchased?'#94a3b8':'#4ade80') },
|
||||
]}
|
||||
/>
|
||||
|
||||
@@ -75,7 +83,7 @@ const LiveTenderTile: React.FC<LiveTenderTileProps> = ({
|
||||
|
||||
{/* Action Bar */}
|
||||
<View style={styles.actionBar}>
|
||||
<TouchableOpacity style={styles.iconBtn} onPress={onDetailsPress}>
|
||||
<TouchableOpacity style={styles.iconBtn} onPress={()=>navigation.navigate('LiveTenderDetail', data)}>
|
||||
<Info size={18} color={colors.primary} />
|
||||
<Text style={styles.iconBtnText}>Details</Text>
|
||||
</TouchableOpacity>
|
||||
@@ -85,12 +93,12 @@ const LiveTenderTile: React.FC<LiveTenderTileProps> = ({
|
||||
styles.mainButton,
|
||||
purchaseEnded
|
||||
? styles.btnDisabled
|
||||
: isPurchased
|
||||
:(purchased? styles.btnDisabled: isPurchased
|
||||
? styles.btnPurchased
|
||||
: styles.btnActive,
|
||||
: styles.btnActive),
|
||||
]}
|
||||
onPress={onPurchasePress}
|
||||
disabled={purchaseEnded}
|
||||
disabled={purchaseEnded || purchased}
|
||||
>
|
||||
<Text
|
||||
style={[
|
||||
@@ -98,9 +106,9 @@ const LiveTenderTile: React.FC<LiveTenderTileProps> = ({
|
||||
isPurchased && !purchaseEnded ? { color: colors.white } : { color: '#fff' },
|
||||
]}
|
||||
>
|
||||
{purchaseEnded ? 'Expired' : isPurchased ? 'Remove' : 'Add to Cart'}
|
||||
{purchaseEnded ? 'Expired' : (purchased?'Purchased':isPurchased ? 'Remove' : 'Add to Cart')}
|
||||
</Text>
|
||||
{!purchaseEnded && (
|
||||
{(!purchaseEnded && !purchased) && (
|
||||
<ArrowRight size={16} color={isPurchased ? colors.white : '#fff'} />
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { StyleSheet, Text, View, ScrollView, StatusBar, TouchableOpacity, RefreshControl } from 'react-native'
|
||||
import React, { useEffect } from 'react'
|
||||
import { StyleSheet, Text, View, ScrollView, StatusBar, TouchableOpacity, RefreshControl, ActivityIndicator } from 'react-native'
|
||||
import React, { useCallback, useEffect } from 'react'
|
||||
import { InsideHeader } from '../../Component/Index'
|
||||
import colors from '../../Constant/Color'
|
||||
import { useNavigation, useRoute } from '@react-navigation/native'
|
||||
import { useFocusEffect, useNavigation, useRoute } from '@react-navigation/native'
|
||||
import { useFetchTenderById, useProceedToBid } from '../../Services/BBPS/Hooks'
|
||||
import FontsFamily from '../../Constant/FontsFamily'
|
||||
|
||||
const ProceedToBidDetail = () => {
|
||||
// Sample data based on your JSON
|
||||
const route = useRoute<any>();
|
||||
const tenderDataId = route.params?.tender;
|
||||
const { data, refetch } = useProceedToBid(tenderDataId?.tenderId);
|
||||
const { data: tenderResponse , refetch:tenderRefetch}: any = useFetchTenderById(tenderDataId?.tenderId);
|
||||
const { data: tenderResponse, refetch: tenderRefetch }: any = useFetchTenderById(tenderDataId?.tenderId);
|
||||
const tenderData = tenderResponse?.data;
|
||||
const [refreshing, setRefreshing] = React.useState(false);
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
|
||||
console.log("proceed to bid tender detail : ", tenderDataId)
|
||||
console.log("proceed to data detail : ", data)
|
||||
@@ -22,18 +22,31 @@ const ProceedToBidDetail = () => {
|
||||
// Format date from array
|
||||
|
||||
const handleStartBidding = () => {
|
||||
refetch();
|
||||
if (data) {
|
||||
console.log(" ppp",tenderData ,"ll",data)
|
||||
navigation.navigate('TenderBidding', { tenderData: tenderData, bidData: data.data });
|
||||
try {
|
||||
setLoading(true);
|
||||
refetch();
|
||||
if (data) {
|
||||
console.log(" ppp", tenderData, "ll", data)
|
||||
navigation.navigate('TenderBidding', { tenderData: tenderData, bidData: data.data });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error in starting bidding: ", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
}
|
||||
const formatDate = (dateArray: any[]) => {
|
||||
if (!dateArray || dateArray.length < 3) return "N/A";
|
||||
const [year, month, day, hour = 0, minute = 0] = dateArray;
|
||||
return `${day.toString().padStart(2, '0')}/${month.toString().padStart(2, '0')}/${year} ${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
// Status indicator component
|
||||
const StatusIndicator = ({ status, label }: { status: boolean; label: string }) => (
|
||||
<View style={styles.statusContainer}>
|
||||
<View style={[styles.statusDot, { backgroundColor: status ? colors.success : colors.error }]} />
|
||||
<Text style={[styles.statusText, { color: status ? colors.success : colors.error, fontFamily: FontsFamily.poppinsSemiBold }]}>
|
||||
<Text style={[styles.statusText, { color: status ? colors.success : colors.error }]}>
|
||||
{label}: {status ? "Submitted" : "Not Submitted"}
|
||||
</Text>
|
||||
</View>
|
||||
@@ -49,53 +62,52 @@ const ProceedToBidDetail = () => {
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.logDetailRow}>
|
||||
<Text style={[styles.label, { fontFamily: FontsFamily.poppinsMedium }]}>Activity:</Text>
|
||||
<Text style={[styles.value, { fontFamily: FontsFamily.poppinsRegular }]}>{log?.activity}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.logDetailRow}>
|
||||
<Text style={[styles.label, { fontFamily: FontsFamily.poppinsMedium }]}>Date & Time:</Text>
|
||||
<Text style={[styles.value, { fontFamily: FontsFamily.poppinsRegular }]}>{format.date6(log?.dateTime)}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.logDetailRow}>
|
||||
<Text style={[styles.label, { fontFamily: FontsFamily.poppinsMedium }]}>System Info:</Text>
|
||||
<Text style={[styles.value, { fontFamily: FontsFamily.poppinsRegular }]}>{log?.osName} / {log?.browserName}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.logDetailRow}>
|
||||
<Text style={[styles.label, { fontFamily: FontsFamily.poppinsMedium }]}>IP Address:</Text>
|
||||
<Text style={[styles.value, { fontFamily: FontsFamily.poppinsRegular }]}>{log?.ipAddress}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.logDetailRow}>
|
||||
<Text style={[styles.label, { fontFamily: FontsFamily.poppinsMedium }]}>Status Message:</Text>
|
||||
<Text style={[styles.value, styles.successText, { fontFamily: FontsFamily.poppinsSemiBold }]}>{log?.logMessage}</Text>
|
||||
</View>
|
||||
|
||||
{log?.headingMsg && (
|
||||
<View style={styles.logDetailRow}>
|
||||
<Text style={[styles.label, { fontFamily: FontsFamily.poppinsMedium }]}>Heading:</Text>
|
||||
<Text style={[styles.value, { fontFamily: FontsFamily.poppinsRegular }]}>{log?.headingMsg}</Text>
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.logDetailRow}>
|
||||
<Text style={styles.label}>Activity:</Text>
|
||||
<Text style={styles.value}>{log?.activity}</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
<View style={styles.logDetailRow}>
|
||||
<Text style={styles.label}>Date & Time:</Text>
|
||||
<Text style={styles.value}>{log?.dateTime}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.logDetailRow}>
|
||||
<Text style={styles.label}>System Info:</Text>
|
||||
<Text style={styles.value}>{log?.osName} / {log?.browserName}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.logDetailRow}>
|
||||
<Text style={styles.label}>IP Address:</Text>
|
||||
<Text style={styles.value}>{log?.ipAddress}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.logDetailRow}>
|
||||
<Text style={styles.label}>Status Message:</Text>
|
||||
<Text style={[styles.value, styles.successText]}>{log?.logMessage}</Text>
|
||||
</View>
|
||||
|
||||
{log?.headingMsg && (
|
||||
<View style={styles.logDetailRow}>
|
||||
<Text style={styles.label}>Heading:</Text>
|
||||
<Text style={styles.value}>{log?.headingMsg}</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
|
||||
// Information row component
|
||||
const InfoRow = ({ label, value, isHighlighted = false }: { label: string; value: string; isHighlighted?: boolean }) => (
|
||||
<View style={styles.infoRow}>
|
||||
<Text style={[styles.infoLabel, { fontFamily: FontsFamily.poppinsMedium }]}>{label}:</Text>
|
||||
<Text style={[styles.infoValue, isHighlighted && styles.highlightedValue, { fontFamily: FontsFamily.poppinsSemiBold }]}>{value}</Text>
|
||||
<Text style={styles.infoLabel}>{label}:</Text>
|
||||
<Text style={[styles.infoValue, isHighlighted && styles.highlightedValue]}>{value}</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
// Feature chip component
|
||||
const FeatureChip = ({ label, enabled }: { label: string; enabled: boolean }) => (
|
||||
<View style={[styles.chip, { backgroundColor: enabled ? colors.successLight : colors.lightGray }]}>
|
||||
<Text style={[styles.chipText, { color: enabled ? colors.success : colors.darkGray, fontFamily: FontsFamily.poppinsSemiBold }]}>
|
||||
<Text style={[styles.chipText, { color: enabled ? colors.success : colors.darkGray }]}>
|
||||
{label} {enabled ? '✓' : '✗'}
|
||||
</Text>
|
||||
</View>
|
||||
@@ -110,9 +122,11 @@ const ProceedToBidDetail = () => {
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(()=>{
|
||||
tenderRefetch()
|
||||
},[tenderDataId?.tenderId])
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
tenderRefetch();
|
||||
}, [tenderData.tenderId])
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, backgroundColor: colors.white }}>
|
||||
@@ -131,34 +145,34 @@ const ProceedToBidDetail = () => {
|
||||
<View style={styles.headerCard}>
|
||||
<View style={styles.tenderHeader}>
|
||||
<View>
|
||||
<Text style={[styles.tenderNumber, { fontFamily: FontsFamily.poppinsSemiBold }]}>{tenderData.tenderNumber}</Text>
|
||||
<Text style={[styles.tenderId, { fontFamily: FontsFamily.poppinsRegular }]}>ID: {tenderData.tenderId}</Text>
|
||||
<Text style={styles.tenderNumber}>{tenderData?.tenderNumber}</Text>
|
||||
<Text style={styles.tenderId}>ID: {tenderData?.tenderId}</Text>
|
||||
</View>
|
||||
<View style={[styles.statusBadge, { backgroundColor: tenderData.biddingStatus ? colors.success : colors.warning }]}>
|
||||
<Text style={[styles.statusBadgeText, { fontFamily: FontsFamily.poppinsSemiBold }]}>
|
||||
{tenderData.biddingStatus ? "Active" : "Inactive"}
|
||||
<View style={[styles.statusBadge, { backgroundColor: tenderData?.biddingStatus ? colors.success : colors.warning }]}>
|
||||
<Text style={styles.statusBadgeText}>
|
||||
{tenderData?.biddingStatus ? "Active" : "Inactive"}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<Text style={[styles.workDescription, { fontFamily: FontsFamily.poppinsRegular }]} numberOfLines={1}>{tenderData.nameofWork}</Text>
|
||||
<Text style={styles.workDescription}>{tenderData?.nameofWork}</Text>
|
||||
|
||||
<View style={styles.headerInfoRow}>
|
||||
<View style={styles.headerInfoItem}>
|
||||
<Text style={[styles.headerInfoLabel, { fontFamily: FontsFamily.poppinsMedium }]}>NIT No.</Text>
|
||||
<Text style={[styles.headerInfoValue, { fontFamily: FontsFamily.poppinsSemiBold }]}>{tenderData.nitNo}</Text>
|
||||
<Text style={styles.headerInfoLabel}>NIT No.</Text>
|
||||
<Text style={styles.headerInfoValue}>{tenderData?.nitNo}</Text>
|
||||
</View>
|
||||
<View style={styles.separator} />
|
||||
<View style={styles.headerInfoItem}>
|
||||
<Text style={[styles.headerInfoLabel, { fontFamily: FontsFamily.poppinsMedium }]}>EMD Amount</Text>
|
||||
<Text style={[styles.headerInfoValue, { fontFamily: FontsFamily.poppinsSemiBold }]}>₹{tenderData.emdInFig}</Text>
|
||||
<Text style={styles.headerInfoLabel}>EMD Amount</Text>
|
||||
<Text style={styles.headerInfoValue}>₹{tenderData?.emdInFig}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Envelope Status */}
|
||||
{/* Envelope Submission Status */}
|
||||
<View style={styles.section}>
|
||||
<Text style={[styles.sectionTitle, { color: colors.black }]}>{`Envelope Submission Status`}</Text>
|
||||
<Text style={styles.sectionTitle}>Envelope Submission Status</Text>
|
||||
<View style={styles.statusGrid}>
|
||||
<StatusIndicator status={tenderData?.envelopeASubmittStatus} label="Envelope A" />
|
||||
<StatusIndicator status={tenderData?.envelopeBSubmittStatus} label="Envelope B" />
|
||||
@@ -166,19 +180,19 @@ const ProceedToBidDetail = () => {
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Logs */}
|
||||
{tenderData.envelopeA_Logs && (
|
||||
{/* Envelope Logs */}
|
||||
{tenderData?.envelopeA_Logs && (
|
||||
<View style={styles.section}>
|
||||
<Text style={[styles.sectionTitle, { color: colors.black }]}>Submission Logs</Text>
|
||||
<EnvelopeLogCard log={tenderData.envelopeA_Logs} title="Envelope A Submission" />
|
||||
<EnvelopeLogCard log={tenderData.envelopeB_Logs} title="Envelope B Submission" />
|
||||
<EnvelopeLogCard log={tenderData.envelopeC_Logs} title="Envelope C Submission" />
|
||||
<Text style={styles.sectionTitle}>Submission Logs</Text>
|
||||
<EnvelopeLogCard log={tenderData?.envelopeA_Logs} title="Envelope A Submission" />
|
||||
<EnvelopeLogCard log={tenderData?.envelopeB_Logs} title="Envelope B Submission" />
|
||||
<EnvelopeLogCard log={tenderData?.envelopeC_Logs} title="Envelope C Submission" />
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* Tender Details */}
|
||||
<View style={styles.section}>
|
||||
<Text style={[styles.sectionTitle, { color: colors.black }]}>Tender Details</Text>
|
||||
<Text style={styles.sectionTitle}>Tender Details</Text>
|
||||
<View style={styles.detailsCard}>
|
||||
<InfoRow label="Envelope Type" value={tenderData?.envelopeType} />
|
||||
<InfoRow label="Tender Stages" value={tenderData?.tenderStages} />
|
||||
@@ -188,24 +202,24 @@ const ProceedToBidDetail = () => {
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Submission Time */}
|
||||
{/* Time Period */}
|
||||
<View style={styles.section}>
|
||||
<Text style={[styles.sectionTitle, { color: colors.black }]}>Submission Time Period</Text>
|
||||
<Text style={styles.sectionTitle}>Submission Time Period</Text>
|
||||
<View style={styles.timeCard}>
|
||||
<View style={styles.timeRow}>
|
||||
<Text style={[styles.timeLabel, { fontFamily: FontsFamily.poppinsSemiBold }]}>Starts On:</Text>
|
||||
<Text style={[styles.timeValue, { fontFamily: FontsFamily.poppinsSemiBold }]}>{format.date6(tenderData.submittedStartDate)}</Text>
|
||||
<Text style={styles.timeLabel}>Starts On:</Text>
|
||||
<Text style={styles.timeValue}>{formatDate(tenderData?.submittedStartDate)}</Text>
|
||||
</View>
|
||||
<View style={styles.timeRow}>
|
||||
<Text style={[styles.timeLabel, { fontFamily: FontsFamily.poppinsSemiBold }]}>Ends On:</Text>
|
||||
<Text style={[styles.timeValue, { fontFamily: FontsFamily.poppinsSemiBold }]}>{format.date6(tenderData.submittedEndDate)}</Text>
|
||||
<Text style={styles.timeLabel}>Ends On:</Text>
|
||||
<Text style={styles.timeValue}>{formatDate(tenderData?.submittedEndDate)}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Feature Chips */}
|
||||
{/* Feature Flags */}
|
||||
<View style={styles.section}>
|
||||
<Text style={[styles.sectionTitle, { color: colors.black }]}>Available Features</Text>
|
||||
<Text style={styles.sectionTitle}>Available Features</Text>
|
||||
<View style={styles.chipsContainer}>
|
||||
<FeatureChip label="View Bid" enabled={tenderData?.viewBid} />
|
||||
<FeatureChip label="Proceed to Bid" enabled={tenderData?.proceedtoBid} />
|
||||
@@ -216,93 +230,362 @@ const ProceedToBidDetail = () => {
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Overall Status */}
|
||||
{/* Final Status */}
|
||||
<View style={styles.finalStatusCard}>
|
||||
<Text style={[styles.finalStatusTitle, { fontFamily: FontsFamily.poppinsSemiBold }]}>Overall Status</Text>
|
||||
<Text style={styles.finalStatusTitle}>Overall Status</Text>
|
||||
<View style={styles.finalStatusRow}>
|
||||
<Text style={[styles.finalStatusLabel, { fontFamily: FontsFamily.poppinsMedium }]}>All Envelopes Submitted:</Text>
|
||||
<Text style={[styles.finalStatusValue, {
|
||||
color: tenderData.envelopeASubmittStatus && tenderData.envelopeBSubmittStatus && tenderData.envelopeCSubmittStatus
|
||||
? colors.success : colors.error, fontFamily: FontsFamily.poppinsSemiBold
|
||||
<Text style={styles.finalStatusLabel}>All Envelopes Submitted:</Text>
|
||||
<Text style={[styles.finalStatusValue,
|
||||
{
|
||||
color: tenderData?.envelopeASubmittStatus && tenderData?.envelopeBSubmittStatus && tenderData?.envelopeCSubmittStatus
|
||||
? colors.success : colors.error
|
||||
}]}>
|
||||
{tenderData.envelopeASubmittStatus && tenderData.envelopeBSubmittStatus && tenderData.envelopeCSubmittStatus ? "COMPLETED" : "PENDING"}
|
||||
{tenderData?.envelopeASubmittStatus && tenderData?.envelopeBSubmittStatus && tenderData?.envelopeCSubmittStatus
|
||||
? "COMPLETED" : "PENDING"}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.finalStatusRow}>
|
||||
<Text style={[styles.finalStatusLabel, { fontFamily: FontsFamily.poppinsMedium }]}>Bidding Status:</Text>
|
||||
<Text style={[styles.finalStatusValue, { color: tenderData.biddingStatus ? colors.success : colors.error, fontFamily: FontsFamily.poppinsSemiBold }]}>
|
||||
{tenderData.biddingStatus ? "ACTIVE" : "CLOSED"}
|
||||
<Text style={styles.finalStatusLabel}>Bidding Status:</Text>
|
||||
<Text style={[styles.finalStatusValue,
|
||||
{ color: tenderData?.biddingStatus ? colors.success : colors.error }]}>
|
||||
{tenderData?.biddingStatus ? "ACTIVE" : "CLOSED"}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.footerSpace} />
|
||||
</ScrollView>
|
||||
|
||||
{/* Footer */}
|
||||
|
||||
|
||||
<View style={styles.footer}>
|
||||
{
|
||||
tenderData?.proceedtoBid && !tenderData?.viewBid && tenderData?.biddingStatus && tenderData?.bidWithdrawStatus === false ? (
|
||||
|
||||
<TouchableOpacity style={styles.proceedButton} onPress={handleStartBidding}>
|
||||
<Text style={[styles.proceedButtonText, { fontFamily: FontsFamily.poppinsSemiBold }]}>Start Bidding</Text>
|
||||
<Text style={styles.proceedButtonText}>{loading ? <ActivityIndicator size={'small'} color={'#ffff'}/> : 'Start Bidding'}</Text>
|
||||
</TouchableOpacity>
|
||||
) : <Text style={[styles.footerText, { fontFamily: FontsFamily.poppinsMedium }]}>One time bidding is completed</Text>
|
||||
}
|
||||
</View>
|
||||
|
||||
) : <Text style={styles.footerText}>One time bidding is completed</Text>
|
||||
} </View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProceedToBidDetail;
|
||||
)
|
||||
}
|
||||
|
||||
export default ProceedToBidDetail
|
||||
|
||||
// Styles remain mostly unchanged but improved shadows, spacing, and rounded corners
|
||||
const styles = StyleSheet.create({
|
||||
container: { flex: 1, padding: 16 },
|
||||
headerCard: { backgroundColor: colors.white, borderRadius: 14, padding: 20, marginBottom: 20, borderLeftWidth: 4, borderLeftColor: colors.primary, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 5, elevation: 5 },
|
||||
tenderHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 12 },
|
||||
tenderNumber: { fontSize: 22, fontWeight: 'bold', color: colors.primary },
|
||||
tenderId: { fontSize: 14, color: colors.darkGray, marginTop: 4 },
|
||||
statusBadge: { paddingHorizontal: 12, paddingVertical: 6, borderRadius: 20 },
|
||||
statusBadgeText: { color: colors.white, fontWeight: '600', fontSize: 12 },
|
||||
workDescription: { fontSize: 13, color: colors.black, marginBottom: 16, fontFamily: FontsFamily.poppinsMedium },
|
||||
headerInfoRow: { flexDirection: 'row', alignItems: 'center', backgroundColor: colors.lightestGray, borderRadius: 8, padding: 12 },
|
||||
headerInfoItem: { flex: 1, alignItems: 'center' },
|
||||
headerInfoLabel: { fontSize: 12, color: colors.darkGray, marginBottom: 4 },
|
||||
headerInfoValue: { fontSize: 16, fontWeight: '600', color: colors.primary },
|
||||
separator: { width: 1, height: 30, backgroundColor: colors.lightGray },
|
||||
section: { marginBottom: 20 },
|
||||
sectionTitle: { fontSize: 18, fontWeight: '600', marginBottom: 12, paddingLeft: 4 },
|
||||
statusGrid: { backgroundColor: colors.white, borderRadius: 14, padding: 16, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 5, elevation: 3 },
|
||||
statusContainer: { flexDirection: 'row', alignItems: 'center', marginBottom: 12 },
|
||||
statusDot: { width: 12, height: 12, borderRadius: 6, marginRight: 10 },
|
||||
statusText: { fontSize: 15, fontWeight: '500' },
|
||||
envelopeCard: { backgroundColor: colors.white, borderRadius: 14, padding: 16, marginBottom: 12, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.05, shadowRadius: 4, elevation: 2 },
|
||||
cardHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 },
|
||||
cardTitle: { fontSize: 16, fontWeight: '600', color: colors.black },
|
||||
envelopeBadge: { paddingHorizontal: 10, paddingVertical: 4, borderRadius: 12 },
|
||||
envelopeBadgeText: { color: colors.white, fontWeight: 'bold', fontSize: 12 },
|
||||
logDetailRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 },
|
||||
label: { fontSize: 14, color: colors.black, flex: 1, fontFamily: FontsFamily.poppinsSemiBold },
|
||||
value: { fontSize: 14, color: colors.darkText, flex: 2, textAlign: 'right' },
|
||||
successText: { color: colors.success, fontWeight: '600' },
|
||||
detailsCard: { backgroundColor: colors.white, borderRadius: 14, padding: 16, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.05, shadowRadius: 4, elevation: 3 },
|
||||
infoRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 10, borderBottomWidth: 1, borderBottomColor: colors.lightestGray },
|
||||
infoLabel: { fontSize: 14, color: colors.black, fontFamily: FontsFamily.poppinsSemiBold },
|
||||
infoValue: { fontSize: 15, fontWeight: '500', color: colors.darkText },
|
||||
highlightedValue: { color: colors.success, fontWeight: '600' },
|
||||
timeCard: { backgroundColor: colors.white, borderRadius: 14, padding: 16, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.05, shadowRadius: 4, elevation: 3 },
|
||||
timeRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: colors.lightestGray },
|
||||
timeLabel: { fontSize: 15, color: colors.black, },
|
||||
timeValue: { fontSize: 15, color: colors.grayText, },
|
||||
chipsContainer: { flexDirection: 'row', flexWrap: 'wrap', gap: 10 },
|
||||
chip: { paddingHorizontal: 16, paddingVertical: 8, borderRadius: 20, marginBottom: 8 },
|
||||
chipText: { fontSize: 14, fontWeight: '500' },
|
||||
finalStatusCard: { backgroundColor: colors.white, borderRadius: 14, padding: 20, marginTop: 10, borderWidth: 2, borderColor: colors.primary, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 5, elevation: 5 },
|
||||
finalStatusTitle: { fontSize: 18, fontWeight: 'bold', marginBottom: 16, textAlign: 'center' },
|
||||
finalStatusRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12, paddingVertical: 8, borderBottomWidth: 1, borderBottomColor: 'rgba(0,0,0,0.1)' },
|
||||
finalStatusLabel: { fontSize: 14, color: colors.black, fontFamily: FontsFamily.poppinsSemiBold },
|
||||
finalStatusValue: { fontSize: 13, },
|
||||
footer: { position: 'absolute', bottom: 0, left: 0, right: 0, backgroundColor: colors.white, padding: 16, borderTopWidth: 1, borderTopColor: colors.lightGray, elevation: 10 },
|
||||
proceedButton: { backgroundColor: colors.primary, paddingVertical: 14, borderRadius: 30, alignItems: 'center' },
|
||||
proceedButtonText: { color: colors.white, fontSize: 16, fontWeight: '600' },
|
||||
footerText: { textAlign: 'center', color: colors.darkGray, fontSize: 14 },
|
||||
});
|
||||
container: {
|
||||
flex: 1,
|
||||
padding: 16,
|
||||
marginBottom: 60,
|
||||
},
|
||||
headerCard: {
|
||||
backgroundColor: colors.primaryLight,
|
||||
borderRadius: 12,
|
||||
padding: 20,
|
||||
marginBottom: 20,
|
||||
borderLeftWidth: 4,
|
||||
borderLeftColor: colors.primary,
|
||||
},
|
||||
tenderHeader: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: 12,
|
||||
},
|
||||
tenderNumber: {
|
||||
fontSize: 22,
|
||||
fontWeight: 'bold',
|
||||
color: colors.primary,
|
||||
},
|
||||
tenderId: {
|
||||
fontSize: 14,
|
||||
color: colors.darkGray,
|
||||
marginTop: 4,
|
||||
},
|
||||
statusBadge: {
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 6,
|
||||
borderRadius: 20,
|
||||
},
|
||||
statusBadgeText: {
|
||||
color: colors.white,
|
||||
fontWeight: '600',
|
||||
fontSize: 12,
|
||||
},
|
||||
workDescription: {
|
||||
fontSize: 16,
|
||||
color: colors.darkText,
|
||||
lineHeight: 22,
|
||||
marginBottom: 16,
|
||||
},
|
||||
headerInfoRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: colors.white,
|
||||
borderRadius: 8,
|
||||
padding: 12,
|
||||
},
|
||||
headerInfoItem: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
},
|
||||
headerInfoLabel: {
|
||||
fontSize: 12,
|
||||
color: colors.darkGray,
|
||||
marginBottom: 4,
|
||||
},
|
||||
headerInfoValue: {
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
color: colors.primary,
|
||||
},
|
||||
separator: {
|
||||
width: 1,
|
||||
height: 30,
|
||||
backgroundColor: colors.lightGray,
|
||||
},
|
||||
section: {
|
||||
marginBottom: 20,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: '600',
|
||||
color: colors.primary,
|
||||
marginBottom: 12,
|
||||
paddingLeft: 4,
|
||||
},
|
||||
statusGrid: {
|
||||
backgroundColor: colors.white,
|
||||
borderRadius: 12,
|
||||
padding: 16,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 4,
|
||||
elevation: 3,
|
||||
},
|
||||
statusContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginBottom: 12,
|
||||
},
|
||||
statusDot: {
|
||||
width: 12,
|
||||
height: 12,
|
||||
borderRadius: 6,
|
||||
marginRight: 10,
|
||||
},
|
||||
statusText: {
|
||||
fontSize: 15,
|
||||
fontWeight: '500',
|
||||
},
|
||||
envelopeCard: {
|
||||
backgroundColor: colors.white,
|
||||
borderRadius: 12,
|
||||
padding: 16,
|
||||
marginBottom: 12,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.lightGray,
|
||||
},
|
||||
cardHeader: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: 12,
|
||||
},
|
||||
cardTitle: {
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
color: colors.primary,
|
||||
},
|
||||
envelopeBadge: {
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 4,
|
||||
borderRadius: 12,
|
||||
},
|
||||
envelopeBadgeText: {
|
||||
color: colors.white,
|
||||
fontWeight: 'bold',
|
||||
fontSize: 12,
|
||||
},
|
||||
logDetailRow: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: 8,
|
||||
},
|
||||
label: {
|
||||
fontSize: 14,
|
||||
color: colors.darkGray,
|
||||
flex: 1,
|
||||
},
|
||||
value: {
|
||||
fontSize: 14,
|
||||
color: colors.darkText,
|
||||
fontWeight: '500',
|
||||
flex: 2,
|
||||
textAlign: 'right',
|
||||
},
|
||||
successText: {
|
||||
color: colors.success,
|
||||
fontWeight: '600',
|
||||
},
|
||||
detailsCard: {
|
||||
backgroundColor: colors.white,
|
||||
borderRadius: 12,
|
||||
padding: 16,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 4,
|
||||
elevation: 3,
|
||||
},
|
||||
infoRow: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
paddingVertical: 10,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.lightestGray,
|
||||
},
|
||||
infoLabel: {
|
||||
fontSize: 14,
|
||||
color: colors.darkGray,
|
||||
},
|
||||
infoValue: {
|
||||
fontSize: 15,
|
||||
fontWeight: '500',
|
||||
color: colors.darkText,
|
||||
},
|
||||
highlightedValue: {
|
||||
color: colors.success,
|
||||
fontWeight: '600',
|
||||
},
|
||||
timeCard: {
|
||||
backgroundColor: colors.white,
|
||||
borderRadius: 12,
|
||||
padding: 16,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 4,
|
||||
elevation: 3,
|
||||
},
|
||||
timeRow: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
paddingVertical: 12,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: colors.lightestGray,
|
||||
},
|
||||
timeLabel: {
|
||||
fontSize: 15,
|
||||
color: colors.darkGray,
|
||||
fontWeight: '500',
|
||||
},
|
||||
timeValue: {
|
||||
fontSize: 15,
|
||||
color: colors.primary,
|
||||
fontWeight: '600',
|
||||
},
|
||||
chipsContainer: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
gap: 10,
|
||||
},
|
||||
chip: {
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 20,
|
||||
marginBottom: 8,
|
||||
},
|
||||
chipText: {
|
||||
fontSize: 14,
|
||||
fontWeight: '500',
|
||||
},
|
||||
finalStatusCard: {
|
||||
backgroundColor: colors.primaryLight,
|
||||
borderRadius: 12,
|
||||
padding: 20,
|
||||
marginTop: 10,
|
||||
borderWidth: 2,
|
||||
borderColor: colors.primary,
|
||||
},
|
||||
finalStatusTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold',
|
||||
color: colors.primary,
|
||||
marginBottom: 16,
|
||||
textAlign: 'center',
|
||||
},
|
||||
finalStatusRow: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: 12,
|
||||
paddingVertical: 8,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: 'rgba(0, 0, 0, 0.1)',
|
||||
},
|
||||
finalStatusLabel: {
|
||||
fontSize: 16,
|
||||
color: colors.darkText,
|
||||
fontWeight: '500',
|
||||
},
|
||||
finalStatusValue: {
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
footerSpace: {
|
||||
height: 30,
|
||||
},
|
||||
success: {
|
||||
color: colors.success,
|
||||
},
|
||||
error: {
|
||||
color: colors.error,
|
||||
},
|
||||
primary: {
|
||||
color: colors.primary,
|
||||
},
|
||||
warning: {
|
||||
color: colors.warning,
|
||||
},
|
||||
successLight: {
|
||||
backgroundColor: colors.successLight,
|
||||
},
|
||||
lightGray: {
|
||||
backgroundColor: colors.lightGray,
|
||||
},
|
||||
footer: {
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
backgroundColor: colors.white,
|
||||
padding: 16,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: colors.lightGray,
|
||||
elevation: 10,
|
||||
},
|
||||
proceedButton: {
|
||||
backgroundColor: colors.primary,
|
||||
paddingVertical: 14,
|
||||
borderRadius: 30,
|
||||
alignItems: 'center',
|
||||
},
|
||||
proceedButtonText: {
|
||||
color: colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
},
|
||||
footerText: {
|
||||
textAlign: 'center',
|
||||
color: colors.darkGray,
|
||||
fontSize: 14,
|
||||
},
|
||||
});
|
||||
@@ -22,7 +22,6 @@ const PurchaseTenderTile = ({ item, onPressDetails }: any) => {
|
||||
const tender = tenderResponse?.data;
|
||||
|
||||
|
||||
console.log("tender for bid : ", tender)
|
||||
const handlePressIn = () => {
|
||||
scale.value = withTiming(0.97, { duration: 120 });
|
||||
elevation.value = withTiming(3, { duration: 120 });
|
||||
@@ -105,10 +104,12 @@ const PurchaseTenderTile = ({ item, onPressDetails }: any) => {
|
||||
</View>
|
||||
|
||||
<TouchableOpacity
|
||||
disabled={!isLive}
|
||||
// disabled={!isLive}
|
||||
onPress={() => {
|
||||
if (isLive) {
|
||||
navigation.navigate('ProceedToBidDetail', { tender });
|
||||
}else{
|
||||
navigation.navigate('LiveTenderDetail', tender)
|
||||
}
|
||||
}}
|
||||
activeOpacity={0.85}
|
||||
|
||||
@@ -26,10 +26,12 @@ const TAB_HEIGHT = 44;
|
||||
|
||||
const Tender = () => {
|
||||
const Navigation = useNavigation() as any
|
||||
const { data: liveTenderData = {} }: any = useFetchLiveTender();
|
||||
const { data: purchaseTenderData = {} } : any = useFetchPurchaseTender();
|
||||
const { data: liveTenderData }: any = useFetchLiveTender();
|
||||
const { data: purchaseTenderData} : any = useFetchPurchaseTender();
|
||||
const SelectedPurchaseList = useSelector((state: any) => state?.selectPurchaseTender);
|
||||
|
||||
console.log("live tender : ", liveTenderData)
|
||||
console.log("purchase tender : ", purchaseTenderData)
|
||||
const liveCount = liveTenderData?.totalCount || 0;
|
||||
|
||||
const purchaseCount = purchaseTenderData?.pages?.[0]?.totalCount || 0;
|
||||
|
||||
@@ -18,9 +18,10 @@ import colors from '../../../Constant/Color';
|
||||
import { SUBMIT_ENVELOPE_A, SUBMIT_ENVELOPE_B, SUBMIT_ENVELOPE_FILE_A, SUBMIT_ENVELOPE_FILE_B } from '../../../Services/BBPS/ApiUrls';
|
||||
import { CheckSquare, Square } from 'lucide-react-native';
|
||||
import { apiCall } from '../../../Axios/Axios';
|
||||
import EditableExcel from '../component/EditableExcel';
|
||||
|
||||
import axios from 'axios';
|
||||
import { useSelector } from 'react-redux';
|
||||
import EnvelopeCExcelPreview from '../Component/EnvelopeCExcelPreview';
|
||||
|
||||
type EnvelopeType = 'A' | 'B' | 'C';
|
||||
type EnvelopeFormState = {
|
||||
@@ -47,7 +48,6 @@ const TenderBidding = () => {
|
||||
const [bidData] = useState<any>(route.params?.tenderData || null);
|
||||
const tenderData: any = route.params?.bidData || null;
|
||||
const [activeEnvelope, setActiveEnvelope] = useState<EnvelopeType>('A');
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [envelopForm, setEnvelopForm] = useState<EnvelopeFormState>({
|
||||
A: {},
|
||||
B: {},
|
||||
@@ -56,6 +56,7 @@ const TenderBidding = () => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [loadingUpload, setLoadingUpload] = useState(false)
|
||||
const [check, setCheck] = useState(false)
|
||||
const [uploadFileResponse, setUploadFileResponse] = useState<any>(null);
|
||||
|
||||
|
||||
const InfoRow = ({ title, value }: InfoRowProps) => (
|
||||
@@ -383,57 +384,54 @@ const TenderBidding = () => {
|
||||
}
|
||||
setLoading(true)
|
||||
const payload = buildEnvelopePayload();
|
||||
console.log('Envelope B Payload:', payload);
|
||||
|
||||
|
||||
if (envelopForm?.[activeEnvelope]?.[3]) {
|
||||
handleSubmitEnvelopeFile(SUBMIT_ENVELOPE_FILE_B)
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await apiCall('post', SUBMIT_ENVELOPE_B, payload) as any
|
||||
console.log("res B envelop : ", res)
|
||||
const result = await handleSubmitEnvelopeFile(SUBMIT_ENVELOPE_FILE_B)
|
||||
|
||||
ToastAndroid.show(res.message, ToastAndroid.SHORT)
|
||||
navigation.goBack()
|
||||
} catch (error) {
|
||||
console.log("error B envelop submit : ", error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
if (result && result?.statusCode === 200) {
|
||||
try {
|
||||
const res = await apiCall('post', SUBMIT_ENVELOPE_B, payload) as any
|
||||
|
||||
if (res?.statusCode === 200) {
|
||||
navigation.goBack()
|
||||
ToastAndroid.show(res.message, ToastAndroid.SHORT)
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("error B envelop submit : ", error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
} else {
|
||||
setLoading(false)
|
||||
Alert.alert('Error', 'File upload failed. Please try again.')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
const handleSubmitEnvelopeFile = async (url: string) => {
|
||||
console.log("url :: ", url)
|
||||
setLoadingUpload(true)
|
||||
const payload = buildEnvelopePayload();
|
||||
try {
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append('files', {
|
||||
uri: envelopForm?.[activeEnvelope]?.[3].uri,
|
||||
name: envelopForm?.[activeEnvelope]?.[3].name,
|
||||
type: envelopForm?.[activeEnvelope]?.[3].type,
|
||||
});
|
||||
// formData.append('requestDto', payload)
|
||||
console.log("first token : ", token?.token)
|
||||
formData.append('requestDto', JSON.stringify(payload))
|
||||
console.log("payload for fil doc : ", formData)
|
||||
const res = await axios.post(url, formData, {
|
||||
headers: {
|
||||
Authentication: `Bearer ${token?.token}`,
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
}) as any;
|
||||
console.log(`res file ${activeEnvelope} api response:`, res)
|
||||
ToastAndroid.show(res.message, ToastAndroid.SHORT)
|
||||
navigation.goBack()
|
||||
} catch (error: any) {
|
||||
console.log(`Envelope file ${activeEnvelope} error:`, error?.response || error.message || error);
|
||||
ToastAndroid.show('Upload failed', ToastAndroid.SHORT);
|
||||
} finally {
|
||||
setLoadingUpload(false);
|
||||
}
|
||||
const payload = buildEnvelopePayload();
|
||||
|
||||
const formData = new FormData();
|
||||
console.log("envelop form : ", envelopForm)
|
||||
|
||||
formData.append('files', {
|
||||
uri: envelopForm?.[activeEnvelope]?.[3].uri,
|
||||
name: envelopForm?.[activeEnvelope]?.[3].name,
|
||||
type: envelopForm?.[activeEnvelope]?.[3].type,
|
||||
});
|
||||
// formData.append('requestDto', payload)
|
||||
formData.append('requestDto', JSON.stringify(payload))
|
||||
console.log("formdata : ", formData)
|
||||
|
||||
const res: any = await apiCall('post', url, formData, {}, 'multipart/form-data');
|
||||
console.log(res, '=============================================');
|
||||
|
||||
return res;
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -508,11 +506,11 @@ const TenderBidding = () => {
|
||||
|
||||
<View style={[styles.infoRow, { flexDirection: 'row', gap: 10 }]}>
|
||||
<TouchableOpacity onPress={() => setCheck(!check)}>
|
||||
{check ? (
|
||||
<CheckSquare size={24} color={colors.primary} />
|
||||
) : (
|
||||
<Square size={24} color={colors.primary} />
|
||||
)}</TouchableOpacity>
|
||||
{check ? (
|
||||
<CheckSquare size={24} color={colors.primary} />
|
||||
) : (
|
||||
<Square size={24} color={colors.primary} />
|
||||
)}</TouchableOpacity>
|
||||
<Text>Check First Terms&Condition</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -537,7 +535,7 @@ const TenderBidding = () => {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<EditableExcel tenderId={tenderData} />
|
||||
<EnvelopeCExcelPreview tenderId={tenderData} />
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export interface LiveTenderTileProps {
|
||||
contractorId?: string;
|
||||
data: any
|
||||
onPurchasePress?: () => void;
|
||||
onDetailsPress?: () => void;
|
||||
|
||||
@@ -24,7 +24,7 @@ const CONTRACTOR_BASIC_PROFILE = `${BASE_URL}contractor/getContractorById?contId
|
||||
const CONTRACTOR_PROFILE_UPDATE = `${BASE_URL}contractor/updateContBasicDetails?id=`
|
||||
const GET_REGISTRATION_STATE = `${BASE_URL}contractor/getContractorOrgTypeandStatus`
|
||||
const GET_MASTER_DATA = `${BASE_URL}contractor/masterdata/all`
|
||||
|
||||
const GET_TENDER_BY_ID = `https://procurelinc.in/EProcurementSB/eTendering/getTenderById?tenderId=`
|
||||
const LOG_IN_URL = `${BASE_URL_Auction}auction/loginWithoutDecryption`
|
||||
const LIVE_TENDER = `${BASE_URL}liveTender/getAllLiveTenders`
|
||||
const LIVE_AUCTION = `${BASE_URL_Auction}api/auction/getLiveAuctionForMobile`
|
||||
@@ -159,5 +159,6 @@ export {
|
||||
SUBMIT_ENVELOPE_FILE_B,
|
||||
GET_ENVELOPE_C,
|
||||
SUBMIT_ENVELOPE_C_UPLOAD,
|
||||
GET_TENDER_BY_ID
|
||||
};
|
||||
|
||||
|
||||
+25
-12
@@ -1,5 +1,5 @@
|
||||
import { useInfiniteQuery, useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { contractorStageThreeSubmit, contractorStageTwoSubmit, fetchAuctionItem, fetchCityDropdown, fetchDashboard, fetchEmdCheck, fetchLiveAuction, fetchLiveTender, fetchMasterData, fetchPurchaseAuction, fetchPurchaseAuctionList, fetchPurchaseTender, fetchTenderById, getAuction, getDesignation, getRagistrationState, offlineAuctionPurchase, offlineTenderPurchase, partnerShipSubmit, proceedToBid, registerContractor, sendAadhaarOtp, submitEnvelopeA, submitEnvelopeB, uploadEnvelopeFile, userAuctionLogin, userLogin, verifyAadhaarOtp, verifyGstNumber, verifyPanHandler, viewAuctionDocumentDownload, viewAuctionDocuments, viewAuctionItemDocuments, fetchPurchaseAuctionPage } from './apis';
|
||||
import { contractorStageThreeSubmit, contractorStageTwoSubmit, fetchAuctionItem, fetchCityDropdown, fetchDashboard, fetchEmdCheck, fetchLiveAuction, fetchLiveTender, fetchMasterData, fetchPurchaseAuction, fetchPurchaseAuctionList, fetchPurchaseTender, fetchTenderById, getAuction, getDesignation, getRagistrationState, offlineAuctionPurchase, offlineTenderPurchase, partnerShipSubmit, proceedToBid, registerContractor, sendAadhaarOtp, submitEnvelopeA, submitEnvelopeB, uploadEnvelopeFile, userAuctionLogin, userLogin, verifyAadhaarOtp, verifyGstNumber, verifyPanHandler, viewAuctionDocumentDownload, viewAuctionDocuments, viewAuctionItemDocuments, fetchPurchaseAuctionPage, tenderDetails, fetchLiveTenderById } from './apis';
|
||||
|
||||
// User
|
||||
|
||||
@@ -36,18 +36,18 @@ export const useFetchPurchaseTender = () => {
|
||||
getNextPageParam: (lastPage, allPages) => {
|
||||
const { totalCount } = lastPage;
|
||||
|
||||
const totalPages = Math.ceil(totalCount / size);
|
||||
const totalPages = Math.ceil(totalCount / 10);
|
||||
const nextPage = allPages.length;
|
||||
return nextPage < totalPages ? nextPage : undefined;
|
||||
},
|
||||
|
||||
return page < totalPages ? page + 1 : undefined;
|
||||
},
|
||||
|
||||
select: (data) => ({
|
||||
...data,
|
||||
result: data.pages.flatMap(
|
||||
(page: any) => page?.data ?? []
|
||||
),
|
||||
}),
|
||||
});
|
||||
select: (data) => ({
|
||||
...data,
|
||||
result: data.pages.flatMap(
|
||||
(page: any) => page?.data ?? []
|
||||
),
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -298,3 +298,16 @@ export const useAucitonDocDownload = () => {
|
||||
|
||||
|
||||
|
||||
|
||||
export const useTenderDetails = () => {
|
||||
return useMutation({
|
||||
mutationFn: (tenderId) => tenderDetails(tenderId),
|
||||
});
|
||||
};
|
||||
|
||||
export const useFetchLiveTenderById = (tenderId:number) => {
|
||||
return useQuery({
|
||||
queryKey: ['fetchLiveTenderById', tenderId],
|
||||
queryFn: () => fetchLiveTenderById(tenderId)
|
||||
});
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import axios from "axios";
|
||||
import { AUCTION_DOCUMENT_DOWNLOAD, DASHBOARD_COUNT, EMD_CHECK, FORARD_AUCTION_ITEM, GET_AUCTION_DETAILS, GET_MY_AUCTION, GET_MY_TENDER, GET_TENDER_DETAILS, LIVE_AUCTION, LIVE_TENDER, LOG_IN_URL, PROCEED_PURCHASE_AUCTION, PROCEED_TO_BID, PURCHASE_TENDER_OFFLINE, TENDER_LOGIN, PURCHASE_AUCTION_LIST, PURCHASE_OFFLINE_AUCTION, REVERSE_AUCTION_ITEM, VIEW_AUCTION_DOCUMENTS, VIEW_AUCTION_ITEM_DETAIL, STAGETHREE, REJ_STAGE_TWO, GETCONTRACTORBYSTATE, GET_MASTER_DATA, ALL_DESIGNATION, GET_REGISTRATION_STATE, PARTNER_API_ENDPOINT, SAVE_CONTRACTOR_STAGE1, VERIFY_GST, VERIFY_ADHAR, SEND_ADHAROTP, SUBMIT_ENVELOPE_FILE_B, SUBMIT_ENVELOPE_FILE_A, SUBMIT_ENVELOPE_A, SUBMIT_ENVELOPE_B, VERIFY_ORGPAN, PURCHASE_AUCTION_PAGE } from "./ApiUrls";
|
||||
import { AUCTION_DOCUMENT_DOWNLOAD, DASHBOARD_COUNT, EMD_CHECK, FORARD_AUCTION_ITEM, GET_AUCTION_DETAILS, GET_MY_AUCTION, GET_MY_TENDER, GET_TENDER_DETAILS, LIVE_AUCTION, LIVE_TENDER, LOG_IN_URL, PROCEED_PURCHASE_AUCTION, PROCEED_TO_BID, PURCHASE_TENDER_OFFLINE, TENDER_LOGIN, PURCHASE_AUCTION_LIST, PURCHASE_OFFLINE_AUCTION, REVERSE_AUCTION_ITEM, VIEW_AUCTION_DOCUMENTS, VIEW_AUCTION_ITEM_DETAIL, STAGETHREE, REJ_STAGE_TWO, GETCONTRACTORBYSTATE, GET_MASTER_DATA, ALL_DESIGNATION, GET_REGISTRATION_STATE, PARTNER_API_ENDPOINT, SAVE_CONTRACTOR_STAGE1, VERIFY_GST, VERIFY_ADHAR, SEND_ADHAROTP, SUBMIT_ENVELOPE_FILE_B, SUBMIT_ENVELOPE_FILE_A, SUBMIT_ENVELOPE_A, SUBMIT_ENVELOPE_B, VERIFY_ORGPAN, PURCHASE_AUCTION_PAGE, GET_TENDER_BY_ID } from "./ApiUrls";
|
||||
import { apiCall } from "../../Axios/Axios";
|
||||
|
||||
// User
|
||||
@@ -341,4 +341,22 @@ export const uploadEnvelopeFile = async ({
|
||||
})
|
||||
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export const tenderDetails = async (tenderId:any ): Promise<any> => {
|
||||
const data = await apiCall<any>('get', `${GET_TENDER_DETAILS}${tenderId}`, );
|
||||
return data
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const fetchLiveTenderById = async (tenderId: number): Promise<any> => {
|
||||
const data = await apiCall<any>(
|
||||
'get',
|
||||
`${GET_TENDER_BY_ID}${tenderId}`
|
||||
);
|
||||
return data;
|
||||
};
|
||||
Reference in New Issue
Block a user