mirror of
https://github.com/Sosokker/B2D-Ventures.git
synced 2025-12-18 13:34:06 +01:00
Merge branch 'main' into back-end
This commit is contained in:
commit
40f2557de4
512
package-lock.json
generated
512
package-lock.json
generated
@ -9,12 +9,14 @@
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^3.9.0",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.2",
|
||||
"@radix-ui/react-avatar": "^1.1.0",
|
||||
"@radix-ui/react-dialog": "^1.1.2",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||
"@radix-ui/react-hover-card": "^1.1.1",
|
||||
"@radix-ui/react-label": "^2.1.0",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.0",
|
||||
"@radix-ui/react-popover": "^1.1.2",
|
||||
"@radix-ui/react-progress": "^1.1.0",
|
||||
"@radix-ui/react-radio-group": "^1.2.1",
|
||||
"@radix-ui/react-select": "^2.1.1",
|
||||
@ -33,6 +35,7 @@
|
||||
"b2d-ventures": "file:",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "1.0.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"embla-carousel-react": "^8.2.0",
|
||||
@ -44,6 +47,7 @@
|
||||
"react-dom": "^18",
|
||||
"react-hook-form": "^7.53.0",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"react-lottie": "^1.2.4",
|
||||
"react-markdown": "^9.0.1",
|
||||
"recharts": "^2.12.7",
|
||||
"stripe": "^17.1.0",
|
||||
@ -60,6 +64,8 @@
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"@types/react-fade-in": "^2.0.2",
|
||||
"@types/react-lottie": "^1.2.10",
|
||||
"@types/react-select-country-list": "^2.2.3",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.5",
|
||||
@ -935,6 +941,33 @@
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz",
|
||||
"integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA=="
|
||||
},
|
||||
"node_modules/@radix-ui/react-alert-dialog": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.2.tgz",
|
||||
"integrity": "sha512-eGSlLzPhKO+TErxkiGcCZGuvbVMnLA1MTnyBksGOeGRGkxHiiJUujsjmNTdWTm4iHVSRaUao9/4Ur671auMghQ==",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.0",
|
||||
"@radix-ui/react-compose-refs": "1.1.0",
|
||||
"@radix-ui/react-context": "1.1.1",
|
||||
"@radix-ui/react-dialog": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-slot": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-arrow": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz",
|
||||
@ -1333,6 +1366,42 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.2.tgz",
|
||||
"integrity": "sha512-u2HRUyWW+lOiA2g0Le0tMmT55FGOEWHwPFt1EPfbLly7uXQExFo5duNKqG2DzmFXIdqOeNd+TpE8baHWJCyP9w==",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.0",
|
||||
"@radix-ui/react-compose-refs": "1.1.0",
|
||||
"@radix-ui/react-context": "1.1.1",
|
||||
"@radix-ui/react-dismissable-layer": "1.1.1",
|
||||
"@radix-ui/react-focus-guards": "1.1.1",
|
||||
"@radix-ui/react-focus-scope": "1.1.0",
|
||||
"@radix-ui/react-id": "1.1.0",
|
||||
"@radix-ui/react-popper": "1.2.0",
|
||||
"@radix-ui/react-portal": "1.1.2",
|
||||
"@radix-ui/react-presence": "1.1.1",
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-slot": "1.1.0",
|
||||
"@radix-ui/react-use-controllable-state": "1.1.0",
|
||||
"aria-hidden": "^1.1.1",
|
||||
"react-remove-scroll": "2.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popper": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz",
|
||||
@ -2311,6 +2380,39 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-fade-in": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-fade-in/-/react-fade-in-2.0.2.tgz",
|
||||
"integrity": "sha512-JdyLYFtyvqDP7mqnKaAyuYD+VMtzAHbUf3kumNQV5QALxjBGmb95HXD0uug1bGol053dtV5yO3NNpGHOMj413g==",
|
||||
"deprecated": "This is a stub types definition. react-fade-in provides its own type definitions, so you do not need this installed.",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"react-fade-in": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-fade-in/node_modules/react": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
|
||||
"integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-fade-in/node_modules/react-fade-in": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-fade-in/-/react-fade-in-2.0.1.tgz",
|
||||
"integrity": "sha512-oqS/WT4znaXEHmL+yo0IDUDY7uC9K4RP35j1SdRUEBspR09B2iIC0i8oJ28tPOr6Ez/L2aktF9p89j+DbsTVNw==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"react": "^16.8 || 17"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-loadable": {
|
||||
"version": "5.5.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-loadable/-/react-loadable-5.5.11.tgz",
|
||||
@ -2322,6 +2424,15 @@
|
||||
"@types/webpack": "^4"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-lottie": {
|
||||
"version": "1.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-lottie/-/react-lottie-1.2.10.tgz",
|
||||
"integrity": "sha512-rCd1p3US4ELKJlqwVnP0h5b24zt5p9OCvKUoNpYExLqwbFZMWEiJ6EGLMmH7nmq5V7KomBIbWO2X/XRFsL0vCA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-select-country-list": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-select-country-list/-/react-select-country-list-2.2.3.tgz",
|
||||
@ -2882,6 +2993,20 @@
|
||||
"resolved": "",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/babel-runtime": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
|
||||
"integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
|
||||
"dependencies": {
|
||||
"core-js": "^2.4.0",
|
||||
"regenerator-runtime": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-runtime/node_modules/regenerator-runtime": {
|
||||
"version": "0.11.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
|
||||
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
|
||||
},
|
||||
"node_modules/bail": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
|
||||
@ -3153,6 +3278,366 @@
|
||||
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cmdk": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.0.0.tgz",
|
||||
"integrity": "sha512-gDzVf0a09TvoJ5jnuPvygTB77+XdOSwEmJ88L6XPFPlv7T3RxbP9jgenfylrAMD0+Le1aO0nVjQUzl2g+vjz5Q==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-dialog": "1.0.5",
|
||||
"@radix-ui/react-primitive": "1.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cmdk/node_modules/@radix-ui/primitive": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz",
|
||||
"integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10"
|
||||
}
|
||||
},
|
||||
"node_modules/cmdk/node_modules/@radix-ui/react-compose-refs": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz",
|
||||
"integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/cmdk/node_modules/@radix-ui/react-context": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz",
|
||||
"integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/cmdk/node_modules/@radix-ui/react-dialog": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz",
|
||||
"integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/primitive": "1.0.1",
|
||||
"@radix-ui/react-compose-refs": "1.0.1",
|
||||
"@radix-ui/react-context": "1.0.1",
|
||||
"@radix-ui/react-dismissable-layer": "1.0.5",
|
||||
"@radix-ui/react-focus-guards": "1.0.1",
|
||||
"@radix-ui/react-focus-scope": "1.0.4",
|
||||
"@radix-ui/react-id": "1.0.1",
|
||||
"@radix-ui/react-portal": "1.0.4",
|
||||
"@radix-ui/react-presence": "1.0.1",
|
||||
"@radix-ui/react-primitive": "1.0.3",
|
||||
"@radix-ui/react-slot": "1.0.2",
|
||||
"@radix-ui/react-use-controllable-state": "1.0.1",
|
||||
"aria-hidden": "^1.1.1",
|
||||
"react-remove-scroll": "2.5.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/cmdk/node_modules/@radix-ui/react-dismissable-layer": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz",
|
||||
"integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/primitive": "1.0.1",
|
||||
"@radix-ui/react-compose-refs": "1.0.1",
|
||||
"@radix-ui/react-primitive": "1.0.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.0.1",
|
||||
"@radix-ui/react-use-escape-keydown": "1.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/cmdk/node_modules/@radix-ui/react-focus-guards": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz",
|
||||
"integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/cmdk/node_modules/@radix-ui/react-focus-scope": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz",
|
||||
"integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/react-compose-refs": "1.0.1",
|
||||
"@radix-ui/react-primitive": "1.0.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/cmdk/node_modules/@radix-ui/react-id": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz",
|
||||
"integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/react-use-layout-effect": "1.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/cmdk/node_modules/@radix-ui/react-portal": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz",
|
||||
"integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/react-primitive": "1.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/cmdk/node_modules/@radix-ui/react-presence": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz",
|
||||
"integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/react-compose-refs": "1.0.1",
|
||||
"@radix-ui/react-use-layout-effect": "1.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/cmdk/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz",
|
||||
"integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/react-slot": "1.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/cmdk/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
|
||||
"integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/react-compose-refs": "1.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/cmdk/node_modules/@radix-ui/react-use-callback-ref": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz",
|
||||
"integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/cmdk/node_modules/@radix-ui/react-use-controllable-state": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz",
|
||||
"integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/react-use-callback-ref": "1.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/cmdk/node_modules/@radix-ui/react-use-escape-keydown": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz",
|
||||
"integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/react-use-callback-ref": "1.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/cmdk/node_modules/@radix-ui/react-use-layout-effect": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz",
|
||||
"integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/cmdk/node_modules/react-remove-scroll": {
|
||||
"version": "2.5.5",
|
||||
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz",
|
||||
"integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==",
|
||||
"dependencies": {
|
||||
"react-remove-scroll-bar": "^2.3.3",
|
||||
"react-style-singleton": "^2.2.1",
|
||||
"tslib": "^2.1.0",
|
||||
"use-callback-ref": "^1.3.0",
|
||||
"use-sidecar": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
@ -3213,6 +3698,13 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/core-js": {
|
||||
"version": "2.6.12",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
|
||||
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
|
||||
"deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
|
||||
"hasInstallScript": true
|
||||
},
|
||||
"node_modules/core-js-pure": {
|
||||
"version": "3.38.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.38.1.tgz",
|
||||
@ -5675,6 +6167,11 @@
|
||||
"loose-envify": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/lottie-web": {
|
||||
"version": "5.12.2",
|
||||
"resolved": "https://registry.npmjs.org/lottie-web/-/lottie-web-5.12.2.tgz",
|
||||
"integrity": "sha512-uvhvYPC8kGPjXT3MyKMrL3JitEAmDMp30lVkuq/590Mw9ok6pWcFCwXJveo0t5uqYw1UREQHofD+jVpdjBv8wg=="
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "10.4.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||
@ -7212,6 +7709,21 @@
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
},
|
||||
"node_modules/react-lottie": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/react-lottie/-/react-lottie-1.2.4.tgz",
|
||||
"integrity": "sha512-kBGxI+MIZGBf4wZhNCWwHkMcVP+kbpmrLWH/SkO0qCKc7D7eSPcxQbfpsmsCo8v2KCBYjuGSou+xTqK44D/jMg==",
|
||||
"dependencies": {
|
||||
"babel-runtime": "^6.26.0",
|
||||
"lottie-web": "^5.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"npm": "^3.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=15.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-markdown": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz",
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^3.9.0",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.2",
|
||||
"@radix-ui/react-avatar": "^1.1.0",
|
||||
"@radix-ui/react-checkbox": "^1.1.2",
|
||||
"@radix-ui/react-dialog": "^1.1.2",
|
||||
@ -17,6 +18,7 @@
|
||||
"@radix-ui/react-hover-card": "^1.1.1",
|
||||
"@radix-ui/react-label": "^2.1.0",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.0",
|
||||
"@radix-ui/react-popover": "^1.1.2",
|
||||
"@radix-ui/react-progress": "^1.1.0",
|
||||
"@radix-ui/react-radio-group": "^1.2.1",
|
||||
"@radix-ui/react-select": "^2.1.1",
|
||||
@ -35,6 +37,7 @@
|
||||
"b2d-ventures": "file:",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "1.0.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"embla-carousel-react": "^8.2.0",
|
||||
@ -46,6 +49,7 @@
|
||||
"react-dom": "^18",
|
||||
"react-hook-form": "^7.53.0",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"react-lottie": "^1.2.4",
|
||||
"react-markdown": "^9.0.1",
|
||||
"recharts": "^2.12.7",
|
||||
"stripe": "^17.1.0",
|
||||
@ -62,6 +66,8 @@
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"@types/react-fade-in": "^2.0.2",
|
||||
"@types/react-lottie": "^1.2.10",
|
||||
"@types/react-select-country-list": "^2.2.3",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.5",
|
||||
|
||||
4836
pnpm-lock.yaml
4836
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -125,7 +125,7 @@ export default async function ProjectDealPage({ params }: { params: { id: number
|
||||
<CardDescription></CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="prose prose-sm max-w-none">
|
||||
<div className="prose prose-sm max-w-none ">
|
||||
<ReactMarkdown>{projectData?.project_description || "No pitch available."}</ReactMarkdown>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
100
src/app/api/generalApi.ts
Normal file
100
src/app/api/generalApi.ts
Normal file
@ -0,0 +1,100 @@
|
||||
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
|
||||
import Swal from "sweetalert2";
|
||||
|
||||
const supabase = createSupabaseClient();
|
||||
|
||||
async function checkFolderExists(bucketName: string, filePath: string) {
|
||||
const { data, error } = await supabase.storage
|
||||
.from(bucketName)
|
||||
.list(filePath);
|
||||
|
||||
if (error) {
|
||||
console.error(`Error checking for folder: ${error.message}`);
|
||||
}
|
||||
return { folderData: data, folderError: error };
|
||||
}
|
||||
|
||||
async function clearFolder(
|
||||
bucketName: string,
|
||||
folderData: any[],
|
||||
filePath: string
|
||||
) {
|
||||
const errors: string[] = [];
|
||||
|
||||
for (const fileItem of folderData) {
|
||||
const { error } = await supabase.storage
|
||||
.from(bucketName)
|
||||
.remove([`${filePath}/${fileItem.name}`]);
|
||||
|
||||
if (error) {
|
||||
errors.push(`Error removing file (${fileItem.name}): ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
async function uploadToFolder(
|
||||
bucketName: string,
|
||||
filePath: string,
|
||||
file: File
|
||||
) {
|
||||
const { data, error } = await supabase.storage
|
||||
.from(bucketName)
|
||||
.upload(filePath, file, { upsert: true });
|
||||
|
||||
if (error) {
|
||||
console.error(`Error uploading file: ${error.message}`);
|
||||
}
|
||||
return { uploadData: data, uploadError: error };
|
||||
}
|
||||
|
||||
export async function uploadFile(
|
||||
file: File,
|
||||
bucketName: string,
|
||||
filePath: string
|
||||
) {
|
||||
const errorMessages: string[] = [];
|
||||
|
||||
// check if the folder exists
|
||||
const { folderData, folderError } = await checkFolderExists(
|
||||
bucketName,
|
||||
filePath
|
||||
);
|
||||
if (folderError) {
|
||||
errorMessages.push(`Error checking for folder: ${folderError.message}`);
|
||||
}
|
||||
|
||||
// clear the folder if it exists
|
||||
if (folderData && folderData.length > 0) {
|
||||
const clearErrors = await clearFolder(bucketName, folderData, filePath);
|
||||
errorMessages.push(...clearErrors);
|
||||
}
|
||||
|
||||
// upload the new file if there were no previous errors
|
||||
let uploadData = null;
|
||||
if (errorMessages.length === 0) {
|
||||
const { uploadData: data, uploadError } = await uploadToFolder(
|
||||
bucketName,
|
||||
filePath,
|
||||
file
|
||||
);
|
||||
uploadData = data;
|
||||
|
||||
if (uploadError) {
|
||||
errorMessages.push(`Error uploading file: ${uploadError.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (errorMessages.length > 0) {
|
||||
Swal.fire({
|
||||
icon: "error",
|
||||
title: "Errors occurred",
|
||||
html: errorMessages.join("<br>"),
|
||||
confirmButtonColor: "red",
|
||||
});
|
||||
return { success: false, errors: errorMessages, data: null };
|
||||
}
|
||||
|
||||
return { success: true, errors: null, data: uploadData };
|
||||
}
|
||||
@ -1,26 +1,53 @@
|
||||
"use client";
|
||||
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
|
||||
import { useState } from "react";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { SubmitHandler } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
import BusinessForm from "@/components/BusinessForm";
|
||||
import { businessFormSchema } from "@/types/schemas/application.schema";
|
||||
import Swal from "sweetalert2";
|
||||
import { getCurrentUserID } from "@/app/api/userApi";
|
||||
import { uploadFile } from "@/app/api/generalApi";
|
||||
import { Loader } from "@/components/loading/loader";
|
||||
|
||||
type businessSchema = z.infer<typeof businessFormSchema>;
|
||||
const BUCKET_PITCH_NAME = "business-application";
|
||||
let supabase = createSupabaseClient();
|
||||
|
||||
export default function ApplyBusiness() {
|
||||
const [applyProject, setApplyProject] = useState(false);
|
||||
const alertShownRef = useRef(false);
|
||||
const [success, setSucess] = useState(false);
|
||||
|
||||
const onSubmit: SubmitHandler<businessSchema> = async (data) => {
|
||||
const transformedData = await transformChoice(data);
|
||||
console.log(transformedData);
|
||||
await sendRegistration(transformedData);
|
||||
await sendApplication(transformedData);
|
||||
};
|
||||
const sendRegistration = async (recvData: any) => {
|
||||
const sendApplication = async (recvData: any) => {
|
||||
setSucess(false);
|
||||
const {
|
||||
data: { user },
|
||||
} = await supabase.auth.getUser();
|
||||
// console.log(user?.id);
|
||||
const pitchType = typeof recvData["businessPitchDeck"];
|
||||
if (pitchType === "object") {
|
||||
if (user?.id) {
|
||||
const uploadSuccess = await uploadFile(
|
||||
recvData["businessPitchDeck"],
|
||||
BUCKET_PITCH_NAME,
|
||||
// file structure: userId/fileName
|
||||
`${user?.id}/pitch-file/pitch.md`
|
||||
);
|
||||
|
||||
if (!uploadSuccess) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("file upload successful");
|
||||
} else {
|
||||
console.error("user ID is undefined.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const { data, error } = await supabase
|
||||
.from("business_application")
|
||||
@ -33,13 +60,15 @@ export default function ApplyBusiness() {
|
||||
is_for_sale: recvData["isForSale"],
|
||||
is_generating_revenue: recvData["isGenerating"],
|
||||
is_in_us: recvData["isInUS"],
|
||||
pitch_deck_url: recvData["businessPitchDeck"],
|
||||
pitch_deck_url:
|
||||
pitchType === "string" ? recvData["businessPitchDeck"] : "",
|
||||
money_raised_to_date: recvData["totalRaised"],
|
||||
community_size: recvData["communitySize"],
|
||||
},
|
||||
])
|
||||
.select();
|
||||
console.table(data);
|
||||
setSucess(true);
|
||||
// console.table(data);
|
||||
Swal.fire({
|
||||
icon: error == null ? "success" : "error",
|
||||
title: error == null ? "success" : "Error: " + error.code,
|
||||
@ -55,7 +84,20 @@ export default function ApplyBusiness() {
|
||||
});
|
||||
};
|
||||
|
||||
let supabase = createSupabaseClient();
|
||||
const hasUserApplied = async (userID: string) => {
|
||||
let { data: business, error } = await supabase
|
||||
.from("business")
|
||||
.select("*")
|
||||
.eq("user_id", userID);
|
||||
console.table(business);
|
||||
if (error) {
|
||||
console.error(error);
|
||||
}
|
||||
if (business) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const transformChoice = (data: any) => {
|
||||
// convert any yes and no to true or false
|
||||
const transformedData = Object.entries(data).reduce(
|
||||
@ -78,9 +120,44 @@ export default function ApplyBusiness() {
|
||||
);
|
||||
return transformedData;
|
||||
};
|
||||
useEffect(() => {
|
||||
const fetchUserData = async () => {
|
||||
try {
|
||||
setSucess(false);
|
||||
const userID = await getCurrentUserID();
|
||||
if (userID) {
|
||||
const hasApplied = await hasUserApplied(userID);
|
||||
setSucess(true);
|
||||
if (hasApplied && !alertShownRef.current) {
|
||||
alertShownRef.current = true;
|
||||
Swal.fire({
|
||||
icon: "info",
|
||||
title: "You Already Have an Account",
|
||||
text: "You have already submitted your business application.",
|
||||
confirmButtonText: "OK",
|
||||
allowOutsideClick: false,
|
||||
allowEscapeKey: false,
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
window.location.href = "/";
|
||||
}
|
||||
});
|
||||
}
|
||||
setSucess(false);
|
||||
} else {
|
||||
console.error("User ID is undefined.");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching user ID:", error);
|
||||
}
|
||||
};
|
||||
// setSucess(true);
|
||||
fetchUserData();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Loader isSuccess={success} />
|
||||
<div className="grid grid-flow-row auto-rows-max w-full h-52 md:h-92 bg-gray-100 dark:bg-gray-800 p-5">
|
||||
<h1 className="text-2xl md:text-5xl font-medium md:font-bold justify-self-center md:mt-8">
|
||||
Apply to raise on B2DVentures
|
||||
|
||||
@ -1,39 +1,63 @@
|
||||
import Image from "next/image";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import Link from "next/link";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { ProjectCard } from "@/components/projectCard";
|
||||
import { getTopProjects } from "@/lib/data/projectQuery";
|
||||
import { createSupabaseClient } from "@/lib/supabase/serverComponentClient";
|
||||
import { Suspense } from "react";
|
||||
import { FC } from "react";
|
||||
|
||||
const TopProjects = async () => {
|
||||
const supabase = createSupabaseClient();
|
||||
const { data: topProjectsData, error: topProjectsError } = await getTopProjects(supabase);
|
||||
interface Project {
|
||||
id: number;
|
||||
project_name: string;
|
||||
project_short_description: string;
|
||||
card_image_url: string;
|
||||
published_time: string;
|
||||
business: { location: string }[];
|
||||
project_tag: { tag: { id: number; value: string }[] }[];
|
||||
project_investment_detail: {
|
||||
min_investment: number;
|
||||
total_investment: number;
|
||||
}[];
|
||||
}
|
||||
|
||||
if (topProjectsError) {
|
||||
return <div>Error loading top projects: {topProjectsError}</div>;
|
||||
}
|
||||
interface TopProjectsProps {
|
||||
projects: Project[];
|
||||
}
|
||||
|
||||
if (!topProjectsData || topProjectsData.length === 0) {
|
||||
const TopProjects: FC<TopProjectsProps> = ({ projects }) => {
|
||||
if (!projects || projects.length === 0) {
|
||||
return <div>No top projects available.</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{topProjectsData.map((project) => (
|
||||
{projects.map((project) => (
|
||||
<Link href={`/deals/${project.id}`} key={project.id}>
|
||||
<ProjectCard
|
||||
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}
|
||||
location={project.business[0]?.location || ""}
|
||||
tags={project.project_tag.flatMap(
|
||||
(item: { tag: { id: number; value: string }[] }) =>
|
||||
Array.isArray(item.tag) ? item.tag.map((tag) => tag.value) : []
|
||||
)}
|
||||
minInvestment={
|
||||
project.project_investment_detail[0]?.min_investment || 0
|
||||
}
|
||||
totalInvestor={0}
|
||||
totalRaised={project.project_investment_detail[0]?.total_investment || 0}
|
||||
totalRaised={
|
||||
project.project_investment_detail[0]?.total_investment || 0
|
||||
}
|
||||
/>
|
||||
</Link>
|
||||
))}
|
||||
@ -44,12 +68,18 @@ const TopProjects = async () => {
|
||||
const ProjectsLoader = () => (
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{[...Array(4)].map((_, index) => (
|
||||
<div key={index} className="h-64 bg-gray-200 animate-pulse rounded-lg"></div>
|
||||
<div
|
||||
key={index}
|
||||
className="h-64 bg-gray-200 animate-pulse rounded-lg"
|
||||
></div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default async function Home() {
|
||||
const supabase = createSupabaseClient();
|
||||
const { data: topProjectsData, error: topProjectsError } =
|
||||
await getTopProjects(supabase);
|
||||
|
||||
return (
|
||||
<main>
|
||||
<div className="relative mx-auto">
|
||||
@ -57,9 +87,14 @@ export default async function Home() {
|
||||
<div className="flex flex-row bg-slate-100 dark:bg-gray-800">
|
||||
<div className="container max-w-screen-xl flex flex-col">
|
||||
<span className="mx-20 px-10 py-10">
|
||||
<p className="text-4xl font-bold">Explore the world of ventures</p>
|
||||
<p className="text-4xl font-bold">
|
||||
Explore the world of ventures
|
||||
</p>
|
||||
<span className="text-lg">
|
||||
<p>Unlock opportunities and connect with a community of passionate</p>
|
||||
<p>
|
||||
Unlock opportunities and connect with a community of
|
||||
passionate
|
||||
</p>
|
||||
<p>investors and innovators.</p>
|
||||
<p>Together, we turn ideas into impact.</p>
|
||||
</span>
|
||||
@ -107,11 +142,23 @@ export default async function Home() {
|
||||
</CardHeader>
|
||||
<CardContent className="flex gap-2">
|
||||
<Button className="flex gap-1 border-2 border-border rounded-md p-1 bg-background text-foreground scale-75 md:scale-100">
|
||||
<Image src={"/github.svg"} width={20} height={20} alt="github" className="scale-75 md:scale-100" />
|
||||
<Image
|
||||
src={"/github.svg"}
|
||||
width={20}
|
||||
height={20}
|
||||
alt="github"
|
||||
className="scale-75 md:scale-100"
|
||||
/>
|
||||
Github
|
||||
</Button>
|
||||
<Button className="flex gap-1 border-2 border-border rounded-md p-1 bg-background text-foreground scale-75 md:scale-100">
|
||||
<Image src={"/github.svg"} width={20} height={20} alt="github" className="scale-75 md:scale-100" />
|
||||
<Image
|
||||
src={"/github.svg"}
|
||||
width={20}
|
||||
height={20}
|
||||
alt="github"
|
||||
className="scale-75 md:scale-100"
|
||||
/>
|
||||
Github
|
||||
</Button>
|
||||
</CardContent>
|
||||
@ -123,10 +170,12 @@ export default async function Home() {
|
||||
<div className="flex flex-col px-10">
|
||||
<span className="pb-5">
|
||||
<p className="text-xl md:text-2xl font-bold">Hottest Deals</p>
|
||||
<p className="text-md md:text-lg">The deals attracting the most interest right now</p>
|
||||
<p className="text-md md:text-lg">
|
||||
The deals attracting the most interest right now
|
||||
</p>
|
||||
</span>
|
||||
<Suspense fallback={<ProjectsLoader />}>
|
||||
<TopProjects />
|
||||
<TopProjects projects={topProjectsData || []} />
|
||||
</Suspense>
|
||||
<div className="self-center py-5 scale-75 md:scale-100">
|
||||
<Button>
|
||||
|
||||
@ -1,25 +1,280 @@
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
|
||||
import ProjectForm from "@/components/ProjectForm";
|
||||
import { projectFormSchema } from "@/types/schemas/application.schema";
|
||||
import { z } from "zod";
|
||||
import { SubmitHandler } from "react-hook-form";
|
||||
import Swal from "sweetalert2";
|
||||
import { uploadFile } from "@/app/api/generalApi";
|
||||
import { Loader } from "@/components/loading/loader";
|
||||
import { useState } from "react";
|
||||
import { errors } from "@playwright/test";
|
||||
|
||||
type projectSchema = z.infer<typeof projectFormSchema>;
|
||||
export default function ApplyProject() {
|
||||
const [projectType, setProjectType] = useState<string[]>([]);
|
||||
const [projectPitch, setProjectPitch] = useState("text");
|
||||
const [applyProject, setApplyProject] = useState(false);
|
||||
const [selectedImages, setSelectedImages] = useState<File[]>([]);
|
||||
const [projectPitchFile, setProjectPitchFile] = useState("");
|
||||
let supabase = createSupabaseClient();
|
||||
const BUCKET_PITCH_APPLICATION_NAME = "project-application";
|
||||
|
||||
export default function ApplyProject() {
|
||||
const [isSuccess, setIsSuccess] = useState(true);
|
||||
const onSubmit: SubmitHandler<projectSchema> = async (data) => {
|
||||
alert("มาแน้ววว");
|
||||
console.table(data);
|
||||
await sendApplication(data);
|
||||
// console.table(data);
|
||||
// console.log(typeof data["projectPhotos"], data["projectPhotos"]);
|
||||
};
|
||||
const saveApplicationData = async (recvData: any, userId: string) => {
|
||||
const pitchType = typeof recvData["projectPitchDeck"];
|
||||
const { data: projectData, error: projectError } = await supabase
|
||||
.from("project_application")
|
||||
.insert([
|
||||
{
|
||||
user_id: userId,
|
||||
pitch_deck_url:
|
||||
pitchType === "string" ? recvData["projectPitchDeck"] : "",
|
||||
target_investment: recvData["targetInvest"],
|
||||
deadline: recvData["deadline"],
|
||||
project_name: recvData["projectName"],
|
||||
project_type_id: recvData["projectType"],
|
||||
short_description: recvData["shortDescription"],
|
||||
min_investment: recvData["minInvest"],
|
||||
},
|
||||
])
|
||||
.select();
|
||||
|
||||
return { projectId: projectData?.[0]?.id, error: projectError };
|
||||
};
|
||||
const saveTags = async (tags: string[], projectId: string) => {
|
||||
const tagPromises = tags.map(async (tag) => {
|
||||
const response = await supabase
|
||||
.from("project_application_tag")
|
||||
.insert([{ tag_id: tag, item_id: projectId }])
|
||||
.select();
|
||||
|
||||
// console.log("Insert response for tag:", tag, response);
|
||||
|
||||
return response;
|
||||
});
|
||||
|
||||
const results = await Promise.all(tagPromises);
|
||||
|
||||
// Collect errors
|
||||
const errors = results
|
||||
.filter((result) => result.error)
|
||||
.map((result) => result.error);
|
||||
|
||||
return { errors };
|
||||
};
|
||||
|
||||
const uploadPitchFile = async (
|
||||
file: File,
|
||||
userId: string,
|
||||
projectId: string
|
||||
) => {
|
||||
if (!file || !userId) {
|
||||
console.error("Pitch file or user ID is undefined.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return await uploadFile(
|
||||
file,
|
||||
BUCKET_PITCH_APPLICATION_NAME,
|
||||
`${userId}/${projectId}/pitches/${file.name}`
|
||||
);
|
||||
};
|
||||
|
||||
const uploadLogoAndPhotos = async (
|
||||
logoFile: File,
|
||||
photos: File[],
|
||||
userId: string,
|
||||
projectId: string
|
||||
) => {
|
||||
const uploadResults: { logo?: any; photos: any[] } = { photos: [] };
|
||||
|
||||
// upload logo
|
||||
if (logoFile) {
|
||||
const logoResult = await uploadFile(
|
||||
logoFile,
|
||||
BUCKET_PITCH_APPLICATION_NAME,
|
||||
`${userId}/${projectId}/logo/${logoFile.name}`
|
||||
);
|
||||
|
||||
if (!logoResult.success) {
|
||||
console.error("Error uploading logo:", logoResult.errors);
|
||||
return { success: false, logo: logoResult, photos: [] };
|
||||
}
|
||||
|
||||
uploadResults.logo = logoResult;
|
||||
}
|
||||
|
||||
// upload each photo
|
||||
const uploadPhotoPromises = photos.map((image) =>
|
||||
uploadFile(
|
||||
image,
|
||||
BUCKET_PITCH_APPLICATION_NAME,
|
||||
`${userId}/${projectId}/photos/${image.name}`
|
||||
)
|
||||
);
|
||||
|
||||
const photoResults = await Promise.all(uploadPhotoPromises);
|
||||
uploadResults.photos = photoResults;
|
||||
|
||||
// check if all uploads were successful
|
||||
const allUploadsSuccessful = photoResults.every((result) => result.success);
|
||||
|
||||
return {
|
||||
success: allUploadsSuccessful,
|
||||
logo: uploadResults.logo,
|
||||
photos: uploadResults.photos,
|
||||
};
|
||||
};
|
||||
|
||||
const displayAlert = (error: any) => {
|
||||
Swal.fire({
|
||||
icon: error == null ? "success" : "error",
|
||||
title: error == null ? "Success" : `Error: ${error.code}`,
|
||||
text:
|
||||
error == null ? "Your application has been submitted" : error.message,
|
||||
confirmButtonColor: error == null ? "green" : "red",
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
// window.location.href = "/";
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const sendApplication = async (recvData: any) => {
|
||||
setIsSuccess(false);
|
||||
const {
|
||||
data: { user },
|
||||
} = await supabase.auth.getUser();
|
||||
|
||||
if (!user?.id) {
|
||||
console.error("User ID is undefined.");
|
||||
return;
|
||||
}
|
||||
|
||||
// save application data
|
||||
const { projectId, error } = await saveApplicationData(recvData, user.id);
|
||||
|
||||
if (error) {
|
||||
displayAlert(error);
|
||||
return;
|
||||
}
|
||||
const tagError = await saveTags(recvData["tag"], projectId);
|
||||
// if (tagError) {
|
||||
// displayAlert(tagError);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// upload pitch file if it’s a file
|
||||
if (typeof recvData["projectPitchDeck"] === "object") {
|
||||
const uploadPitchSuccess = await uploadPitchFile(
|
||||
recvData["projectPitchDeck"],
|
||||
user.id,
|
||||
projectId
|
||||
);
|
||||
|
||||
if (!uploadPitchSuccess) {
|
||||
console.error("Error uploading pitch file.");
|
||||
} else {
|
||||
console.log("Pitch file uploaded successfully.");
|
||||
}
|
||||
}
|
||||
|
||||
// upload logo and photos
|
||||
const { success, logo, photos } = await uploadLogoAndPhotos(
|
||||
recvData["projectLogo"],
|
||||
recvData["projectPhotos"],
|
||||
user.id,
|
||||
projectId
|
||||
);
|
||||
if (!success) {
|
||||
console.error("Error uploading media files.");
|
||||
}
|
||||
|
||||
// console.log("Bucket Name:", BUCKET_PITCH_APPLICATION_NAME);
|
||||
// console.log("Logo Path:", logo.data.path);
|
||||
// console.table(photos);
|
||||
|
||||
const logoURL = await getPrivateURL(
|
||||
logo.data.path,
|
||||
BUCKET_PITCH_APPLICATION_NAME
|
||||
);
|
||||
let photoURLsArray: string[] = [];
|
||||
const photoURLPromises = photos.map(
|
||||
async (item: {
|
||||
success: boolean;
|
||||
errors: typeof errors;
|
||||
data: { path: string };
|
||||
}) => {
|
||||
const photoURL = await getPrivateURL(
|
||||
item.data.path,
|
||||
BUCKET_PITCH_APPLICATION_NAME
|
||||
);
|
||||
if (photoURL?.signedUrl) {
|
||||
photoURLsArray.push(photoURL.signedUrl);
|
||||
} else {
|
||||
console.error("Signed URL for photo is undefined.");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
await Promise.all(photoURLPromises);
|
||||
// console.log(logoURL.publicUrl, projectId, logo.data.path);
|
||||
// console.log(logoURL?.signedUrl, projectId);
|
||||
// console.log(photoURLsArray[0], photoURLsArray[1]);
|
||||
if (logoURL?.signedUrl) {
|
||||
await updateImageURL(logoURL.signedUrl, "project_logo", projectId);
|
||||
} else {
|
||||
console.error("Signed URL for logo is undefined.");
|
||||
}
|
||||
await updateImageURL(photoURLsArray, "project_photos", projectId);
|
||||
// console.log(logoURL, photosUrl);
|
||||
setIsSuccess(true);
|
||||
displayAlert(error);
|
||||
};
|
||||
const updateImageURL = async (
|
||||
url: string | string[],
|
||||
columnName: string,
|
||||
projectId: number
|
||||
) => {
|
||||
const { error } = await supabase
|
||||
.from("project_application")
|
||||
.update({ [columnName]: url })
|
||||
.eq("id", projectId);
|
||||
// console.log(
|
||||
// `Updating ${columnName} with URL: ${url} for project ID: ${projectId}`
|
||||
// );
|
||||
if (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
const getPrivateURL = async (path: string, bucketName: string) => {
|
||||
const { data } = await supabase.storage
|
||||
.from(bucketName)
|
||||
.createSignedUrl(path, 9999999999999999999999999999);
|
||||
// console.table(data);
|
||||
return data;
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<div className="grid auto-rows-max w-3/4 ml-48 bg-zinc-100 dark:bg-zinc-900 mt-10 pt-12 pb-12">
|
||||
<Loader isSuccess={isSuccess} />
|
||||
<div className="grid grid-flow-row auto-rows-max w-full h-52 md:h-92 bg-gray-2s00 dark:bg-gray-800 p-5">
|
||||
<h1 className="text-2xl md:text-5xl font-medium md:font-bold justify-self-center md:mt-8">
|
||||
Apply to raise on B2DVentures
|
||||
</h1>
|
||||
<div className="mt-5 justify-self-center">
|
||||
<p className="text-sm md:text-base text-neutral-500">
|
||||
Begin Your First Fundraising Project. Starting a fundraising project
|
||||
is mandatory for all businesses.
|
||||
</p>
|
||||
<p className="text-sm md:text-base text-neutral-500">
|
||||
This step is crucial to begin your journey and unlock the necessary
|
||||
tools for raising funds.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid auto-rows-max bg-zinc-100 dark:bg-zinc-900 pt-12 -mb-6">
|
||||
<ProjectForm onSubmit={onSubmit} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -17,6 +17,21 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
|
||||
import { Textarea } from "./ui/textarea";
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "@/components/ui/command";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ChevronsUpDown, Check, X } from "lucide-react";
|
||||
|
||||
type projectSchema = z.infer<typeof projectFormSchema>;
|
||||
type FieldType = ControllerRenderProps<any, "projectPhotos">;
|
||||
@ -38,6 +53,11 @@ const ProjectForm = ({
|
||||
const [projectPitch, setProjectPitch] = useState("text");
|
||||
const [selectedImages, setSelectedImages] = useState<File[]>([]);
|
||||
const [projectPitchFile, setProjectPitchFile] = useState("");
|
||||
const [tag, setTag] = useState<{ id: number; value: string }[]>([]);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [selectedTag, setSelectedTag] = useState<
|
||||
{ id: number; value: string }[]
|
||||
>([]);
|
||||
|
||||
const handleFileChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement>,
|
||||
@ -83,8 +103,25 @@ const ProjectForm = ({
|
||||
}
|
||||
}
|
||||
};
|
||||
const fetchTag = async () => {
|
||||
let { data: tag, error } = await supabase.from("tag").select("id, value");
|
||||
|
||||
if (error) {
|
||||
console.error(error);
|
||||
} else {
|
||||
if (tag) {
|
||||
setTag(
|
||||
tag.map((item) => ({
|
||||
id: item.id,
|
||||
value: item.value,
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
fetchProjectType();
|
||||
fetchTag();
|
||||
}, []);
|
||||
return (
|
||||
<Form {...form}>
|
||||
@ -92,23 +129,14 @@ const ProjectForm = ({
|
||||
onSubmit={form.handleSubmit(onSubmit as SubmitHandler<projectSchema>)}
|
||||
className="space-y-8"
|
||||
>
|
||||
<h1 className="text-3xl font-bold mt-10">
|
||||
Begin Your First Fundraising Project
|
||||
</h1>
|
||||
<p className="mt-3 text-sm text-neutral-500">
|
||||
Starting a fundraising project is mandatory for all businesses. This
|
||||
step is crucial <br />
|
||||
to begin your journey and unlock the necessary tools for raising
|
||||
funds.
|
||||
</p>
|
||||
<div className="ml-96 mt-5 space-y-10">
|
||||
<div className="ml-96 space-y-10">
|
||||
{/* project name */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="projectName"
|
||||
render={({ field }: { field: any }) => (
|
||||
<FormItem>
|
||||
<div className="mt-10 space-y-5">
|
||||
<div className="space-y-5">
|
||||
<FormLabel className="font-bold text-lg">
|
||||
Project name
|
||||
</FormLabel>
|
||||
@ -139,7 +167,7 @@ const ProjectForm = ({
|
||||
fieldName="projectType"
|
||||
choices={projectType}
|
||||
handleFunction={(selectedValues: any) => {
|
||||
field.onChange(selectedValues.name);
|
||||
field.onChange(selectedValues.id);
|
||||
}}
|
||||
description={
|
||||
<>Please specify the primary purpose of the funds</>
|
||||
@ -447,15 +475,117 @@ const ProjectForm = ({
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<center>
|
||||
<Button
|
||||
className="mt-12 mb-20 h-10 text-base font-bold py-6 px-5"
|
||||
type="submit"
|
||||
>
|
||||
Submit application
|
||||
</Button>
|
||||
</center>
|
||||
{/* Tags */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="tag"
|
||||
render={({ field }: { field: any }) => (
|
||||
<FormItem>
|
||||
<div className="mt-10 space-y-5">
|
||||
<FormLabel className="font-bold text-lg">Tags</FormLabel>
|
||||
<FormControl>
|
||||
<div className="flex space-x-5">
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
className="w-96 justify-between overflow-hidden text-ellipsis whitespace-nowrap"
|
||||
>
|
||||
{selectedTag.length > 0
|
||||
? selectedTag.map((t) => t.value).join(", ")
|
||||
: "Select tags..."}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-96 p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder="Search tags..." />
|
||||
<CommandList>
|
||||
<CommandEmpty>No tags found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{tag.map((tag) => (
|
||||
<CommandItem
|
||||
key={tag.id}
|
||||
value={tag.value}
|
||||
onSelect={() => {
|
||||
setSelectedTag((prev) => {
|
||||
const exists = prev.find(
|
||||
(t) => t.id === tag.id
|
||||
);
|
||||
const updatedTags = exists
|
||||
? prev.filter((t) => t.id !== tag.id)
|
||||
: [...prev, tag];
|
||||
field.onChange(
|
||||
updatedTags.map((t) => t.id)
|
||||
);
|
||||
return updatedTags;
|
||||
});
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"h-4",
|
||||
selectedTag.some((t) => t.id === tag.id)
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
)}
|
||||
/>
|
||||
{tag.value}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<span className="text-[12px] text-neutral-500 self-center">
|
||||
Add 1 to 5 tags that describe your project. Tags help{" "}
|
||||
<br />
|
||||
investors understand your focus.
|
||||
</span>
|
||||
</div>
|
||||
</FormControl>
|
||||
</div>
|
||||
<FormMessage />
|
||||
{/* display selected tags */}
|
||||
<div className="flex flex-wrap space-x-3">
|
||||
{selectedTag.map((tag) => (
|
||||
<div
|
||||
key={tag.id}
|
||||
className="flex items-center space-x-1 p-1 rounded mt-2 outline outline-offset-2 outline-1"
|
||||
>
|
||||
<span>{tag.value}</span>
|
||||
<button
|
||||
onClick={() => {
|
||||
setSelectedTag((prev) => {
|
||||
const updatedTags = prev.filter(
|
||||
(t) => t.id !== tag.id
|
||||
);
|
||||
field.onChange(updatedTags.map((t) => t.id));
|
||||
return updatedTags;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<center>
|
||||
<Button
|
||||
className="mt-12 mb-20 h-10 text-base font-bold py-6 px-5 "
|
||||
type="submit"
|
||||
>
|
||||
Submit application
|
||||
</Button>
|
||||
</center>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
|
||||
27
src/components/loading/loader.tsx
Normal file
27
src/components/loading/loader.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import Lottie from "react-lottie";
|
||||
import * as loadingData from "./loading.json";
|
||||
|
||||
const loadingOption = {
|
||||
loop: true,
|
||||
autoplay: true,
|
||||
animationData: loadingData,
|
||||
rendererSettings: {
|
||||
preserveAspectRatio: "xMidYMid slice",
|
||||
},
|
||||
};
|
||||
|
||||
interface LoaderProps {
|
||||
isSuccess: boolean;
|
||||
}
|
||||
|
||||
export function Loader(props: LoaderProps) {
|
||||
return (
|
||||
<>
|
||||
{!props.isSuccess && (
|
||||
<div className="fixed inset-0 flex items-center justify-center bg-white bg-opacity-10 backdrop-blur-sm z-50">
|
||||
<Lottie options={loadingOption} height={200} width={200} />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
2549
src/components/loading/loading.json
Normal file
2549
src/components/loading/loading.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -51,8 +51,8 @@ export function NavigationBar() {
|
||||
const projectComponents = [
|
||||
{
|
||||
title: "Projects",
|
||||
href: "/landing",
|
||||
description: "Raise on B2DVentures",
|
||||
href: "/project/apply",
|
||||
description: "Start your new project on B2DVentures",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ export function ProjectCard(props: ProjectCardProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col group border-[1px] border-border relative hover:shadow-md rounded-xl h-[450px]",
|
||||
"flex flex-col group border-[1px] border-border relative hover:shadow-md rounded-xl h-[450px] ",
|
||||
props.className
|
||||
)}>
|
||||
<div className="flex flex-col h-full">
|
||||
@ -58,7 +58,7 @@ export function ProjectCard(props: ProjectCardProps) {
|
||||
|
||||
{/* Info 1 */}
|
||||
<div>
|
||||
<div className="transition-transform duration-500 transform opacity-100 group-hover:opacity-0 p-4">
|
||||
<div className="transition-transform duration-500 transform opacity-100 group-hover:opacity-0 p-4 ">
|
||||
<div className="flex items-center text-muted-foreground">
|
||||
<span className="flex items-center gap-1">
|
||||
<CalendarDaysIcon width={20} />
|
||||
@ -79,7 +79,7 @@ export function ProjectCard(props: ProjectCardProps) {
|
||||
</div>
|
||||
|
||||
{/* Info 2 */}
|
||||
<div className="hidden group-hover:flex group-hover:absolute group-hover:bottom-4 p-4">
|
||||
<div className="hidden group-hover:flex group-hover:absolute group-hover:bottom-4 p-4 ">
|
||||
{/* Info 2 (Visible on hover) */}
|
||||
<div className="transition-transform duration-500 transform translate-y-6 opacity-0 group-hover:translate-y-0 group-hover:opacity-100">
|
||||
<hr className="-ml-4 mb-2" />
|
||||
|
||||
@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
||||
31
src/components/ui/popover.tsx
Normal file
31
src/components/ui/popover.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as PopoverPrimitive from "@radix-ui/react-popover"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Popover = PopoverPrimitive.Root
|
||||
|
||||
const PopoverTrigger = PopoverPrimitive.Trigger
|
||||
|
||||
const PopoverContent = React.forwardRef<
|
||||
React.ElementRef<typeof PopoverPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
||||
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
||||
<PopoverPrimitive.Portal>
|
||||
<PopoverPrimitive.Content
|
||||
ref={ref}
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</PopoverPrimitive.Portal>
|
||||
))
|
||||
PopoverContent.displayName = PopoverPrimitive.Content.displayName
|
||||
|
||||
export { Popover, PopoverTrigger, PopoverContent }
|
||||
@ -2,7 +2,7 @@ import { SupabaseClient } from "@supabase/supabase-js";
|
||||
|
||||
async function getTopProjects(
|
||||
client: SupabaseClient,
|
||||
numberOfRecords: number = 4,
|
||||
numberOfRecords: number = 4
|
||||
) {
|
||||
try {
|
||||
const { data, error } = await client
|
||||
@ -21,7 +21,7 @@ async function getTopProjects(
|
||||
target_investment,
|
||||
investment_deadline
|
||||
),
|
||||
item_tag (
|
||||
project_tag (
|
||||
tag (
|
||||
id,
|
||||
value
|
||||
@ -30,7 +30,7 @@ async function getTopProjects(
|
||||
business (
|
||||
location
|
||||
)
|
||||
`,
|
||||
`
|
||||
)
|
||||
.order("published_time", { ascending: false })
|
||||
.limit(numberOfRecords);
|
||||
@ -48,8 +48,10 @@ async function getTopProjects(
|
||||
}
|
||||
|
||||
function getProjectDataQuery(client: SupabaseClient, projectId: number) {
|
||||
return client.from("project").select(
|
||||
`
|
||||
return client
|
||||
.from("project")
|
||||
.select(
|
||||
`
|
||||
project_name,
|
||||
project_short_description,
|
||||
project_description,
|
||||
@ -65,13 +67,17 @@ function getProjectDataQuery(client: SupabaseClient, projectId: number) {
|
||||
tag_name:value
|
||||
)
|
||||
)
|
||||
`,
|
||||
).eq("id", projectId).single();
|
||||
`
|
||||
)
|
||||
.eq("id", projectId)
|
||||
.single();
|
||||
}
|
||||
|
||||
async function getProjectData(client: SupabaseClient, projectId: number) {
|
||||
const query = client.from("project").select(
|
||||
`
|
||||
const query = client
|
||||
.from("project")
|
||||
.select(
|
||||
`
|
||||
project_name,
|
||||
project_short_description,
|
||||
project_description,
|
||||
@ -87,8 +93,10 @@ async function getProjectData(client: SupabaseClient, projectId: number) {
|
||||
tag_name:value
|
||||
)
|
||||
)
|
||||
`,
|
||||
).eq("id", projectId).single();
|
||||
`
|
||||
)
|
||||
.eq("id", projectId)
|
||||
.single();
|
||||
|
||||
const { data, error } = await query;
|
||||
return { data, error };
|
||||
@ -118,13 +126,15 @@ function searchProjectsQuery(
|
||||
sortByTimeFilter,
|
||||
page = 1,
|
||||
pageSize = 4,
|
||||
}: FilterProjectQueryParams,
|
||||
}: 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,
|
||||
published_time,
|
||||
@ -150,8 +160,10 @@ function searchProjectsQuery(
|
||||
),
|
||||
business_location:location
|
||||
)
|
||||
`,
|
||||
).order("published_time", { ascending: false }).range(start, end);
|
||||
`
|
||||
)
|
||||
.order("published_time", { ascending: false })
|
||||
.range(start, end);
|
||||
|
||||
if (sortByTimeFilter === "all") {
|
||||
sortByTimeFilter = undefined;
|
||||
|
||||
@ -21,7 +21,7 @@ const projectFormSchema = z.object({
|
||||
projectName: z.string().min(5, {
|
||||
message: "Project name must be at least 5 characters.",
|
||||
}),
|
||||
projectType: z.string({
|
||||
projectType: z.number({
|
||||
required_error: "Please select one of the option",
|
||||
}),
|
||||
shortDescription: z
|
||||
@ -90,6 +90,10 @@ const projectFormSchema = z.object({
|
||||
.refine((date) => date > new Date(), {
|
||||
message: "Deadline must be in the future.",
|
||||
}),
|
||||
tag: z
|
||||
.array(z.number())
|
||||
.min(1, "Please provide at least one tag.")
|
||||
.max(5, "You can provide up to 5 tags."),
|
||||
});
|
||||
|
||||
const businessFormSchema = z.object({
|
||||
@ -147,7 +151,7 @@ const businessFormSchema = z.object({
|
||||
.refine((file) => file.size < MAX_FILE_SIZE, {
|
||||
message: "File can't be bigger than 5MB.",
|
||||
})
|
||||
.refine((file) => file.name.endsWith(".md"), {
|
||||
.refine((file) => file.name.toLowerCase().endsWith(".md"), {
|
||||
message: "File must be a markdown file (.md).",
|
||||
}),
|
||||
]),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user