Untitled
unknown
plain_text
12 days ago
19 kB
4
Indexable
import { FC, useState, useEffect } from "react"; import { useSelector } from "react-redux"; import { FaEdit } from "react-icons/fa"; import { Col, Row, Form, FormGroup, Label, Input, InputGroup, InputGroupText, Button, } from "reactstrap"; import { RootState } from "@/Redux/Store"; interface LivingExpensesTabContentsProps { updateField: (field: string, value: any) => void; } const LivingExpensesTabContents: FC<LivingExpensesTabContentsProps> = ({ updateField, }) => { const budgetPlannerData = useSelector( (state: RootState) => state.budgetPlanner ?? { current_living_cost: {}, post_living_cost: {}, current_insurance: {}, post_insurance: {}, } ); const [visibleNotes, setVisibleNotes] = useState<{ [key: string]: boolean }>( {} ); const [currentValues, setCurrentValues] = useState<Record<string, string>>( {} ); const [postValues, setPostValues] = useState<Record<string, string>>({}); const livingCostFieldMappings = { Electricity: "electricity", Gas: "gas", Water: "water", "Landline/Mobile Phones": "landline_mobile_phone", "TV Licence": "tv_license", "Council Tax": "council_tax", "Ground Rent & Service Charges": "ground_rent_service_charges", "Buildings & Contents": "buildings_contents", "Mortgage Payment Protection": "mortgage_payment_protection", Endowment: "endowment", "Pension Contribution": "pension_contribution", Childcare: "childcare", Maintenance: "maintenance", Food: "food", "Car Maintenance": "car_maintenance", Fuel: "fuel", "Public Transport": "public_transport", "TV Broadband": "tv_broadband", "Recreation/Holidays": "recreation_holidays", Clothing: "clothing", "Medical Expenses": "medical_expenses", Education: "education", "Other Living Costs": "other_living_costs", }; const insuranceFieldMappings = { "Motor Insurance": "motor_insurance", "Health Insurance": "health_insurance", "Payment Protection": "payment_protection", "Life Insurance": "life_insurance", "Dental Insurance": "dental_insurance", "Other Insurance": "other_insurance", }; // Initialize local state with Redux data useEffect(() => { if (!budgetPlannerData) return; const initialCurrentValues: Record<string, string> = {}; const initialPostValues: Record<string, string> = {}; // Current Living Costs Object.entries(livingCostFieldMappings).forEach(([field, key]) => { const value = budgetPlannerData?.current_living_cost?.[ key as keyof typeof budgetPlannerData.current_living_cost ] ?? 0; initialCurrentValues[ `CurrentBudgetPlanner.${field.replace(/[\s/&]/g, "")}` ] = value !== 0 ? String(value) : ""; }); // Post Living Costs Object.entries(livingCostFieldMappings).forEach(([field, key]) => { const value = budgetPlannerData?.post_living_cost?.[ key as keyof typeof budgetPlannerData.post_living_cost ] ?? 0; initialPostValues[ `PostCompletionBudgetPlanner.${field.replace(/[\s/&]/g, "")}` ] = value !== 0 ? String(value) : ""; }); // Current Insurance Object.entries(insuranceFieldMappings).forEach(([field, key]) => { const value = budgetPlannerData?.current_insurance?.[ key as keyof typeof budgetPlannerData.current_insurance ] ?? 0; initialCurrentValues[ `CurrentBudgetPlanner.${field.replace(/[\s/&]/g, "")}` ] = value !== 0 ? String(value) : ""; }); // Post Insurance Object.entries(insuranceFieldMappings).forEach(([field, key]) => { const value = budgetPlannerData?.post_insurance?.[ key as keyof typeof budgetPlannerData.post_insurance ] ?? 0; initialPostValues[ `PostCompletionBudgetPlanner.${field.replace(/[\s/&]/g, "")}` ] = value !== 0 ? String(value) : ""; }); setCurrentValues((prev) => ({ ...prev, ...initialCurrentValues })); setPostValues((prev) => ({ ...prev, ...initialPostValues })); }, [budgetPlannerData]); // Update parent modal with current values useEffect(() => { const formatValues = ( values: Record<string, string>, mappings: Record<string, string>, section: string ) => { const formatted = Object.entries(values) .filter( ([key]) => key.startsWith("CurrentBudgetPlanner") && !key.includes("_Notes") ) .reduce((acc, [key, value]) => { const fieldName = key.split(".")[1]; const reduxFieldName = mappings[fieldName as keyof typeof mappings]; if (reduxFieldName) { acc[reduxFieldName] = value === "" ? 0 : parseFloat(value); } return acc; }, {} as Record<string, number | 0>); formatted.total_living_expenses = parseFloat( calculateTotal(values, Object.keys(mappings), "CurrentBudgetPlanner") ) || 0; updateField(section, formatted); }; formatValues(currentValues, livingCostFieldMappings, "current_living_cost"); formatValues(currentValues, insuranceFieldMappings, "current_insurance"); }, [currentValues, updateField]); // Update parent modal with post values useEffect(() => { const formatValues = ( values: Record<string, string>, mappings: Record<string, string>, section: string ) => { const formatted = Object.entries(values) .filter( ([key]) => key.startsWith("PostCompletionBudgetPlanner") && !key.includes("_Notes") ) .reduce((acc, [key, value]) => { const fieldName = key.split(".")[1]; const reduxFieldName = mappings[fieldName as keyof typeof mappings]; if (reduxFieldName) { acc[reduxFieldName] = value === "" ? 0 : parseFloat(value); } return acc; }, {} as Record<string, number | 0>); formatted.total_living_expenses = parseFloat( calculateTotal( values, Object.keys(mappings), "PostCompletionBudgetPlanner" ) ) || 0; updateField(section, formatted); }; formatValues(postValues, livingCostFieldMappings, "post_living_cost"); formatValues(postValues, insuranceFieldMappings, "post_insurance"); }, [postValues, updateField]); const toggleNotes = (event: React.MouseEvent, noteId: string) => { event.preventDefault(); setVisibleNotes((prev) => ({ ...prev, [noteId]: !prev[noteId] })); }; const renderFields = ( prefix: string, fields: string[], mappings: Record<string, string> ) => ( <Form> {fields.map((field) => { const id = field.replace(/[\s/&]/g, ""); const fieldName = `${prefix}.${id}`; const reduxFieldName = mappings[field]; return ( <div key={id}> <FormGroup row className="mb-2"> <Label for={`${prefix}_${reduxFieldName}`} sm={6} style={{ fontSize: "0.9rem" }} > {field} </Label> <Col sm={6}> <InputGroup> <InputGroupText>£</InputGroupText> <Input type="number" name={fieldName} id={`${prefix}_${reduxFieldName}`} className="numeric-decimal living-cost" placeholder="0.00" step="0.01" value={ prefix === "CurrentBudgetPlanner" ? currentValues[fieldName] || "" : postValues[fieldName] || "" } onChange={(e) => { const newValues = prefix === "CurrentBudgetPlanner" ? { ...currentValues, [fieldName]: e.target.value } : { ...postValues, [fieldName]: e.target.value }; prefix === "CurrentBudgetPlanner" ? setCurrentValues(newValues) : setPostValues(newValues); }} /> <InputGroupText className="penNoteIcon_Holder" onClick={(e) => toggleNotes( e, `${ prefix === "CurrentBudgetPlanner" ? "" : "Post_" }${reduxFieldName}_Notes` ) } style={{ cursor: "pointer" }} > <FaEdit /> </InputGroupText> </InputGroup> </Col> </FormGroup> <FormGroup className={`${reduxFieldName}_Notes_Holder mb-2`} style={{ display: visibleNotes[ `${ prefix === "CurrentBudgetPlanner" ? "" : "Post_" }${reduxFieldName}_Notes` ] ? "block" : "none", }} > <Label for={`${prefix}_${reduxFieldName}_Notes`} style={{ fontSize: "0.9rem" }} > Notes </Label> <Input type="textarea" name={`${prefix}.${reduxFieldName}_Notes`} id={`${prefix}_${reduxFieldName}_Notes`} className="textAreaRestrictions form-control" value={ prefix === "CurrentBudgetPlanner" ? currentValues[`${prefix}.${reduxFieldName}_Notes`] || "" : postValues[`${prefix}.${reduxFieldName}_Notes`] || "" } onChange={(e) => { const newValues = prefix === "CurrentBudgetPlanner" ? { ...currentValues, [`${prefix}.${reduxFieldName}_Notes`]: e.target.value, } : { ...postValues, [`${prefix}.${reduxFieldName}_Notes`]: e.target.value, }; prefix === "CurrentBudgetPlanner" ? setCurrentValues(newValues) : setPostValues(newValues); }} /> </FormGroup> </div> ); })} </Form> ); const calculateTotal = ( values: Record<string, string>, fields: string[], prefix: string ) => { return fields .reduce((sum, field) => { const fieldName = `${prefix}.${field.replace(/[\s/&]/g, "")}`; return sum + (parseFloat(values[fieldName]) || 0); }, 0) .toFixed(2); }; const handleCopyFromCurrent = () => { const newPostValues: Record<string, string> = {}; Object.keys(currentValues).forEach((key) => { const newKey = key.replace( "CurrentBudgetPlanner", "PostCompletionBudgetPlanner" ); newPostValues[newKey] = currentValues[key]; }); setPostValues(newPostValues); }; const renderSection = ( title: string, prefix: string, fields?: string[], mappings?: Record<string, string>, isTotal?: boolean ) => ( <div className="col-md-6"> {!isTotal && <h4 className="text-center mb-3">{title}</h4>} <div className={`border rounded-3 shadow-sm ${ isTotal ? "no-padding-vr no-border" : "" }`} > {!isTotal && ( <div className={`bg-light border-bottom p-3 ${ title === "Post Completion" ? "d-flex justify-content-between align-items-center" : "" }`} > <span className="fw-bold text-primary"> {fields && Object.keys(insuranceFieldMappings).includes(fields[0]) ? "Insurances" : "Living Costs"} </span> {title === "Post Completion" && ( <Button color="primary" size="sm" onClick={handleCopyFromCurrent}> Copy from Current </Button> )} </div> )} <div className={`p-3 ${isTotal ? "no-padding-vr no-border" : ""}`}> {isTotal ? ( <FormGroup row className="mb-2"> <Label for={`${prefix}_TotalHome`} sm={6} style={{ fontSize: "0.9rem" }} > Total Living Expenses </Label> <Col sm={6}> <InputGroup> <InputGroupText>£</InputGroupText> <Input type="number" name={`${prefix}.TotalHome`} id={`${prefix}_TotalHome`} className="numeric-decimal fw-bold" readOnly placeholder="0.00" step="0.01" value={ prefix === "CurrentBudgetPlanner" ? ( parseFloat( calculateTotal( currentValues, Object.keys(livingCostFieldMappings), prefix ) ) + parseFloat( calculateTotal( currentValues, Object.keys(insuranceFieldMappings), prefix ) ) ).toFixed(2) : ( parseFloat( calculateTotal( postValues, Object.keys(livingCostFieldMappings), prefix ) ) + parseFloat( calculateTotal( postValues, Object.keys(insuranceFieldMappings), prefix ) ) ).toFixed(2) } /> <InputGroupText className="penNoteIcon_Holder" onClick={(e) => toggleNotes( e, `${ prefix === "CurrentBudgetPlanner" ? "" : "Post_" }TotalHome_Notes` ) } style={{ cursor: "pointer" }} > <FaEdit /> </InputGroupText> </InputGroup> </Col> </FormGroup> ) : ( renderFields(prefix, fields!, mappings!) )} {isTotal && ( <FormGroup className="TotalHome_Notes_Holder mb-2" style={{ display: visibleNotes[ `${ prefix === "CurrentBudgetPlanner" ? "" : "Post_" }TotalHome_Notes` ] ? "block" : "none", }} > <Label for={`${prefix}_TotalHome_Notes`} style={{ fontSize: "0.9rem" }} > Notes </Label> <Input type="textarea" name={`${prefix}.TotalHome_Notes`} id={`${prefix}_TotalHome_Notes`} className="textAreaRestrictions form-control" value={ prefix === "CurrentBudgetPlanner" ? currentValues[`${prefix}.TotalHome_Notes`] || "" : postValues[`${prefix}.TotalHome_Notes`] || "" } onChange={(e) => { const newValues = prefix === "CurrentBudgetPlanner" ? { ...currentValues, [`${prefix}.TotalHome_Notes`]: e.target.value, } : { ...postValues, [`${prefix}.TotalHome_Notes`]: e.target.value, }; prefix === "CurrentBudgetPlanner" ? setCurrentValues(newValues) : setPostValues(newValues); }} /> </FormGroup> )} </div> </div> </div> ); return ( <div> <p> <small> Please enter all monthly living costs accurately. Convert non-monthly expenses: multiply weekly by 4.3, divide quarterly by 3, annual by 12. </small> </p> <Row> {renderSection( "Current", "CurrentBudgetPlanner", Object.keys(livingCostFieldMappings), livingCostFieldMappings )} {renderSection( "Post Completion", "PostCompletionBudgetPlanner", Object.keys(livingCostFieldMappings), livingCostFieldMappings )} </Row> <Row className="mt-4"> {renderSection( "Current", "CurrentBudgetPlanner", Object.keys(insuranceFieldMappings), insuranceFieldMappings )} {renderSection( "Post Completion", "PostCompletionBudgetPlanner", Object.keys(insuranceFieldMappings), insuranceFieldMappings )} </Row> <Row className="mt-4"> {renderSection("", "CurrentBudgetPlanner", [], {}, true)} {renderSection("", "PostCompletionBudgetPlanner", [], {}, true)} </Row> </div> ); }; export default LivingExpensesTabContents;
Editor is loading...
Leave a Comment