diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..af998b8 --- /dev/null +++ b/.env.example @@ -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 \ No newline at end of file diff --git a/.env.local.example b/.env.local.example index 06ff0e5..af998b8 100644 --- a/.env.local.example +++ b/.env.local.example @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/README.md b/README.md index c403366..9d47ac0 100644 --- a/README.md +++ b/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) diff --git a/package-lock.json b/package-lock.json index fb90d3a..dac6371 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index bfa2480..ec5c536 100644 --- a/package.json +++ b/package.json @@ -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" diff --git a/src/app/(investment)/invest/[id]/checkoutPage.tsx b/src/app/(investment)/invest/[id]/checkoutPage.tsx new file mode 100644 index 0000000..0563e6a --- /dev/null +++ b/src/app/(investment)/invest/[id]/checkoutPage.tsx @@ -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(); + 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) => { + 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 ( +
+
+ + Loading... + +
+
+ ); + } + + return ( +
+ {clientSecret && } + + {errorMessage &&
{errorMessage}
} + + {/* Trigger the dialog when the "Pay" button is clicked */} + + + + + + + Are you sure you want to proceed with the investment? + This action cannot be undone, and the investment will be confirmed. + + +
+ +
+ + + +
+ {errorMessage &&

{errorMessage}

} +
+
+
+ ); +}; + +export default CheckoutPage; diff --git a/src/app/(investment)/invest/[id]/page.tsx b/src/app/(investment)/invest/[id]/page.tsx new file mode 100644 index 0000000..04dc0d2 --- /dev/null +++ b/src/app/(investment)/invest/[id]/page.tsx @@ -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(""); + + 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 ( +
+

Invest on ${projectData?.project_name}

+ +
+
+
+

Investment Amount

+ setInvestAmount(Number(e.currentTarget.value))} + /> +
+ + +
+

Terms and Services

+ + + + Select + Term + Description + + + + {term_data.map((item, index) => ( + + + handleCheckboxChange(index)} /> + + {item.term} + {item.description} + + ))} + +
+
+ + +
+

Payment Information

+
+ + + +
+
+
+
+ ); +} diff --git a/src/app/(investment)/invest/page.tsx b/src/app/(investment)/invest/page.tsx deleted file mode 100644 index 9f72caa..0000000 --- a/src/app/(investment)/invest/page.tsx +++ /dev/null @@ -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 ( -
-

Invest on NVIDIA

- - -
-
-

Investment Amount

- -
- - -
-

Payment Information

- -
- - -
-

Terms and Services

- - - - Select - Term - Description - - - - {term_data.map((item, index) => ( - - - handleCheckboxChange(index)} - /> - - {item.term} - {item.description} - - ))} - -
-
- - - - - - - - Are you absolutely sure? - - This action cannot be undone. This will permanently! - - - - - - - - - {error && ( -

{error}

- )} -
-
-
-
- ); -} diff --git a/src/app/(investment)/payment-success/page.tsx b/src/app/(investment)/payment-success/page.tsx new file mode 100644 index 0000000..b297416 --- /dev/null +++ b/src/app/(investment)/payment-success/page.tsx @@ -0,0 +1,18 @@ +import { redirect } from "next/navigation"; + +export default function PaymentSuccess({ searchParams: { amount } }: { searchParams: { amount: string } }) { + if (!amount) { + redirect("/"); + } + + return ( +
+
+

Thank you!

+

You successfully sent

+ +
${amount}
+
+
+ ); +} diff --git a/src/app/api/create-payment-intent/route.ts b/src/app/api/create-payment-intent/route.ts new file mode 100644 index 0000000..6373b40 --- /dev/null +++ b/src/app/api/create-payment-intent/route.ts @@ -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 } + ); + } +} \ No newline at end of file diff --git a/src/app/business/apply/page.tsx b/src/app/business/apply/page.tsx index b4dbe73..50c1038 100644 --- a/src/app/business/apply/page.tsx +++ b/src/app/business/apply/page.tsx @@ -831,4 +831,4 @@ export default function Apply() { ); -} +} \ No newline at end of file diff --git a/src/app/page.tsx b/src/app/page.tsx index eebac2c..4e03c19 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -25,15 +25,15 @@ const TopProjects = async () => { {topProjectsData.map((project) => ( 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} /> ))} diff --git a/src/lib/convertToSubcurrency.tsx b/src/lib/convertToSubcurrency.tsx new file mode 100644 index 0000000..7293b68 --- /dev/null +++ b/src/lib/convertToSubcurrency.tsx @@ -0,0 +1,5 @@ +function convertToSubcurrency(amount: number, factor = 100) { + return Math.round(amount * factor); +} + +export default convertToSubcurrency; diff --git a/src/lib/data/dropdownQuery.ts b/src/lib/data/dropdownQuery.ts index f6ac3dc..2216190 100644 --- a/src/lib/data/dropdownQuery.ts +++ b/src/lib/data/dropdownQuery.ts @@ -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 }; \ No newline at end of file +export { getAllBusinessTypeQuery, getALlFundedStatusQuery, getAllTagsQuery }; diff --git a/src/lib/data/projectQuery.ts b/src/lib/data/projectQuery.ts index c6d0d71..f1a5d3c 100644 --- a/src/lib/data/projectQuery.ts +++ b/src/lib/data/projectQuery.ts @@ -1,72 +1,97 @@ import { SupabaseClient } from "@supabase/supabase-js"; -async function getTopProjects(client: SupabaseClient, numberOfRecords: number = 4) { - try { - const { data, error } = await client - .from("Project") - .select( - ` +async function getTopProjects( + client: SupabaseClient, + numberOfRecords: number = 4, +) { + try { + const { data, error } = await client + .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 }) - .limit(numberOfRecords); + `, + ) + .order("published_time", { ascending: false }) + .limit(numberOfRecords); - if (error) { - console.error("Error fetching top projects:", error.message); - return { data: null, error: error.message }; - } - - return { data, error: null }; - } catch (err) { - console.error("Unexpected error:", err); - return { data: null, error: "An unexpected error occurred." }; + if (error) { + console.error("Error fetching top projects:", error.message); + return { data: null, error: error.message }; } - } -async function getProjectData(client: SupabaseClient, projectId: number) { - const query = client.from("Project").select( + return { data, error: null }; + } catch (err) { + console.error("Unexpected error:", err); + return { data: null, error: "An unexpected error occurred." }; + } +} + +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, +}; diff --git a/src/lib/data/query.ts b/src/lib/data/query.ts index 2af3209..6689a34 100644 --- a/src/lib/data/query.ts +++ b/src/lib/data/query.ts @@ -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 }; \ No newline at end of file +export { getBusinesses, getInvestmentCounts, getProjects, getTags }; diff --git a/src/lib/data/userQuery.ts b/src/lib/data/userQuery.ts index 5ddd8f0..f693786 100644 --- a/src/lib/data/userQuery.ts +++ b/src/lib/data/userQuery.ts @@ -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(); diff --git a/src/lib/stripe/config.ts b/src/lib/stripe/config.ts new file mode 100644 index 0000000..3b5bd9c --- /dev/null +++ b/src/lib/stripe/config.ts @@ -0,0 +1,11 @@ +import { Stripe, loadStripe } from '@stripe/stripe-js'; + +let stripePromise: Promise; +const getStripe = () => { + if (!stripePromise) { + stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!); + } + return stripePromise; +}; + +export default getStripe; \ No newline at end of file diff --git a/src/middleware.ts b/src/middleware.ts index 9226aa2..eeadf78 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -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)$).*)", ], };