mirror of
https://github.com/Sosokker/B2D-Ventures.git
synced 2025-12-19 05:54:06 +01:00
Merge branch 'main' into front-end
This commit is contained in:
commit
ca894212ef
11
.env.example
Normal file
11
.env.example
Normal file
@ -0,0 +1,11 @@
|
||||
PROJECT_ID=supabase-project-id
|
||||
NEXT_PUBLIC_SUPABASE_URL=supabase-project-url
|
||||
NEXT_PUBLIC_SUPABASE_URL_SOURCE = supabase-project-url-without-https:// (ex: https://example.com -> example.com)
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=supabase-anon-key
|
||||
NEXT_PUBLIC_AUTH_GOOGLE_ID=google-auth-token
|
||||
NEXT_PUBLIC_AUTH_GOOGLE_SECRET=google-secret-key
|
||||
NEXT_PUBLIC_DUMMY_EMAIL=supabse-dummy-user-email-for-testing
|
||||
NEXT_PUBLIC_DUMMY_PASSWORD=supabse-dummy-user-password-for-testing
|
||||
NEXT_PUBLIC_TEST_URL=url-to-run-testing-server
|
||||
NEXT_PUBLIC_STRIPE_PUBLIC_KEY=stripe-public-key
|
||||
STRIPE_SECRET_KEY=stripe-secret-key
|
||||
@ -1,4 +1,11 @@
|
||||
NEXT_PUBLIC_SUPABASE_URL=your-project-url
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
|
||||
NEXT_PUBLIC_AUTH_GOOGLE_ID=your-google-id
|
||||
NEXT_PUBLIC_AUTH_GOOGLE_SECRET=your-google-secret
|
||||
PROJECT_ID=supabase-project-id
|
||||
NEXT_PUBLIC_SUPABASE_URL=supabase-project-url
|
||||
NEXT_PUBLIC_SUPABASE_URL_SOURCE = supabase-project-url-without-https:// (ex: https://example.com -> example.com)
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=supabase-anon-key
|
||||
NEXT_PUBLIC_AUTH_GOOGLE_ID=google-auth-token
|
||||
NEXT_PUBLIC_AUTH_GOOGLE_SECRET=google-secret-key
|
||||
NEXT_PUBLIC_DUMMY_EMAIL=supabse-dummy-user-email-for-testing
|
||||
NEXT_PUBLIC_DUMMY_PASSWORD=supabse-dummy-user-password-for-testing
|
||||
NEXT_PUBLIC_TEST_URL=url-to-run-testing-server
|
||||
NEXT_PUBLIC_STRIPE_PUBLIC_KEY=stripe-public-key
|
||||
STRIPE_SECRET_KEY=stripe-secret-key
|
||||
65
README.md
65
README.md
@ -1,36 +1,51 @@
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
# B2D-Ventures
|
||||
|
||||
## Getting Started
|
||||
## About
|
||||
|
||||
First, run the development server:
|
||||
B2D Ventures is a fund raising platform
|
||||
|
||||
## Installation
|
||||
|
||||
List of external services you need before initialize the project
|
||||
- [Supabase](https://supabase.com/)
|
||||
- [Google OAuth2](https://developers.google.com/identity/protocols/oauth2) : Use with supabase authentication
|
||||
- [Stripe](https://stripe.com/en-th)
|
||||
|
||||
1. Create .env to store environment variable
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
cp .env.example .env
|
||||
// Add tokens to .env
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
2. Install dependecies and start server
|
||||
```
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||
## Project Structure
|
||||
|
||||
## Learn More
|
||||
```
|
||||
public/ Static assets
|
||||
src/
|
||||
├─ app/ Contains core application files, routing, and page components
|
||||
│ ├─ api/ API route handlers
|
||||
│ ├─ globals.css Shadcn/ui theming
|
||||
├─ components/ Reusable UI components for building the interface
|
||||
│ ├─ ui/ Shadcn/ui components
|
||||
├─ lib/ Utility functions and service libraries
|
||||
│ ├─ data/ Data-fetching logic or helper functions for handling data (Supabase queries)
|
||||
│ ├─ stripe/ Configuration code related to Stripe payment integration.
|
||||
│ ├─ supabase/ Utilities for Supabase integration (database, authentication, etc.).
|
||||
├─ types/ Infers types from Supabase and includes interfaces
|
||||
├─ middleware.ts Custom middleware for processing each request
|
||||
tests/ Playwright tests for end-to-end testing of the application
|
||||
```
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
## Contributor
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
1. Nantawat Sukrisunt | [Nantawat6510545543](https://github.com/Nantawat6510545543)
|
||||
2. Naytitorn Chaovirachot | [CondricNay](https://github.com/CondricNay)
|
||||
3. Sirin Puenggun | [sosokker](https://github.com/Sosokker)
|
||||
4. Pattadon Loyprasert | [GGWPXXXX](https://github.com/GGWPXXXX)
|
||||
|
||||
603
package-lock.json
generated
603
package-lock.json
generated
@ -23,6 +23,8 @@
|
||||
"@radix-ui/react-switch": "^1.1.1",
|
||||
"@radix-ui/react-tabs": "^1.1.0",
|
||||
"@radix-ui/react-tooltip": "^1.1.2",
|
||||
"@stripe/react-stripe-js": "^2.8.1",
|
||||
"@stripe/stripe-js": "^4.7.0",
|
||||
"@supabase-cache-helpers/postgrest-react-query": "^1.10.1",
|
||||
"@supabase/ssr": "^0.4.1",
|
||||
"@supabase/supabase-js": "^2.45.2",
|
||||
@ -35,7 +37,7 @@
|
||||
"dotenv": "^16.4.5",
|
||||
"embla-carousel-react": "^8.2.0",
|
||||
"lucide-react": "^0.428.0",
|
||||
"next": "14.2.5",
|
||||
"next": "^14.2.15",
|
||||
"next-themes": "^0.3.0",
|
||||
"react": "^18",
|
||||
"react-countup": "^6.5.3",
|
||||
@ -44,6 +46,7 @@
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"react-markdown": "^9.0.1",
|
||||
"recharts": "^2.12.7",
|
||||
"stripe": "^17.1.0",
|
||||
"tailwind-merge": "^2.5.2",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"zod": "^3.23.8"
|
||||
@ -51,12 +54,14 @@
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.47.2",
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.5",
|
||||
"postcss": "^8",
|
||||
"prettier": "^3.3.3",
|
||||
"supabase": "^1.200.3",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5"
|
||||
@ -73,6 +78,271 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.25.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz",
|
||||
"integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/highlight": "^7.25.7",
|
||||
"picocolors": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
"version": "7.17.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz",
|
||||
"integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.17.0",
|
||||
"jsesc": "^2.5.1",
|
||||
"source-map": "^0.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-environment-visitor": {
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz",
|
||||
"integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.24.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-environment-visitor/node_modules/@babel/types": {
|
||||
"version": "7.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz",
|
||||
"integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.25.7",
|
||||
"@babel/helper-validator-identifier": "^7.25.7",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-function-name": {
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz",
|
||||
"integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.24.7",
|
||||
"@babel/types": "^7.24.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-function-name/node_modules/@babel/types": {
|
||||
"version": "7.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz",
|
||||
"integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.25.7",
|
||||
"@babel/helper-validator-identifier": "^7.25.7",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-hoist-variables": {
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz",
|
||||
"integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.24.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-hoist-variables/node_modules/@babel/types": {
|
||||
"version": "7.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz",
|
||||
"integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.25.7",
|
||||
"@babel/helper-validator-identifier": "^7.25.7",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-split-export-declaration": {
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz",
|
||||
"integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.24.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-split-export-declaration/node_modules/@babel/types": {
|
||||
"version": "7.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz",
|
||||
"integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.25.7",
|
||||
"@babel/helper-validator-identifier": "^7.25.7",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-string-parser": {
|
||||
"version": "7.25.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz",
|
||||
"integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "7.25.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz",
|
||||
"integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight": {
|
||||
"version": "7.25.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz",
|
||||
"integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.25.7",
|
||||
"chalk": "^2.4.2",
|
||||
"js-tokens": "^4.0.0",
|
||||
"picocolors": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^1.9.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/chalk": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
|
||||
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^3.2.1",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"supports-color": "^5.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight/node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-flag": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz",
|
||||
"integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.25.8"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser/node_modules/@babel/types": {
|
||||
"version": "7.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz",
|
||||
"integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.25.7",
|
||||
"@babel/helper-validator-identifier": "^7.25.7",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.25.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz",
|
||||
@ -96,6 +366,118 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.25.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz",
|
||||
"integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.25.7",
|
||||
"@babel/parser": "^7.25.7",
|
||||
"@babel/types": "^7.25.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template/node_modules/@babel/types": {
|
||||
"version": "7.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz",
|
||||
"integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.25.7",
|
||||
"@babel/helper-validator-identifier": "^7.25.7",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
|
||||
"integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/generator": "^7.23.0",
|
||||
"@babel/helper-environment-visitor": "^7.22.20",
|
||||
"@babel/helper-function-name": "^7.23.0",
|
||||
"@babel/helper-hoist-variables": "^7.22.5",
|
||||
"@babel/helper-split-export-declaration": "^7.22.6",
|
||||
"@babel/parser": "^7.23.0",
|
||||
"@babel/types": "^7.23.0",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse/node_modules/@babel/generator": {
|
||||
"version": "7.25.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz",
|
||||
"integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.25.7",
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
"@jridgewell/trace-mapping": "^0.3.25",
|
||||
"jsesc": "^3.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse/node_modules/@babel/types": {
|
||||
"version": "7.25.8",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz",
|
||||
"integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.25.7",
|
||||
"@babel/helper-validator-identifier": "^7.25.7",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse/node_modules/globals": {
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse/node_modules/jsesc": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
|
||||
"integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"jsesc": "bin/jsesc"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz",
|
||||
"integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.16.7",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
|
||||
@ -327,9 +709,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/env": {
|
||||
"version": "14.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.5.tgz",
|
||||
"integrity": "sha512-/zZGkrTOsraVfYjGP8uM0p6r0BDT6xWpkjdVbcz66PJVSpwXX3yNiRycxAuDfBKGWBrZBXRuK/YVlkNgxHGwmA=="
|
||||
"version": "14.2.15",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.15.tgz",
|
||||
"integrity": "sha512-S1qaj25Wru2dUpcIZMjxeMVSwkt8BK4dmWHHiBuRstcIyOsMapqT4A4jSB6onvqeygkSSmOkyny9VVx8JIGamQ=="
|
||||
},
|
||||
"node_modules/@next/eslint-plugin-next": {
|
||||
"version": "14.2.5",
|
||||
@ -341,9 +723,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-arm64": {
|
||||
"version": "14.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.5.tgz",
|
||||
"integrity": "sha512-/9zVxJ+K9lrzSGli1///ujyRfon/ZneeZ+v4ptpiPoOU+GKZnm8Wj8ELWU1Pm7GHltYRBklmXMTUqM/DqQ99FQ==",
|
||||
"version": "14.2.15",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.15.tgz",
|
||||
"integrity": "sha512-Rvh7KU9hOUBnZ9TJ28n2Oa7dD9cvDBKua9IKx7cfQQ0GoYUwg9ig31O2oMwH3wm+pE3IkAQ67ZobPfEgurPZIA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -356,9 +738,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-x64": {
|
||||
"version": "14.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.5.tgz",
|
||||
"integrity": "sha512-vXHOPCwfDe9qLDuq7U1OYM2wUY+KQ4Ex6ozwsKxp26BlJ6XXbHleOUldenM67JRyBfVjv371oneEvYd3H2gNSA==",
|
||||
"version": "14.2.15",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.15.tgz",
|
||||
"integrity": "sha512-5TGyjFcf8ampZP3e+FyCax5zFVHi+Oe7sZyaKOngsqyaNEpOgkKB3sqmymkZfowy3ufGA/tUgDPPxpQx931lHg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -371,9 +753,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||
"version": "14.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.5.tgz",
|
||||
"integrity": "sha512-vlhB8wI+lj8q1ExFW8lbWutA4M2ZazQNvMWuEDqZcuJJc78iUnLdPPunBPX8rC4IgT6lIx/adB+Cwrl99MzNaA==",
|
||||
"version": "14.2.15",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.15.tgz",
|
||||
"integrity": "sha512-3Bwv4oc08ONiQ3FiOLKT72Q+ndEMyLNsc/D3qnLMbtUYTQAmkx9E/JRu0DBpHxNddBmNT5hxz1mYBphJ3mfrrw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -386,9 +768,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-musl": {
|
||||
"version": "14.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.5.tgz",
|
||||
"integrity": "sha512-NpDB9NUR2t0hXzJJwQSGu1IAOYybsfeB+LxpGsXrRIb7QOrYmidJz3shzY8cM6+rO4Aojuef0N/PEaX18pi9OA==",
|
||||
"version": "14.2.15",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.15.tgz",
|
||||
"integrity": "sha512-k5xf/tg1FBv/M4CMd8S+JL3uV9BnnRmoe7F+GWC3DxkTCD9aewFRH1s5rJ1zkzDa+Do4zyN8qD0N8c84Hu96FQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -401,9 +783,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-gnu": {
|
||||
"version": "14.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.5.tgz",
|
||||
"integrity": "sha512-8XFikMSxWleYNryWIjiCX+gU201YS+erTUidKdyOVYi5qUQo/gRxv/3N1oZFCgqpesN6FPeqGM72Zve+nReVXQ==",
|
||||
"version": "14.2.15",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.15.tgz",
|
||||
"integrity": "sha512-kE6q38hbrRbKEkkVn62reLXhThLRh6/TvgSP56GkFNhU22TbIrQDEMrO7j0IcQHcew2wfykq8lZyHFabz0oBrA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -416,9 +798,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-musl": {
|
||||
"version": "14.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.5.tgz",
|
||||
"integrity": "sha512-6QLwi7RaYiQDcRDSU/os40r5o06b5ue7Jsk5JgdRBGGp8l37RZEh9JsLSM8QF0YDsgcosSeHjglgqi25+m04IQ==",
|
||||
"version": "14.2.15",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.15.tgz",
|
||||
"integrity": "sha512-PZ5YE9ouy/IdO7QVJeIcyLn/Rc4ml9M2G4y3kCM9MNf1YKvFY4heg3pVa/jQbMro+tP6yc4G2o9LjAz1zxD7tQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -431,9 +813,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||
"version": "14.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.5.tgz",
|
||||
"integrity": "sha512-1GpG2VhbspO+aYoMOQPQiqc/tG3LzmsdBH0LhnDS3JrtDx2QmzXe0B6mSZZiN3Bq7IOMXxv1nlsjzoS1+9mzZw==",
|
||||
"version": "14.2.15",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.15.tgz",
|
||||
"integrity": "sha512-2raR16703kBvYEQD9HNLyb0/394yfqzmIeyp2nDzcPV4yPjqNUG3ohX6jX00WryXz6s1FXpVhsCo3i+g4RUX+g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -446,9 +828,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-ia32-msvc": {
|
||||
"version": "14.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.5.tgz",
|
||||
"integrity": "sha512-Igh9ZlxwvCDsu6438FXlQTHlRno4gFpJzqPjSIBZooD22tKeI4fE/YMRoHVJHmrQ2P5YL1DoZ0qaOKkbeFWeMg==",
|
||||
"version": "14.2.15",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.15.tgz",
|
||||
"integrity": "sha512-fyTE8cklgkyR1p03kJa5zXEaZ9El+kDNM5A+66+8evQS5e/6v0Gk28LqA0Jet8gKSOyP+OTm/tJHzMlGdQerdQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@ -461,9 +843,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-x64-msvc": {
|
||||
"version": "14.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.5.tgz",
|
||||
"integrity": "sha512-tEQ7oinq1/CjSG9uSTerca3v4AZ+dFa+4Yu6ihaG8Ud8ddqLQgFGcnwYls13H5X5CPDPZJdYxyeMui6muOLd4g==",
|
||||
"version": "14.2.15",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.15.tgz",
|
||||
"integrity": "sha512-SzqGbsLsP9OwKNUG9nekShTwhj6JSB9ZLMWQ8g1gG6hdE5gQLncbnbymrwy2yVmH9nikSLYRYxYMFu78Ggp7/g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -1506,6 +1888,27 @@
|
||||
"integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@stripe/react-stripe-js": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-2.8.1.tgz",
|
||||
"integrity": "sha512-C410jVKOATinXLalWotab6E6jlWAlbqUDWL9q1km0p5UHrvnihjjYzA8imYXc4xc4Euf9GeKDQc4n35HKZvgwg==",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.7.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@stripe/stripe-js": "^1.44.1 || ^2.0.0 || ^3.0.0 || ^4.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@stripe/stripe-js": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-4.7.0.tgz",
|
||||
"integrity": "sha512-Dfdg8UumBu+zDnBgGw30zEE9PIm8lunDUuwy2Bois9bb2sxCohVbD1PpAnasPIZhYfIbigmTHEr/Hg+56Vue6A==",
|
||||
"engines": {
|
||||
"node": ">=12.16"
|
||||
}
|
||||
},
|
||||
"node_modules/@supabase-cache-helpers/postgrest-core": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@supabase-cache-helpers/postgrest-core/-/postgrest-core-0.8.1.tgz",
|
||||
@ -1707,6 +2110,29 @@
|
||||
"react": "^18 || ^19"
|
||||
}
|
||||
},
|
||||
"node_modules/@trivago/prettier-plugin-sort-imports": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.3.0.tgz",
|
||||
"integrity": "sha512-r3n0onD3BTOVUNPhR4lhVK4/pABGpbA7bW3eumZnYdKaHkf1qEC+Mag6DPbGNuuh0eG8AaYj+YqmVHSiGslaTQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/generator": "7.17.7",
|
||||
"@babel/parser": "^7.20.5",
|
||||
"@babel/traverse": "7.23.2",
|
||||
"@babel/types": "7.17.0",
|
||||
"javascript-natural-sort": "0.7.1",
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/compiler-sfc": "3.x",
|
||||
"prettier": "2.x - 3.x"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/compiler-sfc": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@types/d3-array": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
|
||||
@ -2387,7 +2813,6 @@
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
|
||||
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
@ -2934,7 +3359,6 @@
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
||||
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
@ -3155,7 +3579,6 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
|
||||
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.2.4"
|
||||
},
|
||||
@ -3167,7 +3590,6 @@
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
@ -3970,7 +4392,6 @@
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
|
||||
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
@ -4139,7 +4560,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.1.3"
|
||||
},
|
||||
@ -4180,7 +4600,6 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
||||
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0"
|
||||
},
|
||||
@ -4192,7 +4611,6 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
|
||||
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
@ -4204,7 +4622,6 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
@ -4876,6 +5293,12 @@
|
||||
"@pkgjs/parseargs": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/javascript-natural-sort": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
|
||||
"integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/jiti": {
|
||||
"version": "1.21.6",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz",
|
||||
@ -4901,6 +5324,18 @@
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/jsesc": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
|
||||
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"jsesc": "bin/jsesc"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/json-buffer": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
|
||||
@ -5780,11 +6215,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/next": {
|
||||
"version": "14.2.5",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-14.2.5.tgz",
|
||||
"integrity": "sha512-0f8aRfBVL+mpzfBjYfQuLWh2WyAwtJXCRfkPF4UJ5qd2YwrHczsrSzXU4tRMV0OAxR8ZJZWPFn6uhSC56UTsLA==",
|
||||
"version": "14.2.15",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-14.2.15.tgz",
|
||||
"integrity": "sha512-h9ctmOokpoDphRvMGnwOJAedT6zKhwqyZML9mDtspgf4Rh3Pn7UTYKqePNoDvhsWBAO5GoPNYshnAUGIazVGmw==",
|
||||
"dependencies": {
|
||||
"@next/env": "14.2.5",
|
||||
"@next/env": "14.2.15",
|
||||
"@swc/helpers": "0.5.5",
|
||||
"busboy": "1.6.0",
|
||||
"caniuse-lite": "^1.0.30001579",
|
||||
@ -5799,15 +6234,15 @@
|
||||
"node": ">=18.17.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@next/swc-darwin-arm64": "14.2.5",
|
||||
"@next/swc-darwin-x64": "14.2.5",
|
||||
"@next/swc-linux-arm64-gnu": "14.2.5",
|
||||
"@next/swc-linux-arm64-musl": "14.2.5",
|
||||
"@next/swc-linux-x64-gnu": "14.2.5",
|
||||
"@next/swc-linux-x64-musl": "14.2.5",
|
||||
"@next/swc-win32-arm64-msvc": "14.2.5",
|
||||
"@next/swc-win32-ia32-msvc": "14.2.5",
|
||||
"@next/swc-win32-x64-msvc": "14.2.5"
|
||||
"@next/swc-darwin-arm64": "14.2.15",
|
||||
"@next/swc-darwin-x64": "14.2.15",
|
||||
"@next/swc-linux-arm64-gnu": "14.2.15",
|
||||
"@next/swc-linux-arm64-musl": "14.2.15",
|
||||
"@next/swc-linux-x64-gnu": "14.2.15",
|
||||
"@next/swc-linux-x64-musl": "14.2.15",
|
||||
"@next/swc-win32-arm64-msvc": "14.2.15",
|
||||
"@next/swc-win32-ia32-msvc": "14.2.15",
|
||||
"@next/swc-win32-x64-msvc": "14.2.15"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.1.0",
|
||||
@ -5938,7 +6373,6 @@
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
|
||||
"integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
@ -6426,6 +6860,21 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz",
|
||||
"integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/prop-types": {
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
@ -6454,6 +6903,20 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
@ -6958,7 +7421,6 @@
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"define-data-property": "^1.1.4",
|
||||
"es-errors": "^1.3.0",
|
||||
@ -7009,7 +7471,6 @@
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
|
||||
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.7",
|
||||
"es-errors": "^1.3.0",
|
||||
@ -7043,6 +7504,15 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
@ -7292,6 +7762,18 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/stripe": {
|
||||
"version": "17.1.0",
|
||||
"resolved": "https://registry.npmjs.org/stripe/-/stripe-17.1.0.tgz",
|
||||
"integrity": "sha512-sNRsJx7LPP2Gg6cBJoHJqhr+UoBVZZRes2BDqbrg+1FDBxJc4Yn+LkrpJ/VnL3XQ3+6I5GrOUnSSFfE/joDFjw==",
|
||||
"dependencies": {
|
||||
"@types/node": ">=8.1.0",
|
||||
"qs": "^6.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.*"
|
||||
}
|
||||
},
|
||||
"node_modules/style-to-object": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz",
|
||||
@ -7494,6 +7976,15 @@
|
||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
|
||||
},
|
||||
"node_modules/to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
|
||||
@ -24,6 +24,8 @@
|
||||
"@radix-ui/react-switch": "^1.1.1",
|
||||
"@radix-ui/react-tabs": "^1.1.0",
|
||||
"@radix-ui/react-tooltip": "^1.1.2",
|
||||
"@stripe/react-stripe-js": "^2.8.1",
|
||||
"@stripe/stripe-js": "^4.7.0",
|
||||
"@supabase-cache-helpers/postgrest-react-query": "^1.10.1",
|
||||
"@supabase/ssr": "^0.4.1",
|
||||
"@supabase/supabase-js": "^2.45.2",
|
||||
@ -36,7 +38,7 @@
|
||||
"dotenv": "^16.4.5",
|
||||
"embla-carousel-react": "^8.2.0",
|
||||
"lucide-react": "^0.428.0",
|
||||
"next": "14.2.5",
|
||||
"next": "^14.2.15",
|
||||
"next-themes": "^0.3.0",
|
||||
"react": "^18",
|
||||
"react-countup": "^6.5.3",
|
||||
@ -45,6 +47,7 @@
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"react-markdown": "^9.0.1",
|
||||
"recharts": "^2.12.7",
|
||||
"stripe": "^17.1.0",
|
||||
"tailwind-merge": "^2.5.2",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"zod": "^3.23.8"
|
||||
@ -52,12 +55,14 @@
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.47.2",
|
||||
"@tailwindcss/typography": "^0.5.15",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.5",
|
||||
"postcss": "^8",
|
||||
"prettier": "^3.3.3",
|
||||
"supabase": "^1.200.3",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5"
|
||||
|
||||
161
src/app/(investment)/invest/[id]/checkoutPage.tsx
Normal file
161
src/app/(investment)/invest/[id]/checkoutPage.tsx
Normal file
@ -0,0 +1,161 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useStripe, useElements, CardElement } from "@stripe/react-stripe-js";
|
||||
import convertToSubcurrency from "@/lib/convertToSubcurrency";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
DialogFooter,
|
||||
DialogClose,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import useSession from "@/lib/supabase/useSession";
|
||||
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
const CheckoutPage = ({
|
||||
amount,
|
||||
project_id,
|
||||
investor_id,
|
||||
isAcceptTermAndService,
|
||||
}: {
|
||||
amount: number;
|
||||
project_id: number;
|
||||
investor_id: string;
|
||||
isAcceptTermAndService: () => boolean;
|
||||
}) => {
|
||||
const stripe = useStripe();
|
||||
const elements = useElements();
|
||||
const [errorMessage, setErrorMessage] = useState<string>();
|
||||
const [clientSecret, setClientSecret] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||
const isAcceptTerm = isAcceptTermAndService();
|
||||
const router = useRouter();
|
||||
|
||||
const { session } = useSession();
|
||||
const user = session?.user;
|
||||
|
||||
useEffect(() => {
|
||||
fetch("/api/create-payment-intent", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ amount: convertToSubcurrency(amount) }),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((data) => setClientSecret(data.clientSecret));
|
||||
}, [amount]);
|
||||
|
||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
setLoading(true);
|
||||
|
||||
if (!stripe || !elements) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { error: submitError } = await elements.submit();
|
||||
|
||||
if (submitError) {
|
||||
setErrorMessage(submitError.message);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await stripe
|
||||
.confirmCardPayment(clientSecret, {
|
||||
payment_method: {
|
||||
card: elements.getElement(CardElement)!,
|
||||
},
|
||||
})
|
||||
.then(async (result) => {
|
||||
if (result.error) {
|
||||
setErrorMessage(result.error.message);
|
||||
} else {
|
||||
try {
|
||||
const supabase = createSupabaseClient();
|
||||
const { data, error } = await supabase.from("investment_deal").insert([
|
||||
{
|
||||
investor_id: investor_id,
|
||||
project_id: project_id,
|
||||
deal_amount: amount,
|
||||
},
|
||||
]);
|
||||
|
||||
if (error) {
|
||||
console.error("Supabase Insert Error:", error.message);
|
||||
} else {
|
||||
console.log("Insert successful:", data);
|
||||
router.push(`http://www.localhost:3000/payment-success?amount=${amount}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Unexpected error during Supabase insert:", err);
|
||||
}
|
||||
}
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
if (!clientSecret || !stripe || !elements) {
|
||||
return (
|
||||
<div className="flex items-center justify-center">
|
||||
<div
|
||||
className="inline-block h-8 w-8 animate-spin rounded-full border-4 border-solid border-current border-e-transparent align-[-0.125em] text-surface motion-reduce:animate-[spin_1.5s_linear_infinite] dark:text-white"
|
||||
role="status">
|
||||
<span className="!absolute !-m-px !h-px !w-px !overflow-hidden !whitespace-nowrap !border-0 !p-0 ![clip:rect(0,0,0,0)]">
|
||||
Loading...
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{clientSecret && <CardElement />}
|
||||
|
||||
{errorMessage && <div>{errorMessage}</div>}
|
||||
|
||||
{/* Trigger the dialog when the "Pay" button is clicked */}
|
||||
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsDialogOpen(true)}
|
||||
disabled={!stripe || loading}
|
||||
className="text-white w-full p-5 bg-black mt-2 rounded-md font-bold disabled:opacity-50 disabled:animate-pulse">
|
||||
{!loading ? `Pay $${amount}` : "Processing..."}
|
||||
</button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Are you sure you want to proceed with the investment?</DialogTitle>
|
||||
<DialogDescription>This action cannot be undone, and the investment will be confirmed.</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<form onSubmit={handleSubmit} className="bg-white p-2 rounded-md">
|
||||
<Button type="submit" disabled={!isAcceptTerm}>
|
||||
Confirm Payment
|
||||
</Button>
|
||||
</form>
|
||||
<DialogClose asChild>
|
||||
<Button type="button" variant="secondary">
|
||||
Cancel
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</DialogFooter>
|
||||
{errorMessage && <p className="text-red-500 mt-2 text-lg font-bold">{errorMessage}</p>}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CheckoutPage;
|
||||
155
src/app/(investment)/invest/[id]/page.tsx
Normal file
155
src/app/(investment)/invest/[id]/page.tsx
Normal file
@ -0,0 +1,155 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useParams } from "next/navigation";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { useQuery } from "@supabase-cache-helpers/postgrest-react-query";
|
||||
|
||||
import convertToSubcurrency from "@/lib/convertToSubcurrency";
|
||||
import CheckoutPage from "./checkoutPage";
|
||||
import { Elements } from "@stripe/react-stripe-js";
|
||||
import { loadStripe } from "@stripe/stripe-js";
|
||||
|
||||
import { getProjectDataQuery } from "@/lib/data/projectQuery";
|
||||
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
|
||||
import useSession from "@/lib/supabase/useSession";
|
||||
|
||||
if (process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY === undefined) {
|
||||
throw new Error("NEXT_PUBLIC_STRIPE_PUBLIC_KEY is not defined");
|
||||
}
|
||||
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY);
|
||||
|
||||
const term_data = [
|
||||
{
|
||||
term: "Minimum Investment",
|
||||
description: "The minimum investment amount is $500.",
|
||||
},
|
||||
{
|
||||
term: "Investment Horizon",
|
||||
description: "Investments are typically locked for a minimum of 12 months.",
|
||||
},
|
||||
{
|
||||
term: "Fees",
|
||||
description: "A management fee of 2% will be applied annually.",
|
||||
},
|
||||
{
|
||||
term: "Returns",
|
||||
description: "Expected annual returns are between 8% and 12%.",
|
||||
},
|
||||
{
|
||||
term: "Risk Disclosure",
|
||||
description: "Investments carry risks, including the loss of principal.",
|
||||
},
|
||||
{
|
||||
term: "Withdrawal Policy",
|
||||
description: "Withdrawals can be made after the lock-in period.",
|
||||
},
|
||||
];
|
||||
|
||||
export default function InvestPage() {
|
||||
const [checkedTerms, setCheckedTerms] = useState(Array(term_data.length).fill(false));
|
||||
const [investAmount, setInvestAmount] = useState(10);
|
||||
const [investor_id, setInvestorId] = useState<string>("");
|
||||
|
||||
const params = useParams<{ id: string }>();
|
||||
const supabase = createSupabaseClient();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchInvestorData = async () => {
|
||||
const { data, error } = await supabase.auth.getSession();
|
||||
if (error) {
|
||||
console.error("Error fetching session:", error);
|
||||
return;
|
||||
}
|
||||
if (data.session) {
|
||||
setInvestorId(data.session.user.id);
|
||||
}
|
||||
};
|
||||
|
||||
fetchInvestorData();
|
||||
}, [supabase]);
|
||||
|
||||
const { data: projectData, isLoading: isLoadingProject } = useQuery(getProjectDataQuery(supabase, Number(params.id)));
|
||||
|
||||
const handleCheckboxChange = (index: number) => {
|
||||
const updatedCheckedTerms = [...checkedTerms];
|
||||
updatedCheckedTerms[index] = !updatedCheckedTerms[index];
|
||||
setCheckedTerms(updatedCheckedTerms);
|
||||
};
|
||||
|
||||
const isAcceptTermAndService = () => {
|
||||
if (checkedTerms.some((checked) => !checked)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mx-10 md:mx-40 my-10">
|
||||
<h1 className="text-2xl md:text-4xl font-bold">Invest on ${projectData?.project_name}</h1>
|
||||
<Separator className="my-4" />
|
||||
<div></div>
|
||||
<div>
|
||||
<div className="w-1/2 space-y-2">
|
||||
<h2 className="text:base md:text-2xl">Investment Amount</h2>
|
||||
<Input
|
||||
className="w-52"
|
||||
type="number"
|
||||
placeholder="min $10"
|
||||
min={10}
|
||||
onChangeCapture={(e) => setInvestAmount(Number(e.currentTarget.value))}
|
||||
/>
|
||||
</div>
|
||||
<Separator className="my-4" />
|
||||
|
||||
<div className=" md:w-2/3 space-y-2">
|
||||
<h2 className="text-2xl">Terms and Services</h2>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Select</TableHead>
|
||||
<TableHead>Term</TableHead>
|
||||
<TableHead>Description</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{term_data.map((item, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell>
|
||||
<input type="checkbox" checked={checkedTerms[index]} onChange={() => handleCheckboxChange(index)} />
|
||||
</TableCell>
|
||||
<TableCell>{item.term}</TableCell>
|
||||
<TableCell>{item.description}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<Separator className="my-4" />
|
||||
|
||||
<div className="w-full space-y-2">
|
||||
<h2 className="text:base md:text-2xl">Payment Information</h2>
|
||||
<div>
|
||||
<Elements
|
||||
stripe={stripePromise}
|
||||
options={{
|
||||
mode: "payment",
|
||||
amount: convertToSubcurrency(investAmount),
|
||||
currency: "usd",
|
||||
}}>
|
||||
<CheckoutPage
|
||||
amount={investAmount}
|
||||
isAcceptTermAndService={isAcceptTermAndService}
|
||||
project_id={Number(params.id)}
|
||||
investor_id={investor_id}
|
||||
/>
|
||||
</Elements>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,168 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { CardsPaymentMethod } from "@/components/paymentMethod";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
DialogFooter,
|
||||
DialogClose,
|
||||
} from "@/components/ui/dialog";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { toast } from "react-hot-toast";
|
||||
|
||||
const term_data = [
|
||||
{
|
||||
term: "Minimum Investment",
|
||||
description: "The minimum investment amount is $500.",
|
||||
},
|
||||
{
|
||||
term: "Investment Horizon",
|
||||
description: "Investments are typically locked for a minimum of 12 months.",
|
||||
},
|
||||
{
|
||||
term: "Fees",
|
||||
description: "A management fee of 2% will be applied annually.",
|
||||
},
|
||||
{
|
||||
term: "Returns",
|
||||
description: "Expected annual returns are between 8% and 12%.",
|
||||
},
|
||||
{
|
||||
term: "Risk Disclosure",
|
||||
description: "Investments carry risks, including the loss of principal.",
|
||||
},
|
||||
{
|
||||
term: "Withdrawal Policy",
|
||||
description: "Withdrawals can be made after the lock-in period.",
|
||||
},
|
||||
];
|
||||
|
||||
export default function Invest() {
|
||||
const [checkedTerms, setCheckedTerms] = useState(
|
||||
Array(term_data.length).fill(false)
|
||||
);
|
||||
const [error, setError] = useState("");
|
||||
const router = useRouter(); // Initialize the router
|
||||
|
||||
const handleCheckboxChange = (index: number) => {
|
||||
const updatedCheckedTerms = [...checkedTerms];
|
||||
updatedCheckedTerms[index] = !updatedCheckedTerms[index];
|
||||
setCheckedTerms(updatedCheckedTerms);
|
||||
};
|
||||
|
||||
const handleTermServiceClick = () => {
|
||||
if (checkedTerms.some((checked) => !checked)) {
|
||||
setError(
|
||||
"Please accept all terms before proceeding with the investment."
|
||||
);
|
||||
} else {
|
||||
setError("");
|
||||
handleInvestmentSuccess();
|
||||
}
|
||||
};
|
||||
|
||||
const handleInvestmentSuccess = () => {
|
||||
toast.success("You successfully invested!");
|
||||
|
||||
setTimeout(() => {
|
||||
router.push("/");
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mx-10 md:mx-40 my-10">
|
||||
<h1 className="text-2xl md:text-4xl font-bold">Invest on NVIDIA</h1>
|
||||
<Separator className="my-4" />
|
||||
|
||||
<div>
|
||||
<div className="w-1/2 space-y-2">
|
||||
<h2 className="text:base md:text-2xl">Investment Amount</h2>
|
||||
<Input
|
||||
className="w-52"
|
||||
type="number"
|
||||
placeholder="min $500"
|
||||
min={500}
|
||||
/>
|
||||
</div>
|
||||
<Separator className="my-4" />
|
||||
|
||||
<div className="w-full space-y-2">
|
||||
<h2 className="text:base md:text-2xl">Payment Information</h2>
|
||||
<CardsPaymentMethod />
|
||||
</div>
|
||||
<Separator className="my-4" />
|
||||
|
||||
<div className=" md:w-2/3 space-y-2">
|
||||
<h2 className="text-2xl">Terms and Services</h2>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Select</TableHead>
|
||||
<TableHead>Term</TableHead>
|
||||
<TableHead>Description</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{term_data.map((item, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={checkedTerms[index]}
|
||||
onChange={() => handleCheckboxChange(index)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>{item.term}</TableCell>
|
||||
<TableCell>{item.description}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button className="mt-4">Invest</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Are you absolutely sure?</DialogTitle>
|
||||
<DialogDescription>
|
||||
This action cannot be undone. This will permanently!
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter className="sm:justify-start">
|
||||
<Button type="submit" onClick={handleTermServiceClick}>
|
||||
Confirm
|
||||
</Button>
|
||||
<DialogClose asChild>
|
||||
<Button type="button" variant="secondary">
|
||||
Close
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</DialogFooter>
|
||||
{error && (
|
||||
<p className="text-red-500 mt-2 text-lg font-bold">{error}</p>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
18
src/app/(investment)/payment-success/page.tsx
Normal file
18
src/app/(investment)/payment-success/page.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default function PaymentSuccess({ searchParams: { amount } }: { searchParams: { amount: string } }) {
|
||||
if (!amount) {
|
||||
redirect("/");
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="max-w-6xl mx-auto p-10 text-white text-center border m-10 rounded-md bg-gradient-to-tr from-blue-500 to-purple-500">
|
||||
<div className="mb-10">
|
||||
<h1 className="text-4xl font-extrabold mb-2">Thank you!</h1>
|
||||
<h2 className="text-2xl">You successfully sent</h2>
|
||||
|
||||
<div className="bg-white p-2 rounded-md text-purple-500 mt-5 text-4xl font-bold">${amount}</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
25
src/app/api/create-payment-intent/route.ts
Normal file
25
src/app/api/create-payment-intent/route.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
|
||||
|
||||
// POST 127.0.0.1:8000/api/create-payment-intetnt
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { amount } = await request.json();
|
||||
|
||||
const paymentIntent = await stripe.paymentIntents.create({
|
||||
amount: amount,
|
||||
currency: "usd",
|
||||
automatic_payment_methods: { enabled: true },
|
||||
});
|
||||
|
||||
return NextResponse.json({ clientSecret: paymentIntent.client_secret });
|
||||
} catch (error) {
|
||||
console.error("Internal Error:", error);
|
||||
// Handle other errors (e.g., network issues, parsing errors)
|
||||
return NextResponse.json(
|
||||
{ error: `Internal Server Error: ${error}` },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -25,15 +25,15 @@ const TopProjects = async () => {
|
||||
{topProjectsData.map((project) => (
|
||||
<Link href={`/deals/${project.id}`} key={project.id}>
|
||||
<ProjectCard
|
||||
name={project.projectName}
|
||||
description={project.projectShortDescription}
|
||||
imageUri={project.cardImage}
|
||||
joinDate={new Date(project.publishedTime).toLocaleDateString()}
|
||||
location={project.Business.location}
|
||||
tags={project.ItemTag.map((item) => item.Tag.value)}
|
||||
minInvestment={project.ProjectInvestmentDetail[0]?.minInvestment || 0}
|
||||
name={project.project_name}
|
||||
description={project.project_short_description}
|
||||
imageUri={project.card_image_url}
|
||||
joinDate={new Date(project.published_time).toLocaleDateString()}
|
||||
location={project.business.location}
|
||||
tags={project.item_tag.map((item) => item.tag.value)}
|
||||
minInvestment={project.project_investment_detail[0]?.min_investment || 0}
|
||||
totalInvestor={0}
|
||||
totalRaised={project.ProjectInvestmentDetail[0]?.totalInvestment || 0}
|
||||
totalRaised={project.project_investment_detail[0]?.total_investment || 0}
|
||||
/>
|
||||
</Link>
|
||||
))}
|
||||
|
||||
5
src/lib/convertToSubcurrency.tsx
Normal file
5
src/lib/convertToSubcurrency.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
function convertToSubcurrency(amount: number, factor = 100) {
|
||||
return Math.round(amount * factor);
|
||||
}
|
||||
|
||||
export default convertToSubcurrency;
|
||||
@ -1,16 +1,15 @@
|
||||
import { SupabaseClient } from "@supabase/supabase-js";
|
||||
|
||||
|
||||
function getAllTagsQuery(client: SupabaseClient) {
|
||||
return client.from("Tag").select("id, value");
|
||||
return client.from("tag").select("id, value");
|
||||
}
|
||||
|
||||
function getALlFundedStatusQuery(client: SupabaseClient) {
|
||||
return client.from("FundedStatus").select("id, value, description");
|
||||
return client.from("funded_status").select("id, value, description");
|
||||
}
|
||||
|
||||
function getAllBusinessTypeQuery(client: SupabaseClient) {
|
||||
return client.from("BusinessType").select("id, value, description");
|
||||
return client.from("business_type").select("id, value, description");
|
||||
}
|
||||
|
||||
export { getAllTagsQuery, getALlFundedStatusQuery, getAllBusinessTypeQuery };
|
||||
export { getAllBusinessTypeQuery, getALlFundedStatusQuery, getAllTagsQuery };
|
||||
|
||||
@ -1,35 +1,38 @@
|
||||
import { SupabaseClient } from "@supabase/supabase-js";
|
||||
|
||||
async function getTopProjects(client: SupabaseClient, numberOfRecords: number = 4) {
|
||||
async function getTopProjects(
|
||||
client: SupabaseClient,
|
||||
numberOfRecords: number = 4,
|
||||
) {
|
||||
try {
|
||||
const { data, error } = await client
|
||||
.from("Project")
|
||||
.from("project")
|
||||
.select(
|
||||
`
|
||||
id,
|
||||
projectName,
|
||||
businessId,
|
||||
publishedTime,
|
||||
projectShortDescription,
|
||||
cardImage,
|
||||
ProjectInvestmentDetail (
|
||||
minInvestment,
|
||||
totalInvestment,
|
||||
targetInvestment,
|
||||
investmentDeadline
|
||||
project_name,
|
||||
business_id,
|
||||
published_time,
|
||||
project_short_description,
|
||||
card_image_url,
|
||||
project_investment_detail (
|
||||
min_investment,
|
||||
total_investment,
|
||||
target_investment,
|
||||
investment_deadline
|
||||
),
|
||||
ItemTag (
|
||||
Tag (
|
||||
item_tag (
|
||||
tag (
|
||||
id,
|
||||
value
|
||||
)
|
||||
),
|
||||
Business (
|
||||
business (
|
||||
location
|
||||
)
|
||||
`
|
||||
`,
|
||||
)
|
||||
.order("publishedTime", { ascending: false })
|
||||
.order("published_time", { ascending: false })
|
||||
.limit(numberOfRecords);
|
||||
|
||||
if (error) {
|
||||
@ -42,31 +45,53 @@ async function getTopProjects(client: SupabaseClient, numberOfRecords: number =
|
||||
console.error("Unexpected error:", err);
|
||||
return { data: null, error: "An unexpected error occurred." };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getProjectData(client: SupabaseClient, projectId: number) {
|
||||
const query = client.from("Project").select(
|
||||
function getProjectDataQuery(client: SupabaseClient, projectId: number) {
|
||||
return client.from("project").select(
|
||||
`
|
||||
project_name:projectName,
|
||||
project_short_description:projectShortDescription,
|
||||
project_description:projectDescription,
|
||||
published_time:publishedTime,
|
||||
...ProjectInvestmentDetail!inner (
|
||||
min_investment:minInvestment,
|
||||
total_investment:totalInvestment,
|
||||
target_investment:targetInvestment,
|
||||
investment_deadline:investmentDeadline
|
||||
project_name,
|
||||
project_short_description,
|
||||
project_description,
|
||||
published_time,
|
||||
...project_investment_detail!inner (
|
||||
min_investment,
|
||||
total_investment,
|
||||
target_investment,
|
||||
investment_deadline
|
||||
),
|
||||
tags:ItemTag!inner (
|
||||
...Tag!inner (
|
||||
tags:item_tag!inner (
|
||||
...tag!inner (
|
||||
tag_name:value
|
||||
)
|
||||
)
|
||||
`
|
||||
).eq("id", projectId).single()
|
||||
`,
|
||||
).eq("id", projectId).single();
|
||||
}
|
||||
|
||||
const {data, error} = await query;
|
||||
return { data, error }
|
||||
async function getProjectData(client: SupabaseClient, projectId: number) {
|
||||
const query = client.from("project").select(
|
||||
`
|
||||
project_name,
|
||||
project_short_description,
|
||||
project_description,
|
||||
published_time,
|
||||
...project_investment_detail!inner (
|
||||
min_investment,
|
||||
total_investment,
|
||||
target_investment,
|
||||
investment_deadline
|
||||
),
|
||||
tags:item_tag!inner (
|
||||
...tag!inner (
|
||||
tag_name:value
|
||||
)
|
||||
)
|
||||
`,
|
||||
).eq("id", projectId).single();
|
||||
|
||||
const { data, error } = await query;
|
||||
return { data, error };
|
||||
}
|
||||
|
||||
export interface FilterParams {
|
||||
@ -79,43 +104,54 @@ export interface FilterParams {
|
||||
}
|
||||
|
||||
export interface FilterProjectQueryParams extends FilterParams {
|
||||
page: number,
|
||||
pageSize: number
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
function searchProjectsQuery(client: SupabaseClient, {searchTerm, tagsFilter, projectStatus, businessTypeFilter, sortByTimeFilter, page = 1, pageSize = 4}: FilterProjectQueryParams) {
|
||||
function searchProjectsQuery(
|
||||
client: SupabaseClient,
|
||||
{
|
||||
searchTerm,
|
||||
tagsFilter,
|
||||
projectStatus,
|
||||
businessTypeFilter,
|
||||
sortByTimeFilter,
|
||||
page = 1,
|
||||
pageSize = 4,
|
||||
}: FilterProjectQueryParams,
|
||||
) {
|
||||
const start = (page - 1) * pageSize;
|
||||
const end = start + pageSize - 1;
|
||||
|
||||
let query = client.from("Project").select(
|
||||
let query = client.from("project").select(
|
||||
`
|
||||
project_id:id,
|
||||
project_name:projectName,
|
||||
published_time:publishedTime,
|
||||
project_short_description:projectShortDescription,
|
||||
card_image_url:cardImage,
|
||||
...ProjectStatus!Project_projectStatusId_fkey!inner (
|
||||
project_name,
|
||||
published_time,
|
||||
project_short_description,
|
||||
card_image_url,
|
||||
...project_status!project_project_status_id_fkey!inner (
|
||||
project_status:value
|
||||
),
|
||||
...ProjectInvestmentDetail!inner (
|
||||
min_investment:minInvestment,
|
||||
total_investment:totalInvestment,
|
||||
target_investment:targetInvestment,
|
||||
investment_deadline:investmentDeadline
|
||||
...project_investment_detail!inner (
|
||||
min_investment,
|
||||
total_investment,
|
||||
target_investment,
|
||||
investment_deadline
|
||||
),
|
||||
tags:ItemTag!inner (
|
||||
...Tag!inner (
|
||||
tags:item_tag!inner (
|
||||
...tag!inner (
|
||||
tag_name:value
|
||||
)
|
||||
),
|
||||
...Business!inner (
|
||||
...businessType!inner (
|
||||
...business!inner (
|
||||
...business_type!inner (
|
||||
business_type:value
|
||||
),
|
||||
business_location:location
|
||||
)
|
||||
`
|
||||
).order("publishedTime", { ascending: false }).range(start, end)
|
||||
`,
|
||||
).order("published_time", { ascending: false }).range(start, end);
|
||||
|
||||
if (sortByTimeFilter === "all") {
|
||||
sortByTimeFilter = undefined;
|
||||
@ -134,24 +170,27 @@ function searchProjectsQuery(client: SupabaseClient, {searchTerm, tagsFilter, pr
|
||||
}
|
||||
|
||||
if (searchTerm) {
|
||||
query = query.ilike('projectName', `%${searchTerm}%`)
|
||||
query = query.ilike("project_name", `%${searchTerm}%`);
|
||||
}
|
||||
|
||||
if (tagsFilter) {
|
||||
query = query.in('ItemTag.Tag.value', tagsFilter)
|
||||
query = query.in("item_tag.tag.value", tagsFilter);
|
||||
}
|
||||
|
||||
if (projectStatus) {
|
||||
query = query.eq("ProjectStatus.value", projectStatus)
|
||||
query = query.eq("project_status.value", projectStatus);
|
||||
}
|
||||
|
||||
if (businessTypeFilter) {
|
||||
query = query.eq("Business.businessType.value", businessTypeFilter)
|
||||
query = query.eq("business.business_type.value", businessTypeFilter);
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
|
||||
export { getTopProjects, getProjectData, searchProjectsQuery };
|
||||
|
||||
export {
|
||||
getProjectData,
|
||||
getProjectDataQuery,
|
||||
getTopProjects,
|
||||
searchProjectsQuery,
|
||||
};
|
||||
|
||||
@ -1,35 +1,44 @@
|
||||
import { SupabaseClient } from "@supabase/supabase-js";
|
||||
|
||||
function getBusinesses(client: SupabaseClient, query: string | null) {
|
||||
return client.from("Business").select("id, businessName, joinedDate").ilike("businessName", `%${query}%`);
|
||||
return client.from("business").select("id, business_name, joined_date").ilike(
|
||||
"business_name",
|
||||
`%${query}%`,
|
||||
);
|
||||
}
|
||||
|
||||
function getProjects(client: SupabaseClient, businessIds: string[]) {
|
||||
return client
|
||||
.from("Project")
|
||||
.from("project")
|
||||
.select(
|
||||
`
|
||||
id,
|
||||
projectName,
|
||||
businessId,
|
||||
publishedTime,
|
||||
projectShortDescription,
|
||||
ProjectInvestmentDetail (
|
||||
minInvestment,
|
||||
totalInvestment,
|
||||
targetInvestment
|
||||
project_name,
|
||||
business_id,
|
||||
published_time,
|
||||
project_short_description,
|
||||
project_investment_detail (
|
||||
min_investment,
|
||||
total_investment,
|
||||
target_investment
|
||||
)
|
||||
`
|
||||
`,
|
||||
)
|
||||
.in("businessId", businessIds);
|
||||
.in("business_id", businessIds);
|
||||
}
|
||||
|
||||
function getTags(client: SupabaseClient, projectIds: string[]) {
|
||||
return client.from("ItemTag").select("itemId, Tag (value)").in("itemId", projectIds);
|
||||
return client.from("item_tag").select("item_id, tag (value)").in(
|
||||
"item_id",
|
||||
projectIds,
|
||||
);
|
||||
}
|
||||
|
||||
function getInvestmentCounts(client: SupabaseClient, projectIds: string[]) {
|
||||
return client.from("InvestmentDeal").select("*", { count: "exact", head: true }).in("projectId", projectIds);
|
||||
return client.from("investment_deal").select("*", {
|
||||
count: "exact",
|
||||
head: true,
|
||||
}).in("project_id", projectIds);
|
||||
}
|
||||
|
||||
export { getBusinesses, getProjects, getTags, getInvestmentCounts };
|
||||
export { getBusinesses, getInvestmentCounts, getProjects, getTags };
|
||||
|
||||
@ -3,7 +3,7 @@ import { SupabaseClient } from "@supabase/supabase-js";
|
||||
async function getUserProfile(client: SupabaseClient, userId: string) {
|
||||
try {
|
||||
const { data, error } = await client
|
||||
.from("Profiles")
|
||||
.from("profiles")
|
||||
.select("updated_at, username, full_name, avatar_url, website, bio")
|
||||
.eq("id", userId)
|
||||
.single();
|
||||
|
||||
11
src/lib/stripe/config.ts
Normal file
11
src/lib/stripe/config.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { Stripe, loadStripe } from '@stripe/stripe-js';
|
||||
|
||||
let stripePromise: Promise<Stripe | null>;
|
||||
const getStripe = () => {
|
||||
if (!stripePromise) {
|
||||
stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);
|
||||
}
|
||||
return stripePromise;
|
||||
};
|
||||
|
||||
export default getStripe;
|
||||
@ -14,6 +14,6 @@ export const config = {
|
||||
* - favicon.ico (favicon file)
|
||||
* Feel free to modify this pattern to include more paths.
|
||||
*/
|
||||
"/((?!_next/static|_next/image|$|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
|
||||
"/((?!_next/static|_next/image|$|favicon.ico|payment-success|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
|
||||
],
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user