diff --git a/.storybook/main.ts b/.storybook/main.ts index 48d59b0..c6268fd 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -14,6 +14,7 @@ const config: StorybookConfig = { name: '@storybook/nextjs', options: {}, }, + staticDirs: ['../public'], }; export default config; diff --git a/app/layout.tsx b/app/layout.tsx index 76c7667..2420930 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,7 @@ import type { Metadata } from 'next'; import type { PropsWithChildren } from 'react'; +import { AppLayout } from '@/components/layout/app-layout'; import { ThemeProvider } from '@/components/theme-provider'; import '@/components/theme-provider/styles.css'; @@ -13,7 +14,9 @@ export default function RootLayout({ children }: PropsWithChildren) { return ( - {children} + + {children} + ); diff --git a/components/layout/app-content.tsx b/components/layout/app-content.tsx new file mode 100644 index 0000000..e48a2a3 --- /dev/null +++ b/components/layout/app-content.tsx @@ -0,0 +1,17 @@ +import type { PropsWithChildren } from 'react'; + +import { Container } from '@radix-ui/themes'; + +export function AppContent(props: PropsWithChildren) { + return ( + + {props.children} + + ); +} diff --git a/components/layout/app-footer.stories.tsx b/components/layout/app-footer.stories.tsx new file mode 100644 index 0000000..f9bb2d9 --- /dev/null +++ b/components/layout/app-footer.stories.tsx @@ -0,0 +1,13 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { AppFooter } from './app-footer'; + +export const Default: StoryObj = { + render: () => , +}; + +const meta: Meta = { + component: AppFooter, +}; + +export default meta; diff --git a/components/layout/app-footer.tsx b/components/layout/app-footer.tsx new file mode 100644 index 0000000..bb90d84 --- /dev/null +++ b/components/layout/app-footer.tsx @@ -0,0 +1,82 @@ +import { GitHubLogoIcon, TwitterLogoIcon } from '@radix-ui/react-icons'; +import { Flex, Link, Text } from '@radix-ui/themes'; +import React from 'react'; + +import { AppContent } from './app-content'; + +// @TODO: replace with real links: SUPA-27 +const FOOTER_SERVICE_LINKS = [ + { + href: 'https://experiment.st', + label: 'Privacy policy', + }, + { + href: 'https://experiment.st', + label: 'Terms of service', + }, +] as const; + +const FOOTER_SOCIAL_LINKS = [ + { + href: 'https://github.com/experiment-station/beecast', + icon: , + title: 'GitHub', + }, + { + href: 'https://twitter.com/beecast_ai', + icon: , + title: 'Twitter', + }, +] as const; + +export function AppFooter() { + return ( + + + + + Built with ☕️ by your folks at the{' '} + + Experiment Station + + + + + {FOOTER_SERVICE_LINKS.map((link) => ( + + + {link.label} + + + {link !== + FOOTER_SERVICE_LINKS[FOOTER_SERVICE_LINKS.length - 1] ? ( + · + ) : null} + + ))} + + + + + + {FOOTER_SOCIAL_LINKS.map((link) => ( + + {link.icon} + + ))} + + + + ©2023 Experiment Station + + + + + ); +} diff --git a/components/layout/app-header.stories.tsx b/components/layout/app-header.stories.tsx new file mode 100644 index 0000000..b3d1379 --- /dev/null +++ b/components/layout/app-header.stories.tsx @@ -0,0 +1,26 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { AppHeader } from './app-header'; + +export const Guest: StoryObj = { + render: () => , +}; + +export const Authenticated: StoryObj = { + render: () => ( + + ), +}; + +const meta: Meta = { + component: AppHeader, +}; + +export default meta; diff --git a/components/layout/app-header.tsx b/components/layout/app-header.tsx new file mode 100644 index 0000000..0d15cb8 --- /dev/null +++ b/components/layout/app-header.tsx @@ -0,0 +1,117 @@ +import { ArrowRightIcon, PersonIcon } from '@radix-ui/react-icons'; +import { + Avatar, + Button, + DropdownMenu, + Flex, + Heading, + Text, +} from '@radix-ui/themes'; +import Image from 'next/image'; +import Link from 'next/link'; + +import { AppContent } from './app-content'; + +type Props = + | { + user: { + avatarURL?: string; + credits: number; + username: string; + }; + variant: 'authenticated'; + } + | { + variant: 'guest'; + }; + +function AppHeaderActionsAuthenticated( + props: Pick, 'user'>, +) { + return ( + + + + {props.user.username} + + + + {props.user.credits === 0 ? 'No' : props.user.credits} credits + remaining + + + + + + } + radius="full" + src={props.user.avatarURL} + /> + + + + Buy credits + Settings + + Sign out + + + + ); +} + +function AppHeaderActionsGuest() { + return ( + + + + + + ); +} + +export function AppHeader(props: Props) { + return ( + + + + + + beecast.ai logo + + + Beecast + + + + + {props.variant === 'authenticated' ? ( + + ) : ( + + )} + + + + ); +} diff --git a/components/layout/app-layout.tsx b/components/layout/app-layout.tsx new file mode 100644 index 0000000..a66eaa4 --- /dev/null +++ b/components/layout/app-layout.tsx @@ -0,0 +1,32 @@ +import type { PropsWithChildren } from 'react'; + +import { Box, Flex } from '@radix-ui/themes'; + +import { AppContent } from './app-content'; +import { AppFooter } from './app-footer'; +import { AppHeader } from './app-header'; + +export function AppLayout({ children }: PropsWithChildren) { + return ( + +
+ +
+ +
+ + {children} + +
+ +
+ +
+
+ ); +} diff --git a/components/theme-provider/radix-themes.provider.tsx b/components/theme-provider/radix-themes.provider.tsx index b1072b6..e136bbe 100644 --- a/components/theme-provider/radix-themes.provider.tsx +++ b/components/theme-provider/radix-themes.provider.tsx @@ -1,12 +1,11 @@ import type { PropsWithChildren } from 'react'; -import { Theme, ThemePanel } from '@radix-ui/themes'; +import { Theme } from '@radix-ui/themes'; export function RadixThemesProvider({ children }: PropsWithChildren) { return ( - + {children} - ); } diff --git a/components/theme-provider/styles.css b/components/theme-provider/styles.css index e544efd..a3de752 100644 --- a/components/theme-provider/styles.css +++ b/components/theme-provider/styles.css @@ -1 +1,5 @@ @import '@radix-ui/themes/styles.css'; + +body { + margin: 0; +} diff --git a/package.json b/package.json index 0c21a88..e6eef42 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ }, "prettier": "@vercel/style-guide/prettier", "dependencies": { + "@radix-ui/react-icons": "^1.3.0", "@radix-ui/themes": "^2.0.2", "next": "14.0.4", "next-themes": "1.0.0-beta.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dc65a2d..caa3180 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,9 @@ settings: excludeLinksFromLockfile: false dependencies: + '@radix-ui/react-icons': + specifier: ^1.3.0 + version: 1.3.0(react@18.2.0) '@radix-ui/themes': specifier: ^2.0.2 version: 2.0.2(@types/react-dom@18.2.17)(@types/react@18.2.43)(react-dom@18.2.0)(react@18.2.0) @@ -2709,6 +2712,14 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@radix-ui/react-icons@1.3.0(react@18.2.0): + resolution: {integrity: sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==} + peerDependencies: + react: ^16.x || ^17.x || ^18.x + dependencies: + react: 18.2.0 + dev: false + /@radix-ui/react-id@1.0.1(@types/react@18.2.43)(react@18.2.0): resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==} peerDependencies: diff --git a/public/beecast.png b/public/beecast.png new file mode 100644 index 0000000..d15e237 Binary files /dev/null and b/public/beecast.png differ diff --git a/public/next.svg b/public/next.svg deleted file mode 100644 index 5174b28..0000000 --- a/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg deleted file mode 100644 index d2f8422..0000000 --- a/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file