diff --git a/apps/frontend/public/icons/received.svg b/apps/frontend/public/icons/received.svg new file mode 100644 index 00000000..c7b66d9d --- /dev/null +++ b/apps/frontend/public/icons/received.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/frontend/public/icons/sent.svg b/apps/frontend/public/icons/sent.svg new file mode 100644 index 00000000..9c9bafb6 --- /dev/null +++ b/apps/frontend/public/icons/sent.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/frontend/src/components/icons/Icons.tsx b/apps/frontend/src/components/icons/Icons.tsx index 68eef688..2a0e3c3a 100644 --- a/apps/frontend/src/components/icons/Icons.tsx +++ b/apps/frontend/src/components/icons/Icons.tsx @@ -12,6 +12,7 @@ import { IoMdRemove as Remove } from "react-icons/io"; import { FaRegCalendar as Calendar } from "react-icons/fa"; import { IoMenu as Menu } from "react-icons/io5"; import { IoIosCloseCircle as CloseLocation } from "react-icons/io"; +import { LuHourglass as Hourglass } from "react-icons/lu"; // eslint-disable-next-line @typescript-eslint/no-explicit-any export const Icons: Record = { @@ -29,6 +30,41 @@ export const Icons: Record = { Calendar, Menu, CloseLocation, + Hourglass, + // @ts-ignore + Job: (props: any) => ( + + + + ), + // @ts-ignore + Star: (props: any) => ( + + + + ), Checked: () => ( ; @@ -11,6 +17,8 @@ const CardIconMapping: Record = { location: "/icons/location.svg", proof: "/icons/proof.svg", overlap: "/icons/overlap.svg", + received: "/icons/received.svg", + sent: "/icons/sent.svg", }; interface CircleCardProps extends CardIconVariants { diff --git a/apps/frontend/src/components/ui/FeedContent.tsx b/apps/frontend/src/components/ui/FeedContent.tsx index f7291a20..dbc7cf38 100644 --- a/apps/frontend/src/components/ui/FeedContent.tsx +++ b/apps/frontend/src/components/ui/FeedContent.tsx @@ -1,10 +1,13 @@ +import { cn } from "@/lib/frontend/util"; import { classed } from "@tw-classed/react"; interface FeedContentProps { + className?: string; title: React.ReactNode; titleOverride?: boolean; - description: string; + description: React.ReactNode; icon: React.ReactNode; + color?: string; } export const IconCircle = classed.div( "flex justify-center items-center h-6 w-6 rounded-full text-label-primary", @@ -12,6 +15,7 @@ export const IconCircle = classed.div( variants: { color: { primary: "bg-button-secondary", + transparent: "bg-transparent", }, border: { true: "", @@ -30,11 +34,18 @@ export const FeedContent = ({ description, titleOverride, icon, + className = "", + color = "primary", }: FeedContentProps) => { return ( -
+
- {icon} + {icon} {titleOverride === true ? ( {title} @@ -45,9 +56,13 @@ export const FeedContent = ({ )}
- - {description} - + {typeof description === "string" ? ( + + {description} + + ) : ( + description + )}
); }; diff --git a/apps/frontend/src/components/ui/NavTab.tsx b/apps/frontend/src/components/ui/NavTab.tsx new file mode 100644 index 00000000..251c94ce --- /dev/null +++ b/apps/frontend/src/components/ui/NavTab.tsx @@ -0,0 +1,10 @@ +import { classed } from "@tw-classed/react"; + +export const NavTab = classed.button("text-sm duration-200", { + variants: { + active: { + true: "font-semibold underline underline-offset-8 text-label-primary", + false: "font-normal text-label-quaternary", + }, + }, +}); diff --git a/apps/frontend/src/config.ts b/apps/frontend/src/config.ts index a0d21c0f..3d871a1b 100644 --- a/apps/frontend/src/config.ts +++ b/apps/frontend/src/config.ts @@ -16,7 +16,8 @@ export const FRONTEND_URL = export const BASE_API_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8080/api"; -export const BASE_API_WS = process.env.NEXT_PUBLIC_API_WS || "ws://localhost:8080"; +export const BASE_API_WS = + process.env.NEXT_PUBLIC_API_WS || "ws://localhost:8080"; export const OAUTH_APP_DETAILS: Record = { strava: { @@ -83,8 +84,8 @@ export const ROUTER_ITEMS: RouterItem[] = [ iconSize: 20, }, { - label: "Narrowcast", - href: "/narrowcast", + label: "Digital pheromones", + href: "/pheromones", icon: Icons.NarrowCast, iconSize: 20, }, diff --git a/apps/frontend/src/pages/narrowcast/index.tsx b/apps/frontend/src/pages/narrowcast/index.tsx deleted file mode 100644 index e46592bf..00000000 --- a/apps/frontend/src/pages/narrowcast/index.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { Banner } from "@/components/cards/Banner"; -import AppLayout from "@/layouts/AppLayout"; - -export default function NarrowcastPage() { - return ( - - Narrowcasting -
- - } - withContainer={false} - > -
- -
- Coming soon: Narrowcast events & opportunities to{" "} - only your most relevant connections instead of - broadcasting to the whole community or making dozens of group - chats. -
-
- Learn more about narrowcasting and other upcoming features like{" "} - digital pheromones and superconnectors{" "} - - here - - . -
-
- } - textCenter={false} - /> -
- - ); -} diff --git a/apps/frontend/src/pages/pheromones/cv/index.tsx b/apps/frontend/src/pages/pheromones/cv/index.tsx new file mode 100644 index 00000000..eb081327 --- /dev/null +++ b/apps/frontend/src/pages/pheromones/cv/index.tsx @@ -0,0 +1,3 @@ +export default function CvPage() { + return <>Cv page; +} diff --git a/apps/frontend/src/pages/pheromones/index.tsx b/apps/frontend/src/pages/pheromones/index.tsx new file mode 100644 index 00000000..54ca6be7 --- /dev/null +++ b/apps/frontend/src/pages/pheromones/index.tsx @@ -0,0 +1,90 @@ +import { NavTab } from "@/components/ui/NavTab"; +import AppLayout from "@/layouts/AppLayout"; +import { useState } from "react"; +import { PheromonesCreate } from "./sections/PheromonesCreate"; +import { PheromonesMatches } from "./sections/PheromonesMatches"; +import { PheromonesSent } from "./sections/PheromonesSent"; +import { PheromonesReceived } from "./sections/PheromonesReceived"; + +enum ActiveTab { + CREATE, + SENT, + RECEIVED, + MATCHES, +} + +export default function PheromonesPage() { + const [activeTab, setActiveTab] = useState(ActiveTab.CREATE); + + const ViewMapping: Record = { + [ActiveTab.CREATE]: PheromonesCreate, + [ActiveTab.SENT]: PheromonesSent, + [ActiveTab.RECEIVED]: PheromonesReceived, + [ActiveTab.MATCHES]: PheromonesMatches, + }; + + return ( + + + Digital pheromones + +
+ { + setActiveTab(ActiveTab.CREATE); + }} + > + Create + + { + setActiveTab(ActiveTab.SENT); + }} + > + Sent + + { + setActiveTab(ActiveTab.RECEIVED); + }} + > + Received + + { + setActiveTab(ActiveTab.MATCHES); + }} + > + Matches + +
+
+ + } + withContainer={false} + > +
+ + Multi-party computation enables{" "} + digital pheromones, the ability to + coordinate in a p2p way using lightweight, privacy-preserving signals. + +
+ {ViewMapping?.[activeTab]?.() ?? null} +
+
+
+ ); +} diff --git a/apps/frontend/src/pages/pheromones/jobs/index.tsx b/apps/frontend/src/pages/pheromones/jobs/index.tsx new file mode 100644 index 00000000..46b7fbe7 --- /dev/null +++ b/apps/frontend/src/pages/pheromones/jobs/index.tsx @@ -0,0 +1,3 @@ +export default function JobsPage() { + return null; +} diff --git a/apps/frontend/src/pages/pheromones/sections/PheromonesCreate.tsx b/apps/frontend/src/pages/pheromones/sections/PheromonesCreate.tsx new file mode 100644 index 00000000..ce0f66a9 --- /dev/null +++ b/apps/frontend/src/pages/pheromones/sections/PheromonesCreate.tsx @@ -0,0 +1,101 @@ +"use client"; +import { Card } from "@/components/cards/Card"; +import { Icons } from "@/components/icons/Icons"; +import { CircleCard } from "@/components/ui/CircleCard"; +import { FeedContent } from "@/components/ui/FeedContent"; +import Link from "next/link"; +import { MdKeyboardArrowRight as ArrowIcon } from "react-icons/md"; + +export const PheromonesCreate = () => { + return ( +
+
+ + Narrowcast{" "} + (private queries on social graph) + +
+ + +
+
+ + + CV + +
+ +
+ + Find jobs you qualify for. + +
+ + + +
+
+ + + Job + +
+ +
+ + Get matched with qualified candidates. + +
+ + +
+
+ + + Complimentary skill match + +
+ +
+ + Get matched with qualified candidates. + +
+
+
+
+ + {`I'm Feeling Serendipitous`} + + + +
+
+ + + Meet someone new this hour + +
+ +
+ + Get matched with qualified candidates. + +
+ +
+
+ ); +}; diff --git a/apps/frontend/src/pages/pheromones/sections/PheromonesMatches.tsx b/apps/frontend/src/pages/pheromones/sections/PheromonesMatches.tsx new file mode 100644 index 00000000..5ce50d37 --- /dev/null +++ b/apps/frontend/src/pages/pheromones/sections/PheromonesMatches.tsx @@ -0,0 +1,48 @@ +"use client"; + +import { CircleCard } from "@/components/ui/CircleCard"; +import { FeedContent } from "@/components/ui/FeedContent"; +import Link from "next/link"; +import { MdKeyboardArrowRight as ArrowIcon } from "react-icons/md"; + +export const PheromonesMatches = () => { + return ( +
+
+ + Date + +
    + + Lorem, ipsum dolor. + } + icon={} + description={ +
    + +
    + } + /> + Lorem, ipsum dolor. + } + icon={} + description={ +
    + +
    + } + /> + +
