| 1 | import NavbarRoditel from "../../../components/NavbarRoditel/NavbarRoditel";
|
|---|
| 2 | import { useState, useEffect } from "react";
|
|---|
| 3 | import { ethers } from "ethers";
|
|---|
| 4 | import { data } from "react-router-dom";
|
|---|
| 5 | import axios from "axios";
|
|---|
| 6 | import { useAuth } from "../../../contexts/AuthContext";
|
|---|
| 7 |
|
|---|
| 8 | const Roditel_PaymentAnnoucements = () => {
|
|---|
| 9 | const { walletAddress } = useAuth(); //go zimame wallet od AuthContext
|
|---|
| 10 | const [soopstenija, setSoopstenija] = useState([]);
|
|---|
| 11 | const [paymentLoading, setPaymentLoading] = useState(null);
|
|---|
| 12 | const [roditelId, setRoditelId] = useState(null);
|
|---|
| 13 | const [paymentStatus, setPaymentStatus] = useState(null);
|
|---|
| 14 | const [txPending, setTxPending] = useState(false);
|
|---|
| 15 | const [plateniSoopstenija, setPlateniSoopstenija] = useState([]);
|
|---|
| 16 |
|
|---|
| 17 | useEffect(() => {
|
|---|
| 18 | const fetchRoditelAndPlateniSoopstenija = async () => {
|
|---|
| 19 | try {
|
|---|
| 20 | const response = await fetch("http://localhost:8080/api/auth/me", {
|
|---|
| 21 | credentials: "include",
|
|---|
| 22 | });
|
|---|
| 23 | const data = await response.json();
|
|---|
| 24 | setRoditelId(data.id);
|
|---|
| 25 | const id = data.id;
|
|---|
| 26 | const plateniResponse = await axios.get(
|
|---|
| 27 | `http://localhost:8080/api/payments/parent/${id}`,
|
|---|
| 28 | { withCredentials: true }
|
|---|
| 29 | );
|
|---|
| 30 | setPlateniSoopstenija(plateniResponse.data);
|
|---|
| 31 | } catch (err) {
|
|---|
| 32 | console.error("Error fetching roditel ID:", err);
|
|---|
| 33 | }
|
|---|
| 34 | };
|
|---|
| 35 |
|
|---|
| 36 | fetchRoditelAndPlateniSoopstenija();
|
|---|
| 37 | }, []);
|
|---|
| 38 |
|
|---|
| 39 | const fetchSoopstenija = async () => {
|
|---|
| 40 | try {
|
|---|
| 41 | const response = await axios.get(
|
|---|
| 42 | "http://localhost:8080/api/paymentsSoopstenija/approved",
|
|---|
| 43 | { withCredentials: true }
|
|---|
| 44 | );
|
|---|
| 45 | setSoopstenija(response.data);
|
|---|
| 46 | } catch (error) {
|
|---|
| 47 | console.error("Error fetching soopstenija:", error);
|
|---|
| 48 | }
|
|---|
| 49 | };
|
|---|
| 50 | useEffect(() => {
|
|---|
| 51 | fetchSoopstenija();
|
|---|
| 52 | }, []);
|
|---|
| 53 |
|
|---|
| 54 | const handlePayment = async (soopstenieId, iznosETH) => {
|
|---|
| 55 | if (!walletAddress) {
|
|---|
| 56 | alert("Ве молиме прво поврзете го вашиот MetaMask wallet во Settings!");
|
|---|
| 57 | return;
|
|---|
| 58 | }
|
|---|
| 59 |
|
|---|
| 60 | if (!roditelId) {
|
|---|
| 61 | alert("Грешка: Не може да се идентификува родителот!");
|
|---|
| 62 | return;
|
|---|
| 63 | }
|
|---|
| 64 |
|
|---|
| 65 | setPaymentLoading(soopstenieId);
|
|---|
| 66 | setTxPending(true);
|
|---|
| 67 |
|
|---|
| 68 | try {
|
|---|
| 69 | // 1. Креирај плаќање
|
|---|
| 70 | const createResponse = await axios.post(
|
|---|
| 71 | "http://localhost:8080/api/payments/create",
|
|---|
| 72 | {
|
|---|
| 73 | soopstenieId: soopstenieId,
|
|---|
| 74 | roditelId: roditelId,
|
|---|
| 75 | },
|
|---|
| 76 | { withCredentials: true }
|
|---|
| 77 | );
|
|---|
| 78 |
|
|---|
| 79 | const payment = createResponse.data;
|
|---|
| 80 |
|
|---|
| 81 | const txHash = await sendTransaction(iznosETH);
|
|---|
| 82 |
|
|---|
| 83 | await axios.post(
|
|---|
| 84 | `http://localhost:8080/api/payments/${payment.id}/submit-tx`,
|
|---|
| 85 | {
|
|---|
| 86 | txHash: txHash,
|
|---|
| 87 | },
|
|---|
| 88 | { withCredentials: true }
|
|---|
| 89 | );
|
|---|
| 90 |
|
|---|
| 91 | alert(
|
|---|
| 92 | `Успешно плаќање!\nTx hash: ${txHash.substring(
|
|---|
| 93 | 0,
|
|---|
| 94 | 10
|
|---|
| 95 | )}...\nПроверете на Sepolia Etherscan`
|
|---|
| 96 | );
|
|---|
| 97 |
|
|---|
| 98 | fetchSoopstenija();
|
|---|
| 99 | } catch (error) {
|
|---|
| 100 | console.error("Грешка:", error);
|
|---|
| 101 | if (error.response?.data) {
|
|---|
| 102 | alert(error.response.data);
|
|---|
| 103 | } else if (error.message.includes("insufficient funds")) {
|
|---|
| 104 | alert(
|
|---|
| 105 | "Немате доволно SepoliaETH! Добијте од faucet: https://sepoliafaucet.com/"
|
|---|
| 106 | );
|
|---|
| 107 | } else {
|
|---|
| 108 | alert("Грешка при плаќање: " + error.message);
|
|---|
| 109 | }
|
|---|
| 110 | } finally {
|
|---|
| 111 | setTxPending(false);
|
|---|
| 112 | setPaymentLoading(null);
|
|---|
| 113 | }
|
|---|
| 114 | };
|
|---|
| 115 |
|
|---|
| 116 | const sendTransaction = async (iznosETH) => {
|
|---|
| 117 | if (!window.ethereum) {
|
|---|
| 118 | throw new Error("MetaMask не е инсталиран");
|
|---|
| 119 | }
|
|---|
| 120 |
|
|---|
| 121 | const provider = new ethers.BrowserProvider(window.ethereum);
|
|---|
| 122 | const signer = await provider.getSigner();
|
|---|
| 123 |
|
|---|
| 124 | const toAddress = "0x1ab6B15AfE81B57fF4a692fbb8C141A1858941e0";
|
|---|
| 125 |
|
|---|
| 126 | const amountInWei = ethers.parseEther(Number(iznosETH).toFixed(18));
|
|---|
| 127 | const tx = await signer.sendTransaction({
|
|---|
| 128 | to: toAddress,
|
|---|
| 129 | value: amountInWei,
|
|---|
| 130 | });
|
|---|
| 131 |
|
|---|
| 132 | console.log("Transaction sent:", tx.hash);
|
|---|
| 133 |
|
|---|
| 134 | setPaymentStatus("Испратена трансакција, ве молиме почекајте");
|
|---|
| 135 |
|
|---|
| 136 | await tx.wait(3);
|
|---|
| 137 |
|
|---|
| 138 | return tx.hash;
|
|---|
| 139 | };
|
|---|
| 140 |
|
|---|
| 141 | const isPlateno = (soopstenieId) => {
|
|---|
| 142 | return plateniSoopstenija.some((p) => p.soopstenieId === soopstenieId);
|
|---|
| 143 | };
|
|---|
| 144 | return (
|
|---|
| 145 | <>
|
|---|
| 146 | <NavbarRoditel />
|
|---|
| 147 | <div className="payment-announcements-container ">
|
|---|
| 148 | <h2 className="text-center">Соопштенија за плаќање</h2>
|
|---|
| 149 |
|
|---|
| 150 | {!walletAddress && (
|
|---|
| 151 | <div className="alert alert-warning w-50 m-5 p-2 " role="alert">
|
|---|
| 152 | Немате поврзано <b>Metamusk Wallet!</b> Ве молиме поврзете го вашиот
|
|---|
| 153 | <b>MetaMask wallet</b> во опции!
|
|---|
| 154 | </div>
|
|---|
| 155 | )}
|
|---|
| 156 |
|
|---|
| 157 | {soopstenija.length === 0 ? (
|
|---|
| 158 | <p>Нема активни соопштенија за плаќање.</p>
|
|---|
| 159 | ) : (
|
|---|
| 160 | <div className="soopstenija-list">
|
|---|
| 161 | {soopstenija.map((soopstenie) => (
|
|---|
| 162 | <div key={soopstenie.id} className="card w-50 m-5">
|
|---|
| 163 | <div className="card-header">
|
|---|
| 164 | <h3>Наслов {soopstenie.naslov}</h3>
|
|---|
| 165 | </div>
|
|---|
| 166 | <div className="card-body">
|
|---|
| 167 | <p className="card-text">{soopstenie.sodrzina}</p>
|
|---|
| 168 | <p>
|
|---|
| 169 | <b>Износ: </b>
|
|---|
| 170 | {Number(soopstenie.iznos).toFixed(18)} ETH
|
|---|
| 171 | </p>
|
|---|
| 172 | <p>soopstenie id: {soopstenie.id}</p>
|
|---|
| 173 | <p>
|
|---|
| 174 | Рок:{" "}
|
|---|
| 175 | {new Date(soopstenie.rokZaPlakjanje).toLocaleDateString()}
|
|---|
| 176 | </p>
|
|---|
| 177 | <p>{soopstenie.plateno}</p>
|
|---|
| 178 | {paymentLoading === soopstenie.id || !walletAddress ? (
|
|---|
| 179 | <p>Немате поврзано wallet! за да платете!</p>
|
|---|
| 180 | ) : isPlateno(soopstenie.id) ? (
|
|---|
| 181 | <span className="text-success">
|
|---|
| 182 | Оваа соопштение е веќе платено!
|
|---|
| 183 | </span>
|
|---|
| 184 | ) : (
|
|---|
| 185 | <button
|
|---|
| 186 | onClick={() =>
|
|---|
| 187 | handlePayment(soopstenie.id, soopstenie.iznos)
|
|---|
| 188 | }
|
|---|
| 189 | >
|
|---|
| 190 | Плати
|
|---|
| 191 | </button>
|
|---|
| 192 | )}
|
|---|
| 193 | </div>
|
|---|
| 194 | </div>
|
|---|
| 195 | ))}
|
|---|
| 196 | </div>
|
|---|
| 197 | )}
|
|---|
| 198 | </div>
|
|---|
| 199 | {txPending && (
|
|---|
| 200 | <div
|
|---|
| 201 | style={{
|
|---|
| 202 | position: "fixed",
|
|---|
| 203 | inset: 0,
|
|---|
| 204 | background: "rgba(0,0,0,0.5)",
|
|---|
| 205 | display: "flex",
|
|---|
| 206 | alignItems: "center",
|
|---|
| 207 | justifyContent: "center",
|
|---|
| 208 | zIndex: 1000,
|
|---|
| 209 | }}
|
|---|
| 210 | >
|
|---|
| 211 | <div
|
|---|
| 212 | style={{
|
|---|
| 213 | background: "#fff",
|
|---|
| 214 | borderRadius: 16,
|
|---|
| 215 | padding: "2rem 2.5rem",
|
|---|
| 216 | textAlign: "center",
|
|---|
| 217 | maxWidth: 320,
|
|---|
| 218 | }}
|
|---|
| 219 | >
|
|---|
| 220 | <div style={{ fontSize: 32, marginBottom: 12 }}></div>
|
|---|
| 221 | <h3 style={{ margin: "0 0 8px" }}>Transaction in progress</h3>
|
|---|
| 222 | <p style={{ margin: 0, color: "#666", fontSize: 14 }}>
|
|---|
| 223 | Please don't close this window
|
|---|
| 224 | </p>
|
|---|
| 225 | </div>
|
|---|
| 226 | </div>
|
|---|
| 227 | )}
|
|---|
| 228 | </>
|
|---|
| 229 | );
|
|---|
| 230 | };
|
|---|
| 231 |
|
|---|
| 232 | export default Roditel_PaymentAnnoucements;
|
|---|