diff --git a/components/ArtistPage/ArtistContent.tsx b/components/ArtistPage/ArtistContent.tsx new file mode 100644 index 0000000..34e9d66 --- /dev/null +++ b/components/ArtistPage/ArtistContent.tsx @@ -0,0 +1,62 @@ +import { useState } from "react"; + +import Card from "../LandingPage/Projects/Card"; +import ArtistTabBar from "./ArtistTabBar"; +import Collectors from "./Collectors"; +import { projects } from "data/projects"; + +interface Props { + artistAddress: string | undefined; + width: number; +} + +const ArtistContent = ({ artistAddress, width }: Props) => { + const [mainContent, setMainContent] = useState("projects"); + + if (mainContent == "projects") { + const filteredProjects = projects.filter((project) => { + if (project.artistAddress == artistAddress) { + return true; + } + }); + return ( +
+ + +
+
+ {filteredProjects.map((project) => ( + + ))} +
+
+ ); + } else if (mainContent == "collectors") { + return ( +
+ + +
+
+ +
+
+ ); + } +}; + +export default ArtistContent; diff --git a/components/ArtistPage/ArtistHead.tsx b/components/ArtistPage/ArtistHead.tsx new file mode 100644 index 0000000..8a4941e --- /dev/null +++ b/components/ArtistPage/ArtistHead.tsx @@ -0,0 +1,59 @@ +interface Props { + artistAddress: string; + artistBio: string; + artistName: string; +} + +export const ArtistHead = ({ artistAddress, artistBio, artistName }: Props) => { + return ( +
+
+ +
+
+

{artistName}

+

+ {artistAddress.slice(0, 5) + + "..." + + artistAddress.slice( + artistAddress.length - 5, + artistAddress.length, + )} +

+
+

{artistBio}

+
+

{artistBio}

+
+
+
+ +
+

{artistName}

+

+ {artistAddress.slice(0, 5) + + "..." + + artistAddress.slice( + artistAddress.length - 5, + artistAddress.length, + )} +

+
+
+

{artistBio}

+
+
+ ); +}; + +ArtistHead.defaultProps = { + artistAddress: "0xF8d9056db2C2189155bc25A30269dc5dDeD15d46", +}; diff --git a/components/ArtistPage/ArtistTabBar.tsx b/components/ArtistPage/ArtistTabBar.tsx new file mode 100644 index 0000000..b4d630e --- /dev/null +++ b/components/ArtistPage/ArtistTabBar.tsx @@ -0,0 +1,40 @@ +import Tab from "../LandingPage/Projects/Tab"; + +interface Props { + mainContent: string; + setMainContent: React.Dispatch>; + width: number; +} + +const ArtistTabBar = ({ + mainContent, + setMainContent, + width, +}: Props): React.JSX.Element => ( +
+
+ setMainContent("projects")} + > + Projects + + + setMainContent("collectors")} + > + Collectors + +
+
+); + +export default ArtistTabBar; diff --git a/components/ArtistPage/CollectorCard.tsx b/components/ArtistPage/CollectorCard.tsx new file mode 100644 index 0000000..33d1320 --- /dev/null +++ b/components/ArtistPage/CollectorCard.tsx @@ -0,0 +1,55 @@ +import React from "react"; + +interface Projects { + editionsOwned: number[]; + title: string; +} + +interface RowProps { + editionsOwned: number[]; + title: string; +} + +interface Props { + collectorName: string; + projects: Projects[]; +} + +const CollectorCard = ({ collectorName, projects }: Props) => { + const ProjectCollectRow = ({ editionsOwned, title }: RowProps) => { + return ( +
+

{title}

+
+ {editionsOwned.map((e, index) => ( + + {index == editionsOwned.length - 1 ? + "#" + e.toString() + : "#" + e.toString() + ", "} + + ))} +
+
+ ); + }; + + return ( +
+
+

{collectorName}

+
+ +
+ {projects.map((p) => ( + + ))} +
+
+ ); +}; + +export default CollectorCard; diff --git a/components/ArtistPage/Collectors.tsx b/components/ArtistPage/Collectors.tsx new file mode 100644 index 0000000..f425098 --- /dev/null +++ b/components/ArtistPage/Collectors.tsx @@ -0,0 +1,30 @@ +import React from "react"; + +import CollectorCard from "./CollectorCard"; +import { collectorData } from "data/collectors"; + +interface OwnedItem { + editionsOwned: number[]; + title: string; +} + +interface CollectorData { + name: string; + owned: OwnedItem[]; +} + +const Collectors = () => { + return ( +
+ {collectorData.map((collector?: CollectorData, index?: number) => ( + + ))} +
+ ); +}; + +export default Collectors; diff --git a/components/Header/MobileNav.tsx b/components/Header/MobileNav.tsx index 08f3584..902d5b8 100644 --- a/components/Header/MobileNav.tsx +++ b/components/Header/MobileNav.tsx @@ -7,16 +7,16 @@ import type { SetState } from "utils/types"; import ConnectButton from "./ConnectButton"; import SocialIcons from "./SocialIcons"; -import { artistUrls, projects } from "data/projects"; -import { cn } from "utils/helpers"; +import { projects } from "data/projects"; +import { cn, deKebabify } from "utils/helpers"; interface Props { isOpen: boolean; setIsOpen: SetState; } -const artists = projects.map(({ artist }) => artist); -const uniqueArtists = [...new Set(artists)]; +const artistSlugs = projects.map(({ artistSlug }) => artistSlug); +const uniqueArtists = [...new Set(artistSlugs)]; const MobileNav = ({ isOpen, setIsOpen }: Props): React.JSX.Element => { const clickHandler = () => setIsOpen(false); @@ -56,19 +56,15 @@ const MobileNav = ({ isOpen, setIsOpen }: Props): React.JSX.Element => {

Artists

    - {uniqueArtists.map((artist) => { - const url = artistUrls[artist]; - + {uniqueArtists.map((slug) => { return ( - - {artist} - + {deKebabify(slug)} + ); })}
diff --git a/components/Header/NavMenu.tsx b/components/Header/NavMenu.tsx index c352364..b6f669c 100644 --- a/components/Header/NavMenu.tsx +++ b/components/Header/NavMenu.tsx @@ -11,11 +11,11 @@ import { NavigationMenuTrigger, navigationMenuTriggerStyle, } from "components/shadcn/NavigationMenu"; -import { artistUrls, projects } from "data/projects"; -import { cn } from "utils/helpers"; +import { projects } from "data/projects"; +import { cn, deKebabify } from "utils/helpers"; -const artists = projects.map(({ artist }) => artist); -const uniqueArtists = [...new Set(artists)]; +const artistSlugs = projects.map(({ artistSlug }) => artistSlug); +const uniqueArtists = [...new Set(artistSlugs)]; export const NavMenu = () => { return ( @@ -50,18 +50,11 @@ export const NavMenu = () => { Artists
    - {uniqueArtists.map((artist) => { - const url = artistUrls[artist]; - + {uniqueArtists.map((slug) => { return ( - - {artist} - + + {deKebabify(slug)} + ); })}
diff --git a/components/LandingPage/Projects/Card.tsx b/components/LandingPage/Projects/Card.tsx index be4f0ac..6d906dd 100644 --- a/components/LandingPage/Projects/Card.tsx +++ b/components/LandingPage/Projects/Card.tsx @@ -12,7 +12,7 @@ interface Props { const Card = ({ project }: Props): React.JSX.Element => { const { - artist, + artistSlug: artist, externalUrl, image, local, diff --git a/components/LandingPage/Projects/Grid.tsx b/components/LandingPage/Projects/Grid.tsx index 9b124c7..bc593d1 100644 --- a/components/LandingPage/Projects/Grid.tsx +++ b/components/LandingPage/Projects/Grid.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; -import { type ProjectSort, Status, projects } from "../../../data/projects"; +import { type ProjectSort, projects } from "../../../data/projects"; import Card from "./Card"; import TabBar from "./TabBar"; @@ -12,14 +12,14 @@ const Grid = ({ width }: Props): React.JSX.Element => { const [activeTab, setActiveTab] = useState("all"); const filteredProjects = projects.filter((project) => { - if (activeTab === "minting") { - return project.status === Status.Minting; + if (activeTab === "Minting") { + return project.status === "Minting"; } - if (activeTab === "upcoming") { - return project.status === Status.Upcoming; + if (activeTab === "Upcoming") { + return project.status === "Upcoming"; } - if (activeTab === "closed") { - return project.status === Status.Closed; + if (activeTab === "Closed") { + return project.status === "Closed"; } return true; }); diff --git a/components/LandingPage/Projects/TabBar.tsx b/components/LandingPage/Projects/TabBar.tsx index 342257c..c6632bf 100644 --- a/components/LandingPage/Projects/TabBar.tsx +++ b/components/LandingPage/Projects/TabBar.tsx @@ -29,22 +29,22 @@ const TabBar = ({ setActiveTab("minting")} + active={activeTab === "Minting"} + onClick={() => setActiveTab("Minting")} > Minting setActiveTab("upcoming")} + active={activeTab === "Upcoming"} + onClick={() => setActiveTab("Upcoming")} > Upcoming setActiveTab("closed")} + active={activeTab === "Closed"} + onClick={() => setActiveTab("Closed")} > Closed diff --git a/components/ProjectPage/Details/OtherCollections/CollectionCard.tsx b/components/ProjectPage/Details/OtherCollections/CollectionCard.tsx index 146cb63..2a8bb67 100644 --- a/components/ProjectPage/Details/OtherCollections/CollectionCard.tsx +++ b/components/ProjectPage/Details/OtherCollections/CollectionCard.tsx @@ -11,7 +11,7 @@ interface Props { const Card = ({ project }: Props): JSX.Element => { const { - artist, + artistSlug: artist, externalUrl, image, local, diff --git a/components/ProjectPage/ProjectHead.tsx b/components/ProjectPage/ProjectHead.tsx index 38cdf68..e6e0ffd 100644 --- a/components/ProjectPage/ProjectHead.tsx +++ b/components/ProjectPage/ProjectHead.tsx @@ -11,7 +11,7 @@ interface Props { } const ProjectHead = ({ project, tab }: Props) => { - const { artist, maxSupply, name, projectSlug, website } = project; + const { artistSlug: artist, maxSupply, name, projectSlug, website } = project; const currentSupply = useCurrentSupply(projectSlug); const maxSupplyText = maxSupply > 999_999 ? "?" : intlNumberFormat(maxSupply); diff --git a/components/TokenPage/TokenPage.tsx b/components/TokenPage/TokenPage.tsx index 4efcf08..6248764 100644 --- a/components/TokenPage/TokenPage.tsx +++ b/components/TokenPage/TokenPage.tsx @@ -15,7 +15,7 @@ interface Props { } const TokenPage = ({ project, tokenId }: Props): JSX.Element => { - const { artist, name, projectSlug, website } = project; + const { artistSlug: artist, name, projectSlug, website } = project; const projectLink = `/project/${projectSlug}`; diff --git a/data/collectors.ts b/data/collectors.ts new file mode 100644 index 0000000..d48888e --- /dev/null +++ b/data/collectors.ts @@ -0,0 +1,44 @@ +export interface OwnedItem { + editionsOwned: number[]; + title: string; +} + +export interface CollectorData { + name: string; + owned: OwnedItem[]; +} + +export const collectorData: CollectorData[] = [ + { + name: "collector.eth", + owned: [ + { + editionsOwned: [11, 24, 81], + title: "Project1", + }, + { + editionsOwned: [24, 13], + title: "Project2", + }, + { + editionsOwned: [108], + title: "Project3", + }, + ], + }, + { + name: "collector2.eth", + owned: [ + { + editionsOwned: [1, 2, 3], + title: "Project1", + }, + { + editionsOwned: [12, 17], + title: "Project2", + }, + ], + }, +]; + +export default collectorData; diff --git a/data/projects.ts b/data/projects.ts index 26e5770..24331c8 100644 --- a/data/projects.ts +++ b/data/projects.ts @@ -5,25 +5,18 @@ if (!blobRoot && env !== "test") { throw new Error("NEXT_PUBLIC_BLOB_ROOT is not defined"); } -export enum Status { - Closed = "Closed", - Minting = "Minting", - Upcoming = "Upcoming", -} +export type Status = "Closed" | "Minting" | "Upcoming"; -export enum Artist { - ImmutableComputer = "Immutable Computer", - Matto = "Matto", -} +export type Artist = "immutable-computer" | "matto"; export const artistUrls: Record = { - [Artist.ImmutableComputer]: "http://immutablecomputer.com/", - [Artist.Matto]: "https://matto.xyz", + "immutable-computer": "http://immutablecomputer.com/", + matto: "https://matto.xyz", }; export interface Project { - artist: Artist; artistAddress?: string; + artistSlug: Artist; aspectRatio: number; contractAddress: string; externalUrl?: string; @@ -57,8 +50,8 @@ export interface Project { export const projects: Project[] = [ { - artist: Artist.Matto, - artistAddress: "0xF8d9056db2C2189155bc25A30269dc5dDeD15d46", + artistSlug: "matto", + // artistAddress: "0xF8d9056db2C2189155bc25A30269dc5dDeD15d46", //removed for now, causing errors when retrieving image data aspectRatio: 0.5625, contractAddress: "0xCC55af23d9861e41C5875F1e76fb3c4122E8C4Fa", externalUrl: "https://substratum.art/project/100x10x1", @@ -84,12 +77,13 @@ export const projects: Project[] = [ }, projectSlug: "100x10x1-a-goerli", scriptType: "Solidity", - status: Status.Upcoming, + status: "Upcoming", usesTransfers: false, website: "https://matto.xyz/project/100x10x1", }, { - artist: Artist.Matto, + artistAddress: "0xF8d9056db2C2189155bc25A30269dc5dDeD15d46", + artistSlug: "matto", aspectRatio: 1, contractAddress: "0x74C093fD987Fff140677Aa83B6CC4680B8ef2956", image: "/projects/haiku.png", @@ -109,12 +103,13 @@ export const projects: Project[] = [ }, projectSlug: "haiku", sansaSlug: "651d85db72a482be8d9417b2", - status: Status.Minting, + status: "Minting", usesTransfers: false, website: "https://matto.xyz/project/freestyle-h-ai-ku/", }, { - artist: Artist.Matto, + artistAddress: "0xF8d9056db2C2189155bc25A30269dc5dDeD15d46", + artistSlug: "matto", aspectRatio: 1, contractAddress: "0x5B17395A9699D2819a9d009bA375a0825b077385", image: "/projects/crystallized-illusions.png", @@ -138,13 +133,13 @@ export const projects: Project[] = [ }, projectSlug: "crystallized-illusions", scriptType: "p5", - status: Status.Closed, + status: "Closed", useTokenName: true, usesTransfers: false, website: "https://matto.xyz", }, { - artist: Artist.ImmutableComputer, + artistSlug: "immutable-computer", aspectRatio: 1.777777777777777, contractAddress: "0xa9132D23886b63D29858Fe541214fEad5815d64A", image: "/projects/negative-carbon.png", @@ -161,17 +156,18 @@ export const projects: Project[] = [ "Each Negative Carbon NFT (NCNFT) offsets more than its carbon footprint using rigorously validated, third-party audited, retired, carbon offsets. Each token is assigned an offset certificate and mint, and that certificate's serial number becomes the token's generative art's entropy. For more information, visit http://immutablecomputer.com/carbon.html.", license: "CC BY-NC 4.0", notes: - "Token entropy is determined by its carbon offset serial number, and is not publicly mintable. The artist is minting, giving, and selling tokens as they see fit.", + "Token entropy is determined by its carbon offset serial number, and is not publicly mintable. The artist is Minting, giving, and selling tokens as they see fit.", tools: "Solidity, p5.js", }, projectSlug: "negative-carbon", scriptType: "p5.js", - status: Status.Minting, + status: "Minting", usesTransfers: true, website: "http://immutablecomputer.com/carbon.html", }, { - artist: Artist.Matto, + artistAddress: "0xF8d9056db2C2189155bc25A30269dc5dDeD15d46", + artistSlug: "matto", aspectRatio: 1, contractAddress: "0x2eEa9f8eb2a3365175c7cb25Db9ae277bE218806", image: "/projects/mathare-memories.png", @@ -197,12 +193,13 @@ export const projects: Project[] = [ projectSlug: "mathare-memories", sansaSlug: "mathare-memories-by-matto", scriptType: "p5.js", - status: Status.Closed, + status: "Closed", usesTransfers: true, website: "https://matto.xyz", }, { - artist: Artist.Matto, + artistAddress: "0xF8d9056db2C2189155bc25A30269dc5dDeD15d46", + artistSlug: "matto", aspectRatio: 1, contractAddress: "0x15BF7610a7d50541e865EfA3adad434147a4E1A9", image: "/projects/texture.svg", @@ -225,12 +222,13 @@ export const projects: Project[] = [ projectSlug: "texture-and-hues", sansaSlug: "texture-and-hues-by-matto", scriptType: "solidity", - status: Status.Closed, + status: "Closed", usesTransfers: false, website: "https://matto.xyz", }, { - artist: Artist.Matto, + artistAddress: "0xF8d9056db2C2189155bc25A30269dc5dDeD15d46", + artistSlug: "matto", aspectRatio: 1, contractAddress: "0x4E171e0F14a9046e14B93221f31Acd2EC4Af8429", externalUrl: "https://chainlife.xyz/", @@ -254,12 +252,13 @@ export const projects: Project[] = [ projectSlug: "chainlife", sansaSlug: "chainlife-by-matto", scriptType: "p5.js", - status: Status.Minting, + status: "Minting", usesTransfers: true, website: "https://matto.xyz", }, { - artist: Artist.Matto, + artistAddress: "0xF8d9056db2C2189155bc25A30269dc5dDeD15d46", + artistSlug: "matto", aspectRatio: 1, contractAddress: "0x7f463b874eC264dC7BD8C780f5790b4Fc371F11f", externalUrl: "https://blonks.xyz/", @@ -282,10 +281,10 @@ export const projects: Project[] = [ }, projectSlug: "blonks", sansaSlug: "blonks-by-matto", - status: Status.Closed, + status: "Closed", usesTransfers: true, website: "https://matto.xyz", }, ]; -export type ProjectSort = "all" | "closed" | "minting" | "upcoming"; +export type ProjectSort = "Closed" | "Minting" | "Upcoming" | "all"; diff --git a/pages/artist/[artistSlug]/index.tsx b/pages/artist/[artistSlug]/index.tsx new file mode 100644 index 0000000..766efe3 --- /dev/null +++ b/pages/artist/[artistSlug]/index.tsx @@ -0,0 +1,54 @@ +import type { NextPage } from "next"; + +import Head from "next/head"; +import { useRouter } from "next/router"; +import React, { useEffect, useState } from "react"; + +import ArtistContent from "components/ArtistPage/ArtistContent"; +import { ArtistHead } from "components/ArtistPage/ArtistHead"; +import { projects } from "data/projects"; +import { deKebabify } from "utils/helpers"; + +const Artist: NextPage = () => { + const { artistSlug } = useRouter().query; + + const [name, setName] = useState(""); + const [bio, setBio] = useState(""); + const [artistAddress, setArtistAddress] = useState(); + + // const artistAddress = "0xF8d9056db2C2189155bc25A30269dc5dDeD15d46"; + // Find artist info from projects + // useEffect for future database hookup + useEffect(() => { + // Find an entry with matching address and pull artist data + const thisInfo = projects.find((p) => p.artistSlug === artistSlug); + if (thisInfo) { + setName(deKebabify(thisInfo.artistSlug)); + setArtistAddress(thisInfo.artistAddress); + + // Would be thisInfo.bio when using data object returned by API + setBio( + "A bio for an artist that takes up a bit more space so we know how much room we're really working with in a situation like this.", + ); + } else { + setName("Artist doesn't exist"); + } + }, [artistSlug]); + + return ( +
+ + substratum | {name} + + + + +
+ ); +}; + +export default Artist; diff --git a/utils/helpers.ts b/utils/helpers.ts index 650ec49..3d2317c 100644 --- a/utils/helpers.ts +++ b/utils/helpers.ts @@ -48,3 +48,10 @@ export const getEtherscanUrl = ({ export const cn = (...inputs: ClassValue[]) => { return twMerge(clsx(inputs)); }; + +export const deKebabify = (kebabStr: string): string => { + return kebabStr + .split("-") // Split the string into an array of words. + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) // Capitalize the first letter of each word. + .join(" "); // Join the words with a space. +};