441 lines
14 KiB
JavaScript
441 lines
14 KiB
JavaScript
import React, { useState } from "react";
|
|
import { OPACITY_ON_LOAD } from "../../Layout/animations";
|
|
import {
|
|
Box,
|
|
Button,
|
|
HStack,
|
|
Input,
|
|
InputGroup,
|
|
InputRightAddon,
|
|
Textarea,
|
|
useDisclosure,
|
|
Image,
|
|
Icon,
|
|
VStack,
|
|
Text,
|
|
useToast,
|
|
} from "@chakra-ui/react";
|
|
import { FormControl, FormLabel, FormHelperText } from "@chakra-ui/react";
|
|
import { DeleteIcon, Search2Icon } from "@chakra-ui/icons";
|
|
import SelectInvestorModal from "./SelectInvestorModal";
|
|
import { Controller, useForm } from "react-hook-form"; // Import useForm
|
|
import { yupResolver } from "@hookform/resolvers/yup"; // Import resolver for Yup
|
|
import * as Yup from "yup"; // Import Yup for validation
|
|
import { motion } from "framer-motion"; // Import Framer Motion for animations
|
|
import { bytesToMB } from "../../Constants/Constants";
|
|
import { useCreateFawateerRequestMutation } from "../../Services/fawateer.maker.service";
|
|
import ToastBox from "../../Components/ToastBox";
|
|
import { useNavigate } from "react-router-dom";
|
|
import CurrencyInput from "../../Components/CurrencyInput";
|
|
|
|
// Validation schema using Yup
|
|
const validationSchema = Yup.object().shape({
|
|
investorName: Yup.string().required("Investor name is required"),
|
|
clientId: Yup.string().required("Client ID is required"),
|
|
transaction_date: Yup.date()
|
|
.required("Date is required")
|
|
.transform((value, originalValue) => {
|
|
return originalValue === "" ? null : value; // Convert empty strings to null
|
|
})
|
|
.typeError("Please enter a valid date")
|
|
.max(new Date(), "Date cannot be in the future"),
|
|
transaction_amount: Yup.number()
|
|
.required("Transaction amount is required")
|
|
.transform((value, originalValue) => (originalValue === "" ? null : value)) // Convert empty strings to null
|
|
.typeError("Transaction amount must be a number") // Custom error message if it's not a number
|
|
.positive("Transaction amount must be greater than zero"),
|
|
spportFile_path: Yup.mixed().required("Support file is required"),
|
|
makerComment: Yup.string() .max(200, "Approve Comment cannot be more than 50 characters"),
|
|
});
|
|
|
|
const CreateRequest = () => {
|
|
const toast = useToast();
|
|
const navigate = useNavigate();
|
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
|
const [selectedInvestor, setSelectorInvestor] = useState({});
|
|
const [filePreview, setFilePreview] = useState(null); // State for previewing the file
|
|
const [fileType, setFileType] = useState(null); // State to store file type for conditional rendering
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [id, setId] = useState(null);
|
|
|
|
// Initialize useForm with the resolver for Yup validation
|
|
const {
|
|
control,
|
|
register,
|
|
watch,
|
|
handleSubmit,
|
|
setValue,
|
|
reset,
|
|
formState: { errors },
|
|
} = useForm({
|
|
resolver: yupResolver(validationSchema),
|
|
});
|
|
|
|
const [creatFawaateerRequest] = useCreateFawateerRequestMutation();
|
|
|
|
const onSubmit = async (data) => {
|
|
console.log(data);
|
|
setIsLoading(true);
|
|
|
|
// Convert data to FormData
|
|
const formData = new FormData();
|
|
|
|
// Append each field from the data object to the FormData
|
|
Object.keys(data).forEach((key) => {
|
|
if (key === "spportFile_path" && data[key] instanceof FileList) {
|
|
// Append the first file from FileList (assuming single file input)
|
|
formData.append(key, data[key][0]); // Append the file
|
|
} else {
|
|
formData.append(key, data[key]); // Append other fields
|
|
}
|
|
});
|
|
|
|
try {
|
|
// Make the API call with formData
|
|
const res = await creatFawaateerRequest({ data: formData, id });
|
|
|
|
if (res?.error) {
|
|
toast({
|
|
render: () => (
|
|
<ToastBox status={"error"} message={res?.error?.data?.message} />
|
|
),
|
|
});
|
|
setIsLoading(false);
|
|
reset();
|
|
return;
|
|
} else if (res?.data) {
|
|
toast({
|
|
render: () => <ToastBox message={res?.data?.message} />,
|
|
});
|
|
setIsLoading(false);
|
|
navigate("/fawateer-history");
|
|
return;
|
|
} else {
|
|
toast({
|
|
render: () => (
|
|
<ToastBox status={"error"} message={"Something went wrong"} />
|
|
),
|
|
});
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
} catch (error) {
|
|
console.error("Error:", error);
|
|
toast({
|
|
render: () => (
|
|
<ToastBox status={"error"} message={"An error occurred"} />
|
|
),
|
|
});
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
};
|
|
|
|
// Handle file change and preview
|
|
const handleFileChange = (e) => {
|
|
const file = e.target.files[0];
|
|
console.log(file);
|
|
setValue("spportFile_path", file); // Set the file value in the form
|
|
setFileType(file); // Set the file type
|
|
|
|
if (file && file.type.startsWith("image/")) {
|
|
// If the file is an image, generate a preview
|
|
const reader = new FileReader();
|
|
reader.onload = () => {
|
|
setFilePreview(reader.result); // Set the image preview
|
|
};
|
|
reader.readAsDataURL(file);
|
|
} else {
|
|
setFilePreview(null); // Clear preview if the file is not an image
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Box {...OPACITY_ON_LOAD} overflowY={"scroll"} height={"100vh"} pb={38}>
|
|
<Box
|
|
display={"flex"}
|
|
justifyContent={"space-between"}
|
|
flexWrap={"wrap"}
|
|
alignItems={"center"}
|
|
mt={5}
|
|
px={4}
|
|
as="form"
|
|
onSubmit={handleSubmit(onSubmit)}
|
|
>
|
|
{/* Investor Name Field */}
|
|
<FormControl
|
|
isRequired
|
|
w={"49%"}
|
|
mb={2}
|
|
isInvalid={errors.investorName}
|
|
>
|
|
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
|
|
Investor name
|
|
</FormLabel>
|
|
<InputGroup size="sm">
|
|
<Input
|
|
bg={"#F5F8F6"}
|
|
focusBorderColor="forestGreen.300"
|
|
size={"sm"}
|
|
fontSize={"sm"}
|
|
rounded={"sm"}
|
|
type={"text"}
|
|
readOnly={true}
|
|
placeholder={"Investor name"}
|
|
{...register("investorName")}
|
|
_placeholder={{ fontSize: "sm" }}
|
|
/>
|
|
<InputRightAddon
|
|
gap={2}
|
|
color={"forestgreen.400"}
|
|
onClick={onOpen}
|
|
cursor={"pointer"}
|
|
fontWeight={600}
|
|
fontSize={"xs"}
|
|
>
|
|
<Search2Icon /> Search
|
|
</InputRightAddon>
|
|
</InputGroup>
|
|
<FormHelperText
|
|
fontSize={"xs"}
|
|
fontWeight={500}
|
|
style={{ color: "red" }}
|
|
>
|
|
{errors.investorName?.message}
|
|
</FormHelperText>
|
|
</FormControl>
|
|
|
|
{/* Client ID Field */}
|
|
<FormControl isRequired w={"49%"} mb={2} isInvalid={errors.clientId}>
|
|
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
|
|
Client Id
|
|
</FormLabel>
|
|
<Input
|
|
bg={"#F5F8F6"}
|
|
focusBorderColor="forestGreen.300"
|
|
size={"sm"}
|
|
fontSize={"sm"}
|
|
rounded={"sm"}
|
|
type={"text"}
|
|
readOnly={true}
|
|
placeholder={"Client ID"}
|
|
{...register("clientId")}
|
|
/>
|
|
<FormHelperText
|
|
fontSize={"xs"}
|
|
fontWeight={500}
|
|
style={{ color: "red" }}
|
|
>
|
|
{errors.clientId?.message}
|
|
</FormHelperText>
|
|
</FormControl>
|
|
|
|
{/* Date Field */}
|
|
<FormControl isRequired w={"49%"} mb={2} isInvalid={errors.date}>
|
|
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
|
|
Date
|
|
</FormLabel>
|
|
<Input
|
|
bg={"#F5F8F6"}
|
|
focusBorderColor="forestGreen.300"
|
|
size={"sm"}
|
|
fontSize={"sm"}
|
|
rounded={"sm"}
|
|
type={"date"}
|
|
max={new Date().toLocaleDateString("en-CA")} // Ensures max is in local timezone
|
|
{...register("transaction_date", {
|
|
setValueAs: (value) => {
|
|
// Convert date string to local timezone Date object
|
|
return value ? new Date(value) : undefined;
|
|
},
|
|
})}
|
|
/>
|
|
<FormHelperText
|
|
fontSize={"xs"}
|
|
fontWeight={500}
|
|
style={{ color: "red" }}
|
|
>
|
|
{errors.transaction_date?.message}
|
|
</FormHelperText>
|
|
</FormControl>
|
|
|
|
{/* Amount Field */}
|
|
<FormControl isRequired w={"49%"} mb={2} isInvalid={errors.amount}>
|
|
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
|
|
Amount (BHD)
|
|
</FormLabel>
|
|
<Controller
|
|
name="transaction_amount"
|
|
control={control}
|
|
render={({ field }) => (
|
|
<CurrencyInput
|
|
bg={"#F5F8F6"}
|
|
{...field}
|
|
textAlign={"right"}
|
|
fontSize={"sm"}
|
|
type="number"
|
|
size={"sm"}
|
|
/>
|
|
)}
|
|
/>
|
|
<FormHelperText
|
|
fontSize={"xs"}
|
|
fontWeight={500}
|
|
style={{ color: "red" }}
|
|
>
|
|
{errors.transaction_amount?.message}
|
|
</FormHelperText>
|
|
</FormControl>
|
|
|
|
{/* Support File Field with Preview */}
|
|
<FormControl
|
|
isRequired
|
|
w={"49%"}
|
|
mb={2}
|
|
isInvalid={errors.spportFile_path}
|
|
>
|
|
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
|
|
Support file
|
|
</FormLabel>
|
|
<Input
|
|
bg={"#F5F8F6"}
|
|
focusBorderColor="forestGreen.300"
|
|
size={"sm"}
|
|
fontSize={"sm"}
|
|
rounded={"sm"}
|
|
type={"file"}
|
|
className="form-control"
|
|
name="spportFile_path"
|
|
placeholder={"Support file"}
|
|
{...register("spportFile_path")}
|
|
// onChange={handleFileChange}
|
|
/>
|
|
<FormHelperText
|
|
fontSize={"xs"}
|
|
fontWeight={500}
|
|
style={{ color: "red" }}
|
|
>
|
|
{errors.spportFile_path?.message}
|
|
</FormHelperText>
|
|
|
|
{/* Animated Preview */}
|
|
{filePreview && fileType?.type.startsWith("image/") && (
|
|
<motion.div
|
|
initial={{ opacity: 0, scale: 0.9 }}
|
|
animate={{ opacity: 1, scale: 1 }}
|
|
transition={{ duration: 0.5 }}
|
|
style={{ marginTop: "10px" }}
|
|
>
|
|
<Box
|
|
position={"relative"}
|
|
display={"flex"}
|
|
alignContent={"flex-end"}
|
|
gap={3}
|
|
mt={2}
|
|
>
|
|
<Image
|
|
src={filePreview}
|
|
alt="File preview"
|
|
maxW={"150px"}
|
|
borderRadius="md"
|
|
boxShadow="md"
|
|
/>
|
|
<Icon
|
|
onClick={() => setFilePreview(null)}
|
|
className="link"
|
|
rounded={"md"}
|
|
color={"red.700"}
|
|
cursor={"pointer"}
|
|
p={1.5}
|
|
position={"absolute"}
|
|
top={0}
|
|
right={0}
|
|
as={DeleteIcon}
|
|
boxSize={7}
|
|
/>
|
|
<VStack justifyItems={"flex-end"} alignItems={"flex-start"}>
|
|
<Text as={"span"} color={"gray.600"} fontSize={"xs"}>
|
|
File Name:{" "}
|
|
<Text as={"span"} color={"GrayText"}>
|
|
{" "}
|
|
{fileType?.name}
|
|
</Text>
|
|
</Text>
|
|
<Text as={"span"} color={"gray.600"} fontSize={"xs"}>
|
|
File Size:{" "}
|
|
<Text as={"span"} color={"GrayText"}>
|
|
{" "}
|
|
{bytesToMB(fileType?.size)} Mb
|
|
</Text>
|
|
</Text>
|
|
<Text as={"span"} color={"gray.600"} fontSize={"xs"}>
|
|
File Type:{" "}
|
|
<Text as={"span"} color={"GrayText"}>
|
|
{" "}
|
|
{fileType?.type}
|
|
</Text>
|
|
</Text>
|
|
</VStack>
|
|
</Box>
|
|
</motion.div>
|
|
)}
|
|
</FormControl>
|
|
|
|
{/* Description Field */}
|
|
<FormControl w={"100%"} mb={2} isInvalid={errors.makerComment}>
|
|
<FormLabel textAlign={"left"} fontSize={"xs"} color={"gray.600"}>
|
|
Description
|
|
</FormLabel>
|
|
<Textarea
|
|
bg={"#F5F8F6"}
|
|
focusBorderColor="forestGreen.300"
|
|
size={"sm"}
|
|
fontSize={"sm"}
|
|
rounded={"sm"}
|
|
placeholder={"Description"}
|
|
{...register("makerComment")}
|
|
maxLength={200}
|
|
/>
|
|
<FormHelperText
|
|
fontSize={"xs"}
|
|
fontWeight={500}
|
|
style={{ color: "red" }}
|
|
>
|
|
{errors.makerComment?.message}
|
|
</FormHelperText>
|
|
<FormHelperText fontSize="xs" color="gray.500">
|
|
<Box as="span" me={1}>
|
|
Maximum length should be 200 characters. You have entered
|
|
</Box>
|
|
{watch("makerComment")?.length || 0} characters.
|
|
</FormHelperText>
|
|
</FormControl>
|
|
|
|
{/* Submit Button */}
|
|
<HStack mt={2} w={"100%"} justifyContent={"flex-end"}>
|
|
<Button
|
|
colorScheme="forestGreen"
|
|
size={"sm"}
|
|
rounded={"sm"}
|
|
fontSize={"xs"}
|
|
type="submit"
|
|
isLoading={isLoading}
|
|
>
|
|
Create request
|
|
</Button>
|
|
</HStack>
|
|
</Box>
|
|
|
|
<SelectInvestorModal
|
|
setId={setId}
|
|
setValue={setValue}
|
|
onClose={onClose}
|
|
isOpen={isOpen}
|
|
onOpen={onOpen}
|
|
/>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
export default CreateRequest;
|