diff --git a/jsconfig.json b/jsconfig.json index d9112b4..b51472a 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,12 +1,27 @@ +// javascript for production { - "compilerOptions": { - "paths": { - "@/*": [ - "./src/*" - ], - "^/*": [ - "./public/*" - ] + "compilerOptions": { + "paths": { + "@/*": ["./src/*"], + "^/*": ["./public/*"] + } } - } } + +// typescript for local development +// { +// "compilerOptions": { +// "jsx": "react-jsx", +// "noUnusedLocals": true, +// "noImplicitAny": false, +// "checkJs": true, +// "allowJs": true, +// "strict": true, +// "baseUrl": ".", +// "paths": { +// "@/*": ["./src/*"], +// "^/*": ["./public/*"] +// } +// }, +// "exclude": ["node_modules"] +// } diff --git a/src/components/dashboard/inquiryPrivacy/city-admin/ShowPrimaryArea/index.jsx b/src/components/dashboard/inquiryPrivacy/city-admin/ShowPrimaryArea/index.jsx index 98e24d0..76c0980 100644 --- a/src/components/dashboard/inquiryPrivacy/city-admin/ShowPrimaryArea/index.jsx +++ b/src/components/dashboard/inquiryPrivacy/city-admin/ShowPrimaryArea/index.jsx @@ -1,18 +1,8 @@ -const { - Button, - IconButton, - Tooltip, - Box, - Dialog, - DialogTitle, - Typography, - DialogContent, - DialogActions, -} = require("@mui/material"); -import LayersIcon from "@mui/icons-material/Layers"; -import CloseIcon from "@mui/icons-material/Close"; -import { useMemo, useState } from "react"; +const { IconButton, Tooltip, Box, Dialog, DialogTitle, Typography, DialogContent } = require("@mui/material"); import MapLayer from "@/core/components/MapLayer"; +import CloseIcon from "@mui/icons-material/Close"; +import LayersIcon from "@mui/icons-material/Layers"; +import { useMemo, useState } from "react"; import ShowBound from "./ShowBound"; const ShowPrimaryArea = ({ primaryArea }) => { diff --git a/src/components/dashboard/inquiryPrivacy/privacy-office/RowActions/Forms/ComputePaymentForm.jsx b/src/components/dashboard/inquiryPrivacy/privacy-office/RowActions/Forms/ComputePaymentForm.jsx index b49c61a..01d3a5d 100644 --- a/src/components/dashboard/inquiryPrivacy/privacy-office/RowActions/Forms/ComputePaymentForm.jsx +++ b/src/components/dashboard/inquiryPrivacy/privacy-office/RowActions/Forms/ComputePaymentForm.jsx @@ -1,47 +1,92 @@ -import { Box, Button, DialogActions, FormControl, InputLabel, MenuItem, Select, TextField } from "@mui/material"; +import { calculatePolygonMetrics } from "@/core/utils/geoCalculations"; +import { PRIVACY_ADMIN_ACTION_API } from "@/core/utils/routes"; +import { safeParsePolygon } from "@/core/utils/utils"; +import useRequest from "@/lib/hooks/useRequest"; +import { + Box, + Button, + DialogActions, + FormControl, + FormHelperText, + InputLabel, + MenuItem, + Select, + TextField, +} from "@mui/material"; import { useEffect, useState } from "react"; import { Controller, useForm, useWatch } from "react-hook-form"; -export default function ComputePaymentForm({ mutate, onClose }) { +const formatNumber = (num) => { + if (!num) return ""; + return Number(num).toLocaleString("en-US"); +}; + +const price_fee = 1; // TODO: Replace with real price_fee +export default function ComputePaymentForm({ mutate, onClose, rowData }) { const [isSubmitting, setIsSubmitting] = useState(false); + const request = useRequest({ notificationSuccess: true, notificationFailed: true }); - const price_fee = 1; // TODO: Replace with value from .env + const coordsArea = safeParsePolygon(rowData.final_area); + const coordsPlan = safeParsePolygon(rowData.final_plan); - const { control, handleSubmit, setValue } = useForm({ + const areaMetrics = coordsArea ? calculatePolygonMetrics(coordsArea) : { area: 0 }; + const planMetrics = coordsPlan ? calculatePolygonMetrics(coordsPlan) : { area: 0 }; + + const calculatedArea = Number(areaMetrics.area.toFixed(2) || 0); + const calculatedPlan = Number(planMetrics.area.toFixed(2) || 0); + + const { + control, + handleSubmit, + setValue, + formState: { errors }, + } = useForm({ defaultValues: { expert_description: "", traffic: "", road_type: "", position: "", - final_area_space: "", + final_area_space: calculatedArea, // عرصه + final_plan_space: calculatedPlan, // عیان payment_amount: "", }, }); - // Watch dependent fields for recalculation - const { traffic, road_type, position, final_area_space } = useWatch({ + const { traffic, road_type, position } = useWatch({ control, }); useEffect(() => { + const hasGeometry = calculatedArea > 0 || calculatedPlan > 0; + const t = parseFloat(traffic || ""); const r = parseFloat(road_type || ""); const p = parseFloat(position || ""); - const area = parseFloat(final_area_space || ""); - if (t && r && p && area) { - const result = t * r * p * area * price_fee; - setValue("payment_amount", result.toString()); + const inputsValid = hasGeometry && !isNaN(t) && t > 0 && !isNaN(r) && r > 0 && !isNaN(p) && p > 0; + + if (inputsValid) { + const resultArea = t * r * p * calculatedArea * price_fee; + const resultPlan = t * r * p * calculatedPlan * price_fee; + + setValue("payment_amount", Math.floor(resultArea + resultPlan).toString()); } else { setValue("payment_amount", ""); } - }, [traffic, road_type, position, final_area_space, price_fee, setValue]); + }, [traffic, road_type, position, setValue, calculatedArea, calculatedPlan]); const onSubmit = (data) => { - console.log(data); setIsSubmitting(true); - mutate(); - onClose(); + + try { + request(`${PRIVACY_ADMIN_ACTION_API}/computing_payment/${rowData.id}`, "post", { + data, + }); + } catch (error) { + console.error("Error submitting payment:", error); + } finally { + setIsSubmitting(false); + } }; return ( @@ -50,14 +95,18 @@ export default function ComputePaymentForm({ mutate, onClose }) { ( ترافیک - سبک متوسط سنگین + {errors.traffic && ( + {errors.traffic.message} + )} )} /> @@ -65,39 +114,66 @@ export default function ComputePaymentForm({ mutate, onClose }) { ( نوع راه - فرعی و راه‌آهن اصلی و بزرگراه آزادراه + {errors.road_type && ( + {errors.road_type.message} + )} )} /> - - ( - - موقعیت اقتصادی - - - )} - /> + ( + + موقعیت اقتصادی + + {errors.position && ( + {errors.position.message} + )} + + )} + /> + ( - + + )} + /> + ( + )} /> @@ -105,15 +181,35 @@ export default function ComputePaymentForm({ mutate, onClose }) { ( - - )} + render={({ field }) => { + const { value } = field; + + return ( + + ); + }} /> } + rules={{ required: "توضیحات ناظر اجباری است" }} + render={({ field }) => ( + + )} /> diff --git a/src/components/dashboard/inquiryPrivacy/privacy-office/RowActions/RenderActionForm.jsx b/src/components/dashboard/inquiryPrivacy/privacy-office/RowActions/RenderActionForm.jsx index 51c465a..846cc1a 100644 --- a/src/components/dashboard/inquiryPrivacy/privacy-office/RowActions/RenderActionForm.jsx +++ b/src/components/dashboard/inquiryPrivacy/privacy-office/RowActions/RenderActionForm.jsx @@ -22,7 +22,7 @@ export default function RenderActionForm({ type, rowData, mutate, onClose }) { case "confirm_request_5": return ; case "confirm_request_13": - return ; + return ; default: return <>; } diff --git a/src/core/utils/utils.js b/src/core/utils/utils.js index eec2824..1198244 100644 --- a/src/core/utils/utils.js +++ b/src/core/utils/utils.js @@ -148,3 +148,18 @@ export const getClosedPolygonCoordinates = (polygon) => { return coordinates; }; + +export function safeParsePolygon(polygonJson) { + if (!polygonJson) return null; + + try { + const parsed = JSON.parse(polygonJson); + if (parsed && parsed.coordinates) { + return parsed.coordinates; + } + return null; + } catch (err) { + console.error("Invalid polygon JSON:", err); + return null; + } +}