adding new logo and new sections
This commit is contained in:
parent
75cf1594ee
commit
6ca0233e31
@ -1,16 +1,16 @@
|
||||
import { RiMenu3Line, RiLeafFill } from "react-icons/ri";
|
||||
import { UserMenu } from "./components/UserMenu";
|
||||
import { Search } from "./components/Search";
|
||||
import { Notifications } from "./components/Notifications";
|
||||
import { MobileMenu } from "./components/MobileMenu";
|
||||
import Logo from "@/assets/Logo";
|
||||
export default function Header() {
|
||||
return (
|
||||
<header className="flex h-[var(--header-height)] shrink-0 grow-0 ">
|
||||
<div className="fixed z-header flex h-[var(--header-height)] w-full grow border-b bg-background p-5 sm:w-[calc(100vw_-_var(--sidebar-closed-width))] sm:border-b-0 sm:py-0 xl:w-[calc(100vw_-_var(--sidebar-open-width))]">
|
||||
<div className="flex flex-1 items-stretch justify-between gap-x-4 sm:border-b">
|
||||
<div className="center justify-between gap-x-3 text-foreground">
|
||||
<RiLeafFill className="h-6 w-6 sm:hidden" />
|
||||
<div className="font-semibold sm:text-lg">Flockstr</div>
|
||||
<Logo className="h-[30px] w-[30px] sm:hidden" />
|
||||
<div className="text-xl font-semibold">Flockstr</div>
|
||||
</div>
|
||||
<div className="flex grow items-center justify-end">
|
||||
<div className="sm:hidden">
|
||||
|
@ -1,14 +1,13 @@
|
||||
import Link from "next/link";
|
||||
import { RiLeafFill } from "react-icons/ri";
|
||||
|
||||
import Logo from "@/assets/Logo";
|
||||
export default function Keystone() {
|
||||
return (
|
||||
<div className="center hidden sm:flex">
|
||||
<Link
|
||||
href="/"
|
||||
className="center fixed h-[var(--header-height)] w-[var(--sidebar-closed-width)] border-r xl:w-[var(--sidebar-open-width)] xl:justify-start xl:pl-5"
|
||||
className="center fixed h-[var(--header-height)] w-[var(--sidebar-closed-width)] border-r hover:text-primary xl:w-[var(--sidebar-open-width)] xl:justify-start xl:pl-5"
|
||||
>
|
||||
<RiLeafFill className="h-6 w-6" />
|
||||
<Logo className="h-[30px] w-[30px]" />
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,9 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { RiCloseFill, RiLeafFill } from "react-icons/ri";
|
||||
import { RiCloseFill } from "react-icons/ri";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import useLocalStorage from "@/lib/hooks/useLocalStorage";
|
||||
import Logo from "@/assets/Logo";
|
||||
|
||||
export default function MobileBanner() {
|
||||
const [showPWAPrompt, setShowPWAPrompt] = useState(false);
|
||||
@ -25,7 +26,7 @@ export default function MobileBanner() {
|
||||
return (
|
||||
<div className="fixed bottom-[var(--bottom-nav-height)] flex w-screen items-center gap-3 border-t bg-card px-3 py-2.5 sm:hidden">
|
||||
<div className="center h-[32px] w-[32px] shrink-0 rounded-[6px] border bg-white shadow">
|
||||
<RiLeafFill className="text-black" />
|
||||
<Logo className="text-black" />
|
||||
</div>
|
||||
<div className="flex-1 text-sm font-medium text-foreground">
|
||||
Get our PWA
|
||||
|
30
app/(app)/app/_sections/BecomeACreator.tsx
Normal file
30
app/(app)/app/_sections/BecomeACreator.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import Image from "next/image";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
export default function BecomeACreator() {
|
||||
return (
|
||||
<div className="padded-container overflow-x-clip pb-8 md:py-[120px]">
|
||||
<div className="flex w-full flex-col items-center justify-between gap-8 lg:flex-row">
|
||||
<Image
|
||||
alt="creator icons"
|
||||
width={512}
|
||||
height={210.82}
|
||||
src={
|
||||
"https://whop.com/_next/image/?url=%2Fv2%2Fhomepage-whop-sellers-grid.png&w=1080&q=75"
|
||||
}
|
||||
className="h-44 object-cover object-bottom lg:h-auto"
|
||||
/>
|
||||
<div className="md:self-start lg:max-w-lg lg:self-center">
|
||||
<h2 className="font-condensed text-2xl font-bold text-foreground sm:text-3xl">
|
||||
Start earning on Nostr
|
||||
</h2>
|
||||
<div className="mb-6 mt-2 text-muted-foreground">
|
||||
Start earning bitcoin from your content. Create lists that for fans
|
||||
can subscribe to!
|
||||
</div>
|
||||
<Button>Become a Creator</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
123
app/(app)/app/_sections/FeaturedLists.tsx
Normal file
123
app/(app)/app/_sections/FeaturedLists.tsx
Normal file
@ -0,0 +1,123 @@
|
||||
"use client";
|
||||
import {
|
||||
Section,
|
||||
SectionHeader,
|
||||
SectionTitle,
|
||||
SectionContent,
|
||||
} from "@/containers/PageSection";
|
||||
import LiveBadge from "@/components/Badges/LiveBadge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { RiArrowRightLine } from "react-icons/ri";
|
||||
import VideoCard from "@/components/VideoCard";
|
||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import Image from "next/image";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Avatar, AvatarImage, AvatarFallback } from "@radix-ui/react-avatar";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
export default function FeaturedLists() {
|
||||
const demo = [
|
||||
{
|
||||
id: 1,
|
||||
title: "BTC Radio",
|
||||
picture:
|
||||
"https://assets.whop.com/cdn-cgi/image/width=1080/https://assets.whop.com/images/images/51602.original.png?1693358530",
|
||||
tags: ["music", "crypto", "art"],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "The Book of Alpha: NFTs and crypto taking over. Market Talk",
|
||||
picture:
|
||||
"https://assets.whop.com/cdn-cgi/image/width=1080/https://assets.whop.com/images/images/31095.thumbnail.png?1692203850",
|
||||
tags: ["NFTs", "crypto", "art", "trading"],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Space Talk: What's Elon up to?",
|
||||
picture:
|
||||
"https://assets.whop.com/cdn-cgi/image/width=1080/https://assets.whop.com/images/images/40088.original.png?1692206315",
|
||||
tags: ["Space"],
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "The Book of Alpha: NFTs and crypto taking over. Market Talk",
|
||||
picture:
|
||||
"https://assets.whop.com/cdn-cgi/image/width=1080/https://assets.whop.com/images/images/40680.original.png?1692206434",
|
||||
tags: ["Market"],
|
||||
},
|
||||
];
|
||||
return (
|
||||
<Section>
|
||||
<SectionHeader>
|
||||
<div className="center gap-x-2">
|
||||
<SectionTitle>Featured Lists</SectionTitle>
|
||||
</div>
|
||||
<Button variant={"ghost"}>
|
||||
View all <RiArrowRightLine className="ml-1 h-4 w-4" />
|
||||
</Button>
|
||||
</SectionHeader>
|
||||
<SectionContent className="sm:md-feed-cols relative flex flex-col gap-3">
|
||||
{demo.map((e) => (
|
||||
<Card key={e.id} className="max-sm:border-0 max-sm:shadow-none">
|
||||
<div className="hidden overflow-hidden rounded-t-md sm:flex">
|
||||
<Image
|
||||
width={250}
|
||||
height={150}
|
||||
src={e.picture}
|
||||
alt={e.title}
|
||||
unoptimized
|
||||
className={cn(
|
||||
"h-auto w-auto object-cover transition-all group-hover:scale-105",
|
||||
"aspect-video",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<CardContent className="flex gap-x-3 p-0 sm:p-4">
|
||||
<div className="shrink-0">
|
||||
<Avatar className="center h-[60px] w-[60px] overflow-hidden rounded-sm bg-muted sm:h-12 sm:w-12">
|
||||
<AvatarImage
|
||||
src={e.picture}
|
||||
alt="user"
|
||||
className="h-full w-auto max-w-none object-cover"
|
||||
/>
|
||||
<AvatarFallback className="text-sm">SC</AvatarFallback>
|
||||
</Avatar>
|
||||
</div>
|
||||
<div className="flex flex-1 justify-between gap-3 max-sm:items-center">
|
||||
<div className="flex flex-1 flex-col justify-between">
|
||||
<div className="">
|
||||
<CardTitle className="line-clamp-1 max-sm:text-sm">
|
||||
{e.title}
|
||||
</CardTitle>
|
||||
<CardDescription className="line-clamp-2 text-xs">
|
||||
Here is my description of this list that I am offering
|
||||
</CardDescription>
|
||||
</div>
|
||||
<div className="max-sm:hidden">
|
||||
<Badge variant={"outline"}>100 subs</Badge>
|
||||
</div>
|
||||
</div>
|
||||
<div className="gap-y-1.5 text-right">
|
||||
<div className="max-sm:hidden">
|
||||
<div className="text-sm font-medium">2k sats</div>
|
||||
<div className="text-[10px] text-muted-foreground">
|
||||
/month
|
||||
</div>
|
||||
</div>
|
||||
<Badge variant={"green"} className="">
|
||||
Free
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</SectionContent>
|
||||
</Section>
|
||||
);
|
||||
}
|
70
app/(app)/app/_sections/LiveStreaming.tsx
Normal file
70
app/(app)/app/_sections/LiveStreaming.tsx
Normal file
@ -0,0 +1,70 @@
|
||||
import {
|
||||
Section,
|
||||
SectionHeader,
|
||||
SectionTitle,
|
||||
SectionContent,
|
||||
} from "@/containers/PageSection";
|
||||
import LiveBadge from "@/components/Badges/LiveBadge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { RiArrowRightLine } from "react-icons/ri";
|
||||
import VideoCard from "@/components/VideoCard";
|
||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
||||
export default function LiveStreamingSection() {
|
||||
const demo = [
|
||||
{
|
||||
id: 1,
|
||||
title: "BTC Radio",
|
||||
picture:
|
||||
"https://assets.whop.com/cdn-cgi/image/width=1080/https://assets.whop.com/images/images/51602.original.png?1693358530",
|
||||
tags: ["music", "crypto", "art"],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "The Book of Alpha: NFTs and crypto taking over. Market Talk",
|
||||
picture:
|
||||
"https://assets.whop.com/cdn-cgi/image/width=1080/https://assets.whop.com/images/images/31095.thumbnail.png?1692203850",
|
||||
tags: ["NFTs", "crypto", "art", "trading"],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Space Talk: What's Elon up to?",
|
||||
picture:
|
||||
"https://assets.whop.com/cdn-cgi/image/width=1080/https://assets.whop.com/images/images/40088.original.png?1692206315",
|
||||
tags: ["Space"],
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "The Book of Alpha: NFTs and crypto taking over. Market Talk",
|
||||
picture:
|
||||
"https://assets.whop.com/cdn-cgi/image/width=1080/https://assets.whop.com/images/images/40680.original.png?1692206434",
|
||||
tags: ["Market"],
|
||||
},
|
||||
];
|
||||
return (
|
||||
<Section>
|
||||
<SectionHeader>
|
||||
<div className="center gap-x-2">
|
||||
<SectionTitle>Streaming Now</SectionTitle>
|
||||
<LiveBadge text={"LIVE"} />
|
||||
</div>
|
||||
<Button variant={"ghost"}>
|
||||
View all <RiArrowRightLine className="ml-1 h-4 w-4" />
|
||||
</Button>
|
||||
</SectionHeader>
|
||||
<SectionContent className="relative">
|
||||
<ScrollArea>
|
||||
<div className="flex space-x-2 pb-4">
|
||||
{demo.map((item) => (
|
||||
<VideoCard
|
||||
key={item.id}
|
||||
card={item}
|
||||
className="min-w-[250px] max-w-[350px]"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<ScrollBar orientation="horizontal" />
|
||||
</ScrollArea>
|
||||
</SectionContent>
|
||||
</Section>
|
||||
);
|
||||
}
|
@ -2,6 +2,16 @@ import { Button } from "@/components/ui/button";
|
||||
import HorizontalCarousel from "./_sections/HorizontalCarousel";
|
||||
import { RiArrowRightLine } from "react-icons/ri";
|
||||
import LongFormContentCard from "@/components/LongFormContentCard";
|
||||
import BecomeACreator from "./_sections/BecomeACreator";
|
||||
import {
|
||||
Section,
|
||||
SectionHeader,
|
||||
SectionTitle,
|
||||
SectionContent,
|
||||
} from "@/containers/PageSection";
|
||||
import LiveStreamingSection from "./_sections/LiveStreaming";
|
||||
import FeaturedListsSection from "./_sections/FeaturedLists";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="relative space-y-6 pt-5 sm:pt-7">
|
||||
@ -16,22 +26,23 @@ export default function Page() {
|
||||
</div>
|
||||
<HorizontalCarousel />
|
||||
</section>
|
||||
<section className="relative space-y-3 overflow-x-hidden sm:space-y-3">
|
||||
<div className="flex items-center justify-between max-sm:pr-3">
|
||||
<h2 className="font-condensed text-xl font-semibold sm:text-xl">
|
||||
Long form content
|
||||
</h2>
|
||||
<Section>
|
||||
<SectionHeader>
|
||||
<SectionTitle>Long form content</SectionTitle>
|
||||
<Button variant={"ghost"}>
|
||||
View all <RiArrowRightLine className="ml-1 h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="relative grid gap-4">
|
||||
</SectionHeader>
|
||||
<SectionContent className="sm:lg-feed-cols relative mx-auto flex flex-col gap-4">
|
||||
<LongFormContentCard />
|
||||
<LongFormContentCard />
|
||||
<LongFormContentCard />
|
||||
<LongFormContentCard />
|
||||
</div>
|
||||
</section>
|
||||
</SectionContent>
|
||||
</Section>
|
||||
<BecomeACreator />
|
||||
<LiveStreamingSection />
|
||||
<FeaturedListsSection />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -2,8 +2,8 @@
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { RiMenu3Line, RiLeafFill, RiCloseFill } from "react-icons/ri";
|
||||
|
||||
import { RiMenu3Line, RiCloseFill } from "react-icons/ri";
|
||||
import Logo from "@/assets/Logo";
|
||||
export default function Header() {
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const navigation = [
|
||||
@ -29,7 +29,7 @@ export default function Header() {
|
||||
</li>
|
||||
))}
|
||||
<Link href="/">
|
||||
<RiLeafFill className="h-8 w-8 text-primary" />
|
||||
<Logo className="h-8 w-8 text-primary" />
|
||||
</Link>
|
||||
{navigation.slice(2, 4).map((item) => (
|
||||
<li className="">
|
||||
@ -42,7 +42,7 @@ export default function Header() {
|
||||
</div>
|
||||
<div className="flex min-h-[var(--header-height)] flex-1 items-stretch justify-between gap-x-4 lg:hidden">
|
||||
<div className="center justify-between gap-x-3 text-primary">
|
||||
<RiLeafFill className="h-7 w-7" />
|
||||
<Logo className="h-7 w-7" />
|
||||
</div>
|
||||
<div className="flex grow items-center justify-end">
|
||||
<button
|
||||
|
@ -73,6 +73,27 @@
|
||||
.app-layout {
|
||||
@apply sm:layout-grid-small-sidebar xl:layout-grid-large-sidebar flex flex-col sm:grid;
|
||||
}
|
||||
.lg-feed-cols {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
|
||||
justify-items: stretch;
|
||||
justify-content: stretch;
|
||||
width: 100%;
|
||||
}
|
||||
.md-feed-cols {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
justify-items: stretch;
|
||||
justify-content: stretch;
|
||||
width: 100%;
|
||||
}
|
||||
.sm-feed-cols {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
justify-items: stretch;
|
||||
justify-content: stretch;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@layer components {
|
||||
.center {
|
||||
|
14
assets/Logo/index.tsx
Normal file
14
assets/Logo/index.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import type { SVGProps } from "react";
|
||||
|
||||
const Logo = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={188.303}
|
||||
height={289.598}
|
||||
viewBox="54.17 96.938 188.303 289.598"
|
||||
{...props}
|
||||
>
|
||||
<path d="M230.869 98.463a118.54 118.54 0 0 0-2.062 3.007 341.651 341.651 0 0 0-3.038 4.613c-7.325 11.203-15.753 17.585-30.313 26.161-14.559 8.576-30.42 10.49-63.656 28.87-5.655 3.127-19.19 9.271-19.642 12.208-.45 2.937-.281 14.794-.281 14.794.116 2.402-.032 3.511-.128 5.997-.095 2.486-.139 6.348.186 14.256.525 13.241-2.282 9.535-4.203 3.548-1.922-5.987-4.622-15.41-5.397-26.819-.4-5.951-.525-8.938-1.587-8.747-1.063.19-3.063 3.557-7.213 10.312-6.975 11.306-11.4 22.838-13.244 34.376-1.843 11.537-1.106 23.08 2.244 34.406.25.824.375 1.236.231 1.236-.143 0-.556-.412-1.381-1.236-2.975-3.006-5.337-7.248-6.956-12.304-1.619-5.056-2.494-10.925-2.494-17.185 0-4.407-.091-6.577-.868-6.318-.777.259-2.24 2.946-4.982 8.253-6.45 12.48-10.045 22.685-11.204 32.338-1.158 9.654.12 18.757 3.421 29.035 3.012 9.377 6.837 16.353 10.18 21.205 3.34 4.853 6.198 7.581 7.274 8.464 5.532 4.538 6.337 6.521 4.078 5.959-2.258-.562-7.581-3.67-14.306-9.312-6.8-5.663-10.5-8.392-11.225-8.144-.725.247 1.525 3.47 6.625 9.71 7.4 9.06 15.129 17.366 29.704 25.226 14.576 7.86 15.039 6.407 26.214 11.596 10.175 4.737 16.35 7.702 20.994 10.24 4.644 2.538 7.756 4.65 11.806 7.676 2.35 1.771 5.588 4.12 8.913 6.467 3.325 2.347 6.737 4.695 9.437 6.466 8.85 5.87 4.065-4.607-3.96-11.69-17.175-15.158-21.043-16.706-24.778-27.568-3.735-10.861-5.266-15.307 5.1-33.732 5.401-9.6 14.637-17.98 23.146-26.608 8.51-8.628 20.374-18.548 38.424-34.54 10.517-9.317 16.709-16.023 17.834-19.015 1.125-2.991-2.817-2.27-12.567 3.27-2.475 1.442-6.875 3.759-11.962 6.333a699.312 699.312 0 0 1-16.088 7.877 1981.677 1981.677 0 0 0-15.475 7.409c-4.712 2.28-8.6 4.185-10.525 5.153-7.625 3.872-11.616 6.915-14.007 8.692-2.391 1.778-3.183 2.288-4.408 1.094-.825-.824-.607-6.257 1.44-13.542 2.048-7.284 5.926-16.419 12.423-24.646l4.314-4.652 5.608-3.487s36.235-21.656 43.472-27.222c7.236-5.567 13.818-9.906 20.852-17.141 5.8-5.966 9.862-12.71 13.2-23.294 3.337-10.584 3.255-22.41-.32-39.893-2-9.802-3.437-15.63-4.762-18.488-1.325-2.857-2.538-2.744-4.088-.664Zm-25.627 64.881c-1.863 2.475-4.967 5.83-9.228 9.458-4.261 3.627-12.102 9.792-17.795 13.846-14.714 10.478-21.444 15.399-33.585 27.488-12.142 12.09-19.646 22.125-27.67 37.529-5.379 10.328-7.509 21.846-8.7 30.74-1.192 8.895-.115 12.854 1.014 24.396 1.918 9.248 1.333 13.436-1.667 4.062-3-9.373-3.992-18.894-2.85-28.948 2.393-21.084 13.206-41.627 27.407-58.617 14.2-16.99 23.827-24.467 52.125-45.241 17.024-12.498 14.794-12.12 19.233-15.42 4.438-3.3 11.06-11.702 1.716.707Z" />
|
||||
</svg>
|
||||
);
|
||||
export default Logo;
|
19
components/Badges/LiveBadge.tsx
Normal file
19
components/Badges/LiveBadge.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
type LiveBadgeProps = {
|
||||
text?: string;
|
||||
};
|
||||
export default function LiveBadge({ text }: LiveBadgeProps) {
|
||||
return (
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="relative h-[14px] w-[14px]">
|
||||
<div className="absolute h-[14px] w-[14px] animate-ping rounded-full bg-primary/30"></div>
|
||||
<div className="absolute h-[14px] w-[14px] animate-pulse rounded-full bg-primary/30"></div>
|
||||
<div className="absolute left-[3px] top-[3px] h-2 w-2 rounded-full bg-primary"></div>
|
||||
</div>
|
||||
{!!text && (
|
||||
<div className="h-[14px] text-[12px] font-semibold leading-[15px] text-primary">
|
||||
{text}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -29,7 +29,7 @@ type CreatorCardProps = {
|
||||
|
||||
export default function LongFormContentCard() {
|
||||
return (
|
||||
<Card className="relative max-w-[400px] overflow-hidden">
|
||||
<Card className="relative overflow-hidden">
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 p-4 pb-4">
|
||||
<div className="center gap-x-3">
|
||||
<Avatar className="center h-8 w-8 overflow-hidden rounded-sm bg-muted">
|
||||
|
52
components/VideoCard/index.tsx
Normal file
52
components/VideoCard/index.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
import Image from "next/image";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Badge } from "../ui/badge";
|
||||
import { RxClock } from "react-icons/rx";
|
||||
|
||||
type VideoCardProps = {
|
||||
card: {
|
||||
picture: string;
|
||||
title: string;
|
||||
tags: string[];
|
||||
};
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export default function VideoCard({ className, card }: VideoCardProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"group flex flex-col space-y-3 rounded-[16px] p-2 hover:bg-muted",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="overflow-hidden rounded-md">
|
||||
<Image
|
||||
src={card.picture}
|
||||
alt={card.title}
|
||||
width={250}
|
||||
height={150}
|
||||
unoptimized
|
||||
className={cn(
|
||||
"h-auto w-auto object-cover transition-all group-hover:scale-105",
|
||||
"aspect-video",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 space-y-2 text-base">
|
||||
<h3 className="line-clamp-2 font-medium leading-none">{card.title}</h3>
|
||||
<div className="flex flex-col items-start">
|
||||
<div className="center gap-x-1 text-xs text-muted-foreground">
|
||||
<RxClock className="h-4 w-4 text-primary" />
|
||||
<span>12:00 PM</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="-mt-1 flex gap-2 overflow-x-scroll">
|
||||
{card.tags.map((tag) => (
|
||||
<Badge>{tag}</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
import * as React from "react";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||
@ -12,6 +12,8 @@ const badgeVariants = cva(
|
||||
"border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
|
||||
secondary:
|
||||
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
green:
|
||||
"border-transparent bg-green-100 text-green-700 hover:bg-green-200/80",
|
||||
destructive:
|
||||
"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
|
||||
outline: "text-foreground",
|
||||
@ -20,8 +22,8 @@ const badgeVariants = cva(
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
export interface BadgeProps
|
||||
extends React.HTMLAttributes<HTMLDivElement>,
|
||||
@ -30,7 +32,7 @@ export interface BadgeProps
|
||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
return (
|
||||
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants }
|
||||
export { Badge, badgeVariants };
|
||||
|
53
components/ui/scroll-area.tsx
Normal file
53
components/ui/scroll-area.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const ScrollArea = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<ScrollAreaPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn("relative overflow-hidden", className)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
|
||||
{children}
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
<ScrollBar />
|
||||
<ScrollAreaPrimitive.Corner />
|
||||
</ScrollAreaPrimitive.Root>
|
||||
))
|
||||
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
|
||||
|
||||
const ScrollBar = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
>(({ className, orientation = "vertical", ...props }, ref) => (
|
||||
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
||||
ref={ref}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"flex touch-none select-none transition-colors",
|
||||
orientation === "vertical" &&
|
||||
"h-full w-2.5 border-l border-l-transparent p-[1px]",
|
||||
orientation === "horizontal" &&
|
||||
"h-2.5 border-t border-t-transparent p-[1px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.ScrollAreaThumb
|
||||
className={cn(
|
||||
"relative rounded-full bg-border",
|
||||
orientation === "vertical" && "flex-1"
|
||||
)}
|
||||
/>
|
||||
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
))
|
||||
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
|
||||
|
||||
export { ScrollArea, ScrollBar }
|
15
components/ui/skeleton.tsx
Normal file
15
components/ui/skeleton.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Skeleton({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<div
|
||||
className={cn("animate-pulse rounded-md bg-primary/10", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Skeleton }
|
52
containers/PageSection/index.tsx
Normal file
52
containers/PageSection/index.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Section = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative space-y-4 overflow-x-hidden sm:space-y-6",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
Section.displayName = "Section";
|
||||
|
||||
const SectionHeader = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("flex items-center justify-between max-sm:-mr-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SectionHeader.displayName = "SectionHeader";
|
||||
|
||||
const SectionTitle = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLHeadingElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<h3
|
||||
ref={ref}
|
||||
className={cn("font-condensed text-xl font-bold sm:text-2xl", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SectionTitle.displayName = "SectionTitle";
|
||||
|
||||
const SectionContent = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("relative", className)} {...props} />
|
||||
));
|
||||
SectionContent.displayName = "SectionContent";
|
||||
|
||||
export { Section, SectionHeader, SectionTitle, SectionContent };
|
@ -9,9 +9,8 @@ const nextConfig = {
|
||||
];
|
||||
},
|
||||
images: {
|
||||
domains: ["t2.gstatic.com", "www.google.com"],
|
||||
domains: ["t2.gstatic.com", "www.google.com", "whop.com"],
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
|
@ -20,6 +20,7 @@
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-scroll-area": "^1.0.5",
|
||||
"@radix-ui/react-select": "^2.0.0",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@radix-ui/react-switch": "^1.0.3",
|
||||
|
Loading…
x
Reference in New Issue
Block a user