+
+
+ ); +}; diff --git a/apps/frontend/src/pages/pheromones/sections/PheromonesReceived.tsx b/apps/frontend/src/pages/pheromones/sections/PheromonesReceived.tsx new file mode 100644 index 00000000..9fdc19dd --- /dev/null +++ b/apps/frontend/src/pages/pheromones/sections/PheromonesReceived.tsx @@ -0,0 +1,54 @@ +"use client"; + +import { CircleCard } from "@/components/ui/CircleCard"; +import { FeedContent } from "@/components/ui/FeedContent"; +import Link from "next/link"; +import { MdKeyboardArrowRight as ArrowIcon } from "react-icons/md"; + +export const PheromonesReceived = () => { + return ( +
+
+ + Date + +
    + + Lorem, ipsum dolor. + } + icon={} + description={ +
    + + opt-in to match + + +
    + } + /> + Lorem, ipsum dolor. + } + icon={} + description={ +
    + + opt-in to match + + +
    + } + /> + +
+
+
+ ); +}; diff --git a/apps/frontend/src/pages/pheromones/sections/PheromonesSent.tsx b/apps/frontend/src/pages/pheromones/sections/PheromonesSent.tsx new file mode 100644 index 00000000..f0d8f734 --- /dev/null +++ b/apps/frontend/src/pages/pheromones/sections/PheromonesSent.tsx @@ -0,0 +1,48 @@ +"use client"; + +import { CircleCard } from "@/components/ui/CircleCard"; +import { FeedContent } from "@/components/ui/FeedContent"; +import Link from "next/link"; +import { MdKeyboardArrowRight as ArrowIcon } from "react-icons/md"; + +export const PheromonesSent = () => { + return ( +
+
+ + Date + +
    + + Lorem, ipsum dolor. + } + icon={} + description={ +
    + +
    + } + /> + Lorem, ipsum dolor. + } + icon={} + description={ +
    + +
    + } + /> + +
+
+
+ ); +};