diff --git a/.gitignore b/.gitignore index fd3dbb5..031e1fe 100644 --- a/.gitignore +++ b/.gitignore @@ -26,7 +26,8 @@ yarn-debug.log* yarn-error.log* # local env files -.env*.local +.env*. +.env # vercel .vercel diff --git a/package-lock.json b/package-lock.json index 361a165..fdcc5d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "b2d-ventures", "version": "0.1.0", "dependencies": { + "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-navigation-menu": "^1.2.0", "@radix-ui/react-progress": "^1.1.0", @@ -475,6 +476,31 @@ } } }, + "node_modules/@radix-ui/react-avatar": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.0.tgz", + "integrity": "sha512-Q/PbuSMk/vyAd/UoIShVGZ7StHHeRFYU7wXmi5GV+8cLXflZAEpHL/F697H1klrzxKXNtZ97vWiC0q3RKUH8UA==", + "dependencies": { + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "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-collection": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", diff --git a/package.json b/package.json index 92f01a2..9a28531 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,11 @@ "lint": "next lint" }, "dependencies": { + "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-navigation-menu": "^1.2.0", "@radix-ui/react-progress": "^1.1.0", + "@radix-ui/react-select": "^2.1.1", "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", "@supabase/ssr": "^0.4.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 22fa7a4..2194c7a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,6 +1,13 @@ lockfileVersion: '6.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + dependencies: + '@radix-ui/react-avatar': + specifier: ^1.1.0 + version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1) '@radix-ui/react-dropdown-menu': specifier: ^2.1.1 version: 2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1) @@ -10,6 +17,9 @@ dependencies: '@radix-ui/react-progress': specifier: ^1.1.0 version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-select': + specifier: ^2.1.1 + version: 2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1) '@radix-ui/react-separator': specifier: ^1.1.0 version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1) @@ -17,10 +27,10 @@ dependencies: specifier: ^1.1.0 version: 1.1.0(@types/react@18.3.4)(react@18.3.1) '@supabase/ssr': - specifier: ^0.4.0 + specifier: ^0.4.1 version: 0.4.1(@supabase/supabase-js@2.45.2) '@supabase/supabase-js': - specifier: ^2.45.1 + specifier: ^2.45.2 version: 2.45.2 class-variance-authority: specifier: ^0.7.0 @@ -331,6 +341,10 @@ packages: requiresBuild: true optional: true + /@radix-ui/number@1.1.0: + resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} + dev: false + /@radix-ui/primitive@1.1.0: resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==} dev: false @@ -355,6 +369,29 @@ packages: react-dom: 18.3.1(react@18.3.1) dev: false + /@radix-ui/react-avatar@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-Q/PbuSMk/vyAd/UoIShVGZ7StHHeRFYU7wXmi5GV+8cLXflZAEpHL/F697H1klrzxKXNtZ97vWiC0q3RKUH8UA==} + 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 + dependencies: + '@radix-ui/react-context': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@types/react': 18.3.4 + '@types/react-dom': 18.3.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + dev: false + /@radix-ui/react-collection@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==} peerDependencies: @@ -726,6 +763,46 @@ packages: react-dom: 18.3.1(react@18.3.1) dev: false + /@radix-ui/react-select@2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-8iRDfyLtzxlprOo9IicnzvpsO1wNCkuwzzCM+Z5Rb5tNOpCdMvcc2AkzX0Fz+Tz9v6NJ5B/7EEgyZveo4FBRfQ==} + 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 + dependencies: + '@radix-ui/number': 1.1.0 + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-context': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-popper': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.4)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1) + '@types/react': 18.3.4 + '@types/react-dom': 18.3.0 + aria-hidden: 1.2.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.5.7(@types/react@18.3.4)(react@18.3.1) + dev: false + /@radix-ui/react-separator@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.4)(react-dom@18.3.1)(react@18.3.1): resolution: {integrity: sha512-3uBAs+egzvJBDZAzvb/n4NxxOYpnspmWxO2u5NbZ8Y6FM/NdrGSF9bop3Cf6F6C71z1rTSn8KV0Fo2ZVd79lGA==} peerDependencies: @@ -3717,7 +3794,3 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: true - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false diff --git a/public/placeholder-user.jpg b/public/placeholder-user.jpg new file mode 100644 index 0000000..6fa7543 Binary files /dev/null and b/public/placeholder-user.jpg differ diff --git a/public/placeholder.svg b/public/placeholder.svg new file mode 100644 index 0000000..e763910 --- /dev/null +++ b/public/placeholder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/deals/page.tsx b/src/app/deals/page.tsx new file mode 100644 index 0000000..21678af --- /dev/null +++ b/src/app/deals/page.tsx @@ -0,0 +1,152 @@ +"use client"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { useState } from "react"; +import { + Clock3Icon, + MessageSquareIcon, + UserIcon, + UsersIcon, +} from "lucide-react"; +import { Separator } from "@/components/ui/separator"; +import { ExtendableCard } from "@/components/extendableCard"; + +export default function Deals() { + const [postAtFilter, setPostAtFilter] = useState(""); + const [contentTypeFilter, setContentTypeFilter] = useState(""); + const [authorFilter, setAuthorFilter] = useState(""); + const [groupsFilter, setGroupFilter] = useState(""); + const data = [ + { + name: "NVDA", + description: + "Founded in 1993, NVIDIA is a key innovator of computer graphics and AI technology", + joinDate: "December 2021", + location: "Bangkok, Thailand", + tags: null, + minInvestment: 10000, + totalInvestor: 58400, + totalRaised: 9000000, + }, + { + name: "Apple Inc.", + description: + "Founded in 1976, Apple Inc. is a leading innovator in consumer electronics, software, and online services, known for products like the iPhone, MacBook, and the App Store.", + joinDate: "February 2020", + location: "Cupertino, California, USA", + tags: null, + minInvestment: 10000, + totalInvestor: 58400, + totalRaised: 9000000, + }, + { + name: "Google LLC", + description: + "Founded in 1998, Google LLC specializes in internet-related services and products, including search engines, online advertising, cloud computing, and the Android operating system.", + joinDate: "April 2019", + location: "Mountain View, California, USA", + tags: null, + minInvestment: 10000, + totalInvestor: 5000, + totalRaised: 1500000000, + }, + { + name: "Microsoft Corporation", + description: + "Microsoft Corporation is a multinational technology company.", + joinDate: "January 2018", + location: "California, USA", + tags: null, + minInvestment: 250, + totalInvestor: 5000, + totalRaised: 1500000, + }, + ]; + + return ( +
+
+

