diff --git a/.env b/.env deleted file mode 100644 index 2e0184271..000000000 --- a/.env +++ /dev/null @@ -1,9 +0,0 @@ -DB_URL="mongodb://localhost:27017/TrustBridge" -JWT_SECRET="NetDpVFFq8H8tYtWT3bKjKzgTmsXPZ7mylWw/Z4t2PKC09bhJuL9aud0u4GPuGBojCDZ/XHrpHDBmPokRoTA==" -FRONTEND_URL=http://localhost:5173 -REDIS_URL=redis://localhost:6379 -VITE_BACKEND_URL=http://localhost:5000 -GOOGLE_CLIENT_ID="1087411543598-b2nh98usn3o29fpjn3s6mfljhkoo65s5.apps.googleusercontent.com" -GOOGLE_CLIENT_SECRET="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" -GOOGLE_CALLBACK_URL="http://localhost:5000/auth/google/callback" -TWO_FACTOR_ISSUER=TrustBridge \ No newline at end of file diff --git a/dev-dist/sw.js b/dev-dist/sw.js index 872a740bd..fdb6ef6b4 100644 --- a/dev-dist/sw.js +++ b/dev-dist/sw.js @@ -82,7 +82,7 @@ define(['./workbox-d70286d7'], (function (workbox) { 'use strict'; "revision": "3ca0b8505b4bec776b69afdba2768812" }, { "url": "index.html", - "revision": "0.kh39an3nq1" + "revision": "0.8b72d1pu7a8" }], {}); workbox.cleanupOutdatedCaches(); workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { diff --git a/package-lock.json b/package-lock.json index f5882ffd0..6911e8e97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,8 @@ "dependencies": { "@botpress/webchat": "^4.0.4", "@hookform/resolvers": "^5.2.2", + "@stripe/react-stripe-js": "^5.4.1", + "@stripe/stripe-js": "^8.6.1", "agora-rtc-sdk-ng": "^4.24.0", "axios": "^1.13.2", "date-fns": "^3.3.1", @@ -26,6 +28,7 @@ "recharts": "^3.5.0", "socket.io-client": "^4.8.1", "sonner": "^2.0.7", + "stripe": "^20.1.2", "yup": "^1.7.1" }, "devDependencies": { @@ -3669,6 +3672,30 @@ "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", "license": "MIT" }, + "node_modules/@stripe/react-stripe-js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-5.4.1.tgz", + "integrity": "sha512-ipeYcAHa4EPmjwfv0lFE+YDVkOQ0TMKkFWamW+BqmnSkEln/hO8rmxGPPWcd9WjqABx6Ro8Xg4pAS7evCcR9cw==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "@stripe/stripe-js": ">=8.0.0 <9.0.0", + "react": ">=16.8.0 <20.0.0", + "react-dom": ">=16.8.0 <20.0.0" + } + }, + "node_modules/@stripe/stripe-js": { + "version": "8.6.1", + "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-8.6.1.tgz", + "integrity": "sha512-UJ05U2062XDgydbUcETH1AoRQLNhigQ2KmDn1BG8sC3xfzu6JKg95Qt6YozdzFpxl1Npii/02m2LEWFt1RYjVA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12.16" + } + }, "node_modules/@surma/rollup-plugin-off-main-thread": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", @@ -3900,7 +3927,7 @@ "version": "24.3.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.1.tgz", "integrity": "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==", - "dev": true, + "devOptional": true, "license": "MIT", "peer": true, "dependencies": { @@ -9155,9 +9182,9 @@ } }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -10564,6 +10591,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stripe": { + "version": "20.1.2", + "resolved": "https://registry.npmjs.org/stripe/-/stripe-20.1.2.tgz", + "integrity": "sha512-qU+lQRRJnTxmyvglYBPE24/IepncmywsAg0GDTsTdP2pb+3e3RdREHJZjKgqCmv0phPxN/nmgNPnIPPH8w0P4A==", + "license": "MIT", + "dependencies": { + "qs": "^6.14.1" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@types/node": ">=16" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, "node_modules/style-to-js": { "version": "1.1.21", "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", @@ -11108,7 +11155,7 @@ "version": "7.10.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { diff --git a/package.json b/package.json index 1c9adc1ed..01fc5e340 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ "dependencies": { "@botpress/webchat": "^4.0.4", "@hookform/resolvers": "^5.2.2", + "@stripe/react-stripe-js": "^5.4.1", + "@stripe/stripe-js": "^8.6.1", "agora-rtc-sdk-ng": "^4.24.0", "axios": "^1.13.2", "date-fns": "^3.3.1", @@ -28,6 +30,7 @@ "recharts": "^3.5.0", "socket.io-client": "^4.8.1", "sonner": "^2.0.7", + "stripe": "^20.1.2", "yup": "^1.7.1" }, "devDependencies": { diff --git a/src/App.tsx b/src/App.tsx index 549bc00e8..b563bb75a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,6 +17,8 @@ import { RegisterPage } from "./pages/auth/RegisterPage"; // Dashboard Pages import { EntrepreneurDashboard } from "./pages/dashboard/EntrepreneurDashboard"; +import { EntrepreneurRequests } from "./pages/dashboard/EntrepreneurRequests"; +import { ManageTeam } from "./pages/dashboard/ManageTeam"; import { InvestorDashboard } from "./pages/dashboard/InvestorDashboard"; // Profile Pages @@ -41,7 +43,6 @@ import { AudioCall } from "./pages/webRTC/AudioCall"; import { Toaster } from "react-hot-toast"; import { HomePage } from "./pages/home/HomePage"; import { Supporters } from "./pages/admin/supporters"; -import { FlaggedAccounts } from "./pages/admin/flaggedAccounts"; import { Users } from "./pages/admin/Users"; import { Campaigns } from "./pages/admin/Campaigns"; import { DealsPage } from "./pages/deals/DealsPage"; @@ -67,6 +68,8 @@ import { CommunityGuidelines } from "./pages/legal/CommunityGuidelines"; import SendMassNotification from "./pages/admin/SendMassNotification"; import { DashboardCampaigns } from "./pages/dashCamp/DashboardCampaigns"; import { DashboardCampaignDetail } from "./pages/dashCamp/DashboardCampaignDetail"; +import { AdminDealsPage } from "./pages/admin/AdminDealsPage"; +import { AdminDealPayments } from "./pages/admin/AdminDealPayments"; function App() { return ( @@ -91,6 +94,14 @@ function App() { {/* Dashboard Routes */} }> } /> + } + /> + } + /> } /> } /> } /> @@ -103,13 +114,11 @@ function App() { } /> } /> } /> - } /> } /> - } - /> + } /> } /> + } /> + } /> }> diff --git a/src/components/DealForm.tsx b/src/components/DealForm.tsx new file mode 100644 index 000000000..3c8ffd511 --- /dev/null +++ b/src/components/DealForm.tsx @@ -0,0 +1,402 @@ +import React, { useState, useEffect } from "react"; +import axios from "axios"; +import { Button } from "./ui/Button"; +import { X } from "lucide-react"; +import toast from "react-hot-toast"; + +const URL = import.meta.env.VITE_BACKEND_URL; + +interface DealFormProps { + entrepreneur: any; + investor: any; + onClose: () => void; + valuation: number; + readOnly?: boolean; + initialData?: any; + isEmbedded?: boolean; + onSubmit?: (data: any) => void; +} + +export const DealForm: React.FC = ({ + entrepreneur, + investor, + onClose, + valuation, + readOnly = false, + initialData, + isEmbedded = false, + onSubmit, +}) => { + const [formData, setFormData] = useState({ + investmentAmount: initialData?.investmentAmount || "", + equityOffered: initialData?.equityOffered || "", + preMoneyValuation: initialData?.preMoneyValuation || valuation || 0, + postMoneyValuation: initialData?.postMoneyValuation || 0, + investmentType: initialData?.investmentType || "Equity", + boardSeat: initialData?.boardSeat || "No", + votingRights: initialData?.votingRights || "None", + dividends: initialData?.dividends || "On Exit Only", + rofr: initialData?.rofr || "No", + exitStrategy: initialData?.exitStrategy || "Acquisition", + exitTimeline: initialData?.exitTimeline, + additionalTerms: initialData?.additionalTerms || "", + stage: initialData?.stage || "Seed", + }); + + // Auto-calculate Post-Money Valuation + useEffect(() => { + const amount = Number(formData.investmentAmount) || 0; + const preMoney = Number(formData.preMoneyValuation) || 0; + setFormData((prev) => ({ + ...prev, + postMoneyValuation: preMoney + amount, + })); + }, [formData.investmentAmount, formData.preMoneyValuation]); + + const handleChange = ( + e: React.ChangeEvent< + HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement + > + ) => { + const { name, value } = e.target; + setFormData((prev) => ({ ...prev, [name]: value })); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + const payload = { + investorId: investor.userId, + entrepreneurId: entrepreneur.userId, + ...formData, + investmentAmount: Number(formData.investmentAmount), + equityOffered: Number(formData.equityOffered), + preMoneyValuation: Number(formData.preMoneyValuation), + postMoneyValuation: Number(formData.postMoneyValuation), + }; + + if (onSubmit) { + onSubmit(payload); + return; + } + + try { + await axios.post(`${URL}/deal/create-deal`, payload, { + withCredentials: true, + }); + + toast.success("Deal proposal sent successfully!"); + onClose(); + } catch (error) { + console.error(error); + toast.error("Failed to send deal proposal."); + } + }; + + const content = ( +
+ {!isEmbedded && ( + + )} +

+ {readOnly ? (isEmbedded ? "Current Deal Terms" : "View Deal Proposal") : (isEmbedded ? "Proposed Counter Offer" : "Startup Investment Deal Form")} +

+ +
+ {/* ... form content ... */} + {/* Section 1: Investor Details */} +
+

+ Section 1: Investor Details +

+
+
+ + +
+
+ + +
+
+
+ + {/* Section 2: Entrepreneur Details */} +
+

+ Section 2: Entrepreneur Details +

+
+
+ + +
+
+ + +
+
+
+ + {/* Section 3: Investment Terms */} +
+

+ Section 3: Investment Terms +

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + {/* Section 4: Investor Rights & Preferences */} +
+

+ Section 4: Investor Rights & Preferences +

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + {/* Section 5: Exit Strategy */} +
+

+ Section 5: Exit Strategy +

+
+
+ + +
+
+ + +
+
+
+ + {/* Additional Terms */} +
+ +