From ad6d4941e1bf700e42406ffd3b92a2b010d64c5d Mon Sep 17 00:00:00 2001 From: Nevo David Date: Wed, 10 Apr 2024 18:18:03 +0700 Subject: [PATCH] feat: stage two --- .../src/app/(site)/marketplace/buyer/page.tsx | 16 ++ .../src/app/(site)/marketplace/layout.tsx | 11 + .../src/app/(site)/marketplace/page.tsx | 17 ++ .../app/(site)/marketplace/seller/page.tsx | 16 ++ apps/frontend/src/components/layout/title.tsx | 2 +- .../src/components/layout/top.menu.tsx | 9 +- .../components/marketplace/buyer.seller.tsx | 20 ++ .../src/components/marketplace/buyer.tsx | 235 ++++++++++++++++++ .../components/marketplace/marketplace.tsx | 3 + .../src/components/marketplace/seller.tsx | 32 +++ apps/frontend/src/middleware.ts | 21 +- .../src/database/prisma/schema.prisma | 21 ++ .../src/form/checkbox.tsx | 15 +- .../src/form/slider.tsx | 8 +- 14 files changed, 412 insertions(+), 14 deletions(-) create mode 100644 apps/frontend/src/app/(site)/marketplace/buyer/page.tsx create mode 100644 apps/frontend/src/app/(site)/marketplace/layout.tsx create mode 100644 apps/frontend/src/app/(site)/marketplace/page.tsx create mode 100644 apps/frontend/src/app/(site)/marketplace/seller/page.tsx create mode 100644 apps/frontend/src/components/marketplace/buyer.seller.tsx create mode 100644 apps/frontend/src/components/marketplace/buyer.tsx create mode 100644 apps/frontend/src/components/marketplace/marketplace.tsx create mode 100644 apps/frontend/src/components/marketplace/seller.tsx diff --git a/apps/frontend/src/app/(site)/marketplace/buyer/page.tsx b/apps/frontend/src/app/(site)/marketplace/buyer/page.tsx new file mode 100644 index 00000000..3f09e89b --- /dev/null +++ b/apps/frontend/src/app/(site)/marketplace/buyer/page.tsx @@ -0,0 +1,16 @@ +import { Buyer } from '@gitroom/frontend/components/marketplace/buyer'; + +export const dynamic = 'force-dynamic'; +import { Metadata } from 'next'; + +export const metadata: Metadata = { + title: 'Gitroom Marketplace', + description: '', +}; +export default async function Index({ + searchParams, +}: { + searchParams: { code: string }; +}) { + return ; +} diff --git a/apps/frontend/src/app/(site)/marketplace/layout.tsx b/apps/frontend/src/app/(site)/marketplace/layout.tsx new file mode 100644 index 00000000..9589be86 --- /dev/null +++ b/apps/frontend/src/app/(site)/marketplace/layout.tsx @@ -0,0 +1,11 @@ +import { BuyerSeller } from '@gitroom/frontend/components/marketplace/buyer.seller'; +import { ReactNode } from 'react'; + +export default function Layout({ children }: { children: ReactNode }) { + return ( + <> + + {children} + + ) +} \ No newline at end of file diff --git a/apps/frontend/src/app/(site)/marketplace/page.tsx b/apps/frontend/src/app/(site)/marketplace/page.tsx new file mode 100644 index 00000000..665b9cd4 --- /dev/null +++ b/apps/frontend/src/app/(site)/marketplace/page.tsx @@ -0,0 +1,17 @@ +export const dynamic = 'force-dynamic'; +import { Metadata } from 'next'; +import { cookies } from 'next/headers'; +import { redirect } from 'next/navigation'; + +export const metadata: Metadata = { + title: 'Gitroom Marketplace', + description: '', +}; +export default async function Index({ + searchParams, +}: { + searchParams: { code: string }; +}) { + const currentCookie = cookies()?.get('marketplace')?.value; + return redirect(currentCookie === 'buyer' ? '/marketplace/buyer' : '/marketplace/seller'); +} diff --git a/apps/frontend/src/app/(site)/marketplace/seller/page.tsx b/apps/frontend/src/app/(site)/marketplace/seller/page.tsx new file mode 100644 index 00000000..c9377f6a --- /dev/null +++ b/apps/frontend/src/app/(site)/marketplace/seller/page.tsx @@ -0,0 +1,16 @@ +import { Seller } from '@gitroom/frontend/components/marketplace/seller'; + +export const dynamic = 'force-dynamic'; +import { Metadata } from 'next'; + +export const metadata: Metadata = { + title: 'Gitroom Marketplace', + description: '', +}; +export default async function Index({ + searchParams, +}: { + searchParams: { code: string }; +}) { + return ; +} diff --git a/apps/frontend/src/components/layout/title.tsx b/apps/frontend/src/components/layout/title.tsx index 2cf2aaa3..5e0b0aa3 100644 --- a/apps/frontend/src/components/layout/title.tsx +++ b/apps/frontend/src/components/layout/title.tsx @@ -7,7 +7,7 @@ import {menuItems} from "@gitroom/frontend/components/layout/top.menu"; export const Title = () => { const path = usePathname(); const currentTitle = useMemo(() => { - return menuItems.find(item => item.path === path)?.name; + return menuItems.find(item => path.indexOf(item.path) > -1)?.name; }, [path]); return ( diff --git a/apps/frontend/src/components/layout/top.menu.tsx b/apps/frontend/src/components/layout/top.menu.tsx index f9ee9f2a..ba9a0780 100644 --- a/apps/frontend/src/components/layout/top.menu.tsx +++ b/apps/frontend/src/components/layout/top.menu.tsx @@ -29,6 +29,11 @@ export const menuItems = [ path: '/billing', role: ['ADMIN', 'SUPERADMIN'], }, + { + name: 'Marketplace', + icon: 'marketplace', + path: '/marketplace', + }, ]; export const TopMenu: FC = () => { @@ -59,8 +64,8 @@ export const TopMenu: FC = () => { } return true; }) - .map((p) => p.path) - .indexOf(path) === index + .map((p) => path.indexOf(p.path) > -1 ? index : -1) + .indexOf(index) === index ? 'text-primary showbox' : 'text-gray' )} diff --git a/apps/frontend/src/components/marketplace/buyer.seller.tsx b/apps/frontend/src/components/marketplace/buyer.seller.tsx new file mode 100644 index 00000000..7562693b --- /dev/null +++ b/apps/frontend/src/components/marketplace/buyer.seller.tsx @@ -0,0 +1,20 @@ +"use client"; +import { FC } from 'react'; +import { usePathname } from 'next/navigation'; +import clsx from 'clsx'; +import Link from 'next/link'; + +export const BuyerSeller: FC = () => { + const path = usePathname(); + const pathComputed = path === '/marketplace' ? '/marketplace/seller' : path; + return ( +
+
+
+ -1 && 'bg-forth')}>Seller + -1 && 'bg-forth')}>Buyer +
+
+
+ ); +}; diff --git a/apps/frontend/src/components/marketplace/buyer.tsx b/apps/frontend/src/components/marketplace/buyer.tsx new file mode 100644 index 00000000..3e407592 --- /dev/null +++ b/apps/frontend/src/components/marketplace/buyer.tsx @@ -0,0 +1,235 @@ +'use client'; + +import { FC, useCallback, useRef, useState } from 'react'; +import { Checkbox } from '@gitroom/react/form/checkbox'; +import { useRouter, useSearchParams } from 'next/navigation'; +import clsx from 'clsx'; +import interClass from '@gitroom/react/helpers/inter.font'; +import { Button } from '@gitroom/react/form/button'; + +export const LabelCheckbox: FC<{ + label: string; + name: string; + value: string; + checked: boolean; + onChange: (value: string, status: boolean) => void; +}> = (props) => { + const { label, name, value, checked, onChange } = props; + const ref = useRef(null); + const [innerCheck, setInnerCheck] = useState(checked); + + const change = useCallback(() => { + setInnerCheck(!innerCheck); + onChange(value, !innerCheck); + }, [innerCheck]); + + return ( +
+ + +
+ ); +}; + +export const Options: FC<{ + title: string; + options: Array<{ key: string; value: string }>; + query: string; +}> = (props) => { + const { title, options, query } = props; + const router = useRouter(); + const searchParams = (useSearchParams().get(query) || '')?.split(',') || []; + + const change = (value: string, state: boolean) => { + const getAll = new URLSearchParams(window.location.search).get(query); + const splitAll = (getAll?.split(',') || []).filter((f) => f); + + if (state) { + splitAll?.push(value); + } else { + splitAll?.splice(splitAll.indexOf(value), 1); + } + + const params = new URLSearchParams(window.location.search); + if (!splitAll?.length) { + params.delete(query); + } else { + params.set(query, splitAll?.join(',') || ''); + } + + router.replace('?' + params.toString()); + return params.toString(); + }; + + return ( + <> +
+ {title} +
+
+ {options.map((option) => ( +
+ -1} + name={query} + onChange={change} + /> +
+ ))} +
+ + ); +}; + +export const Card = () => { + return ( +
+
+
+
+ +
+
+ + + +
+
22,6K
+
+
+
+
+
+
Nevo David
+
+
+ Content Writer +
+
+ Influencer +
+
+
+ + + +
+
+
+ Maecenas dignissim justo eget nulla rutrum molestie. Maecenas + lobortis sem dui, +
+
+
AI
+
+
+
+
AI
+
+
+
+
AI
+
+
+
+
+ +
+
+ ); +}; + +export const Buyer = () => { + return ( +
+
+
+

Filter

+
+ + +
+
+
+
+
234 Result
+ + + + + + + + + + + +
+
+ ); +}; diff --git a/apps/frontend/src/components/marketplace/marketplace.tsx b/apps/frontend/src/components/marketplace/marketplace.tsx new file mode 100644 index 00000000..46e033b4 --- /dev/null +++ b/apps/frontend/src/components/marketplace/marketplace.tsx @@ -0,0 +1,3 @@ +export const Marketplace = () => { + return
+} \ No newline at end of file diff --git a/apps/frontend/src/components/marketplace/seller.tsx b/apps/frontend/src/components/marketplace/seller.tsx new file mode 100644 index 00000000..6945d321 --- /dev/null +++ b/apps/frontend/src/components/marketplace/seller.tsx @@ -0,0 +1,32 @@ +'use client'; + +import { Slider } from "@gitroom/react/form/slider"; +import { Button } from '@gitroom/react/form/button'; + +export const Seller = () => { + return ( +
+
+

Seller Mode

+
+
+
John Smith
+
+ {}} /> +
Active
+
+
+
+ +
+
+
+
+

Details

+
+ asdfasdf +
+
+
+ ); +}; diff --git a/apps/frontend/src/middleware.ts b/apps/frontend/src/middleware.ts index 027d1d5e..021f0529 100644 --- a/apps/frontend/src/middleware.ts +++ b/apps/frontend/src/middleware.ts @@ -90,7 +90,26 @@ export async function middleware(request: NextRequest) { return NextResponse.redirect(new URL(`/analytics`, nextUrl.href)); } - return NextResponse.next(); + const next = NextResponse.next(); + + if ( + nextUrl.pathname === '/marketplace/seller' || + nextUrl.pathname === '/marketplace/buyer' + ) { + const type = nextUrl.pathname.split('/marketplace/')[1].split('/')[0]; + + next.cookies.set('marketplace', type === 'seller' ? 'seller' : 'buyer', { + path: '/', + sameSite: false, + httpOnly: true, + secure: true, + expires: new Date(Date.now() + 15 * 60 * 1000), + domain: + '.' + new URL(removeSubdomain(process.env.FRONTEND_URL!)).hostname, + }); + } + + return next; } catch (err) { return NextResponse.redirect(new URL('/auth/logout', nextUrl.href)); } diff --git a/libraries/nestjs-libraries/src/database/prisma/schema.prisma b/libraries/nestjs-libraries/src/database/prisma/schema.prisma index e3c258e8..493ae4bd 100644 --- a/libraries/nestjs-libraries/src/database/prisma/schema.prisma +++ b/libraries/nestjs-libraries/src/database/prisma/schema.prisma @@ -32,6 +32,10 @@ model User { email String password String? providerName Provider + name String? + lastName String? + pictureId String? + picture Media? @relation(fields: [pictureId], references: [id]) providerId String? organizations UserOrganization[] timezone Int @@ -40,6 +44,7 @@ model User { updatedAt DateTime @updatedAt lastReadNotifications DateTime @default(now()) inviteId String? + tagUser TagUser[] @@unique([email, providerName]) @@index([lastReadNotifications]) @@ -94,6 +99,21 @@ model TrendingLog { date DateTime } +model TagUser { + id String @id @default(uuid()) + user User @relation(fields: [userId], references: [id]) + userId String + tagOptions TagOptions @relation(fields: [tagOptionsId], references: [id]) + tagOptionsId String +} + +model TagOptions { + id String @id @default(uuid()) + key String + value String + tagUser TagUser[] +} + model Star { id String @id @default(uuid()) stars Int @@ -116,6 +136,7 @@ model Media { organizationId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + userPicture User[] @@index([organizationId]) } diff --git a/libraries/react-shared-libraries/src/form/checkbox.tsx b/libraries/react-shared-libraries/src/form/checkbox.tsx index a335d39d..59ef4790 100644 --- a/libraries/react-shared-libraries/src/form/checkbox.tsx +++ b/libraries/react-shared-libraries/src/form/checkbox.tsx @@ -1,17 +1,18 @@ 'use client'; -import { FC, useCallback, useState } from 'react'; +import { FC, forwardRef, useCallback, useState } from 'react'; import clsx from 'clsx'; import Image from 'next/image'; import { useFormContext, useWatch } from 'react-hook-form'; -export const Checkbox: FC<{ +export const Checkbox = forwardRef void; -}> = (props) => { - const { checked, className, disableForm } = props; + variant?: 'default' | 'hollow'; +}>((props, ref: any) => { + const { checked, className, disableForm, variant } = props; const form = useFormContext(); const register = disableForm ? {} : form.register(props.name!); @@ -31,10 +32,12 @@ export const Checkbox: FC<{ return (
@@ -43,4 +46,4 @@ export const Checkbox: FC<{ )}
); -}; +}); diff --git a/libraries/react-shared-libraries/src/form/slider.tsx b/libraries/react-shared-libraries/src/form/slider.tsx index 3e63fedb..0add647c 100644 --- a/libraries/react-shared-libraries/src/form/slider.tsx +++ b/libraries/react-shared-libraries/src/form/slider.tsx @@ -3,17 +3,17 @@ import {FC, useCallback} from "react"; import clsx from "clsx"; -export const Slider: FC<{value: 'on' | 'off', onChange: (value: 'on' | 'off') => void}> = (props) => { - const {value, onChange} = props; +export const Slider: FC<{value: 'on' | 'off', fill?: boolean, onChange: (value: 'on' | 'off') => void}> = (props) => { + const {value, onChange, fill} = props; const change = useCallback(() => { onChange(value === 'on' ? 'off' : 'on'); }, [value]); return ( -
+
) -} \ No newline at end of file +}