Investment Opportunities

+
+

Browse current investment opportunities on Republic.

+

+ All companies are vetted & pass due diligence. +

+ {/* filters */} +
+ + + + + {/* {postAtFilter} + {contentTypeFilter} + {authorFilter} + {groupsFilter} */} +
+ +
+ +
+

Deals

+

The deals attracting the most interest right now

+
+ {/* block for all the deals */} +
+ {data.map((item, index) => ( + + ))} +
+
+ ); +} diff --git a/src/app/invest/page.tsx b/src/app/invest/page.tsx index 029468b..1605850 100644 --- a/src/app/invest/page.tsx +++ b/src/app/invest/page.tsx @@ -28,18 +28,18 @@ export default function Invest() { }, []); return (
-
+
logo

NVIDIA

World's first non-metal sustainable battery

-
+
{["Technology", "Gaming"].map((tag) => ( {tag} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 6ec0c9d..49b3802 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -3,7 +3,7 @@ import { Montserrat } from "next/font/google"; import { ThemeProvider } from "@/components/theme-provider"; import "@/app/globals.css"; -import { UnsignedNav } from "@/components/navigationBar/Unsigned"; +import { NavigationBar } from "@/components/navigationBar/nav"; const montserrat = Montserrat({ subsets: ["latin"], @@ -29,7 +29,7 @@ export default function RootLayout({ children }: RootLayoutProps) {
- +
{children}
diff --git a/src/app/page.tsx b/src/app/page.tsx index 777c825..15f1f49 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,9 +1,15 @@ 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 { BusinessCard } from "@/components/businessCard"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; import { Separator } from "@/components/ui/separator"; +import { ExtendableCard } from "@/components/extendableCard"; export default function Home() { return ( @@ -13,12 +19,14 @@ export default function Home() {

Explore the world of ventures

-

Unlock opportunities and connect with a community of passionate

+

+ Unlock opportunities and connect with a community of passionate +

investors and innovators.

Together, we turn ideas into impact.

@@ -75,17 +83,26 @@ export default function Home() {

Hottest Deals

-

The deals attracting the most interest right now

+

+ The deals attracting the most interest right now +

- - + + + - -
diff --git a/src/components/extendableCard.tsx b/src/components/extendableCard.tsx new file mode 100644 index 0000000..933ee9d --- /dev/null +++ b/src/components/extendableCard.tsx @@ -0,0 +1,87 @@ +"use client"; +import { CalendarDaysIcon } from "lucide-react"; + +interface XMap { + // tagName: colorCode + [tag: string]: string; +} + +interface ExtendableCardProps { + name: string; + description: string; + joinDate: string; + location: string; + tags: XMap | null; + minInvestment: number; + totalInvestor: number; + totalRaised: number; +} + +export function ExtendableCard(props: ExtendableCardProps) { + return ( +
+
+ Card image +
+
+

+ {props.name} +

+ + {/* Default content (visible when not hovered) */} +
+ + + Joined {props.joinDate} + +
+
+ {props.location} +
+
+ {["Technology", "Gaming"].map((tag) => ( + + {tag} + + ))} +
+ + {/* Hover content (appears when hovered) */} +
+

{props.description}

+
+
+
+
+

+ ${props.totalRaised.toLocaleString()}{" "} + committed and reserved +

+
+

+ {props.totalInvestor.toLocaleString()}{" "} + investors +

+
+

+ ${props.minInvestment.toLocaleString()} min. + investment +

+
+
+
+
+
+
+ ); +} diff --git a/src/components/navigationBar/Unsigned.tsx b/src/components/navigationBar/nav.tsx similarity index 78% rename from src/components/navigationBar/Unsigned.tsx rename to src/components/navigationBar/nav.tsx index 2bc21c2..33b8d45 100644 --- a/src/components/navigationBar/Unsigned.tsx +++ b/src/components/navigationBar/nav.tsx @@ -17,7 +17,11 @@ import { NavigationMenuTrigger, navigationMenuTriggerStyle, } from "@/components/ui/navigation-menu"; -import { Search } from "lucide-react"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { Skeleton } from "@/components/ui/skeleton"; +import { Search, Bell, Heart, Wallet } from "lucide-react"; + +import useSession from "@/lib/supabase/useSession"; const landings = [ { @@ -54,7 +58,44 @@ const ListItem = React.forwardRef, React.ComponentPropsWit ); ListItem.displayName = "ListItem"; -export function UnsignedNav() { +const unAuthenticatedComponents = () => { + return ( +
+ + + + +
+ ); +}; + +const authenticatedComponents = () => { + return ( +
+ + + + + + 1 + +
+ ); +}; + +export function NavigationBar() { + const { session, loading } = useSession(); + const user = session?.user; + const [sessionLoaded, setSessionLoaded] = React.useState(false); + + React.useEffect(() => { + if (!loading) { + setSessionLoaded(true); + } + }, [loading]); + const businessComponents = [ { title: "Businesses", @@ -151,12 +192,17 @@ export function UnsignedNav() {
- - - - + {sessionLoaded ? ( + user ? ( + authenticatedComponents() + ) : ( + unAuthenticatedComponents() + ) + ) : ( +
+ +
+ )}
diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx new file mode 100644 index 0000000..51e507b --- /dev/null +++ b/src/components/ui/avatar.tsx @@ -0,0 +1,50 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx new file mode 100644 index 0000000..cbe5a36 --- /dev/null +++ b/src/components/ui/select.tsx @@ -0,0 +1,160 @@ +"use client" + +import * as React from "react" +import * as SelectPrimitive from "@radix-ui/react-select" +import { Check, ChevronDown, ChevronUp } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Select = SelectPrimitive.Root + +const SelectGroup = SelectPrimitive.Group + +const SelectValue = SelectPrimitive.Value + +const SelectTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + span]:line-clamp-1", + className + )} + {...props} + > + {children} + + + + +)) +SelectTrigger.displayName = SelectPrimitive.Trigger.displayName + +const SelectScrollUpButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName + +const SelectScrollDownButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +SelectScrollDownButton.displayName = + SelectPrimitive.ScrollDownButton.displayName + +const SelectContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, position = "popper", ...props }, ref) => ( + + + + + {children} + + + + +)) +SelectContent.displayName = SelectPrimitive.Content.displayName + +const SelectLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SelectLabel.displayName = SelectPrimitive.Label.displayName + +const SelectItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + + {children} + +)) +SelectItem.displayName = SelectPrimitive.Item.displayName + +const SelectSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SelectSeparator.displayName = SelectPrimitive.Separator.displayName + +export { + Select, + SelectGroup, + SelectValue, + SelectTrigger, + SelectContent, + SelectLabel, + SelectItem, + SelectSeparator, + SelectScrollUpButton, + SelectScrollDownButton, +} diff --git a/src/components/ui/skeleton.tsx b/src/components/ui/skeleton.tsx new file mode 100644 index 0000000..01b8b6d --- /dev/null +++ b/src/components/ui/skeleton.tsx @@ -0,0 +1,15 @@ +import { cn } from "@/lib/utils" + +function Skeleton({ + className, + ...props +}: React.HTMLAttributes) { + return ( +
+ ) +} + +export { Skeleton } diff --git a/src/lib/supabase/useSession.ts b/src/lib/supabase/useSession.ts new file mode 100644 index 0000000..c4bb61e --- /dev/null +++ b/src/lib/supabase/useSession.ts @@ -0,0 +1,27 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { createSupabaseClient } from "./clientComponentClient"; +import { Session } from "@supabase/supabase-js"; + +export default function useSession() { + const [session, setSession] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const supabase = createSupabaseClient(); + + const getSession = async () => { + const { + data: { session }, + } = await supabase.auth.getSession(); + + setSession(session); + setLoading(false); + }; + + getSession(); + }, []); + + return { session, loading }; +}