diff --git a/README.md b/README.md index e93440f..5f64588 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,12 @@ - **Database**: postgres - **Storage Adapter**: localDisk -## external resources +# external resources +## APIs https://openlibrary.org/dev/docs/api -## maybe use -https://www.reddit.com/r/nextjs/comments/1ej1y32/share_cool_shadcnstyle_components_libraries_you/ -https://motion-primitives.com/docs +## UIs +https://ui.shadcn.com/ +https://magicui.design/ +https://motion-primitives.com/ diff --git a/assets/2025-04-25-20-29-26.png b/assets/2025-04-25-20-29-26.png new file mode 100644 index 0000000..a5afd26 Binary files /dev/null and b/assets/2025-04-25-20-29-26.png differ diff --git a/next.config.mjs b/next.config.mjs index 3e309f8..ed62259 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -3,7 +3,7 @@ import { withPayload } from '@payloadcms/next/withPayload' /** @type {import('next').NextConfig} */ const nextConfig = { images: { - domains: ['covers.openlibrary.org'], + domains: ['covers.openlibrary.org', 'cdn.beitzah.net'], }, } diff --git a/package-lock.json b/package-lock.json index 889bfe4..80006df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@payloadcms/next": "3.31.0", "@payloadcms/payload-cloud": "3.31.0", "@payloadcms/richtext-lexical": "3.31.0", + "@radix-ui/react-label": "^2.1.4", "@radix-ui/react-slot": "^1.2.0", "@radix-ui/react-tabs": "^1.1.4", "@tailwindcss/cli": "^4.1.4", @@ -4134,6 +4135,52 @@ } } }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.4.tgz", + "integrity": "sha512-wy3dqizZnZVV4ja0FNnUhIWNwWdoldXrneEyUcVtLYDAt8ovGS4ridtMAOGgXBBIfggL4BOveVWsjXDORdGEQg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.0.tgz", + "integrity": "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-presence": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.3.tgz", diff --git a/package.json b/package.json index 11ce51a..4149a94 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@payloadcms/next": "3.31.0", "@payloadcms/payload-cloud": "3.31.0", "@payloadcms/richtext-lexical": "3.31.0", + "@radix-ui/react-label": "^2.1.4", "@radix-ui/react-slot": "^1.2.0", "@radix-ui/react-tabs": "^1.1.4", "@tailwindcss/cli": "^4.1.4", diff --git a/src/app/(frontend)/books/[bookId]/page.client.tsx b/src/app/(frontend)/books/[bookId]/page.client.tsx index 8327ec0..45727d0 100644 --- a/src/app/(frontend)/books/[bookId]/page.client.tsx +++ b/src/app/(frontend)/books/[bookId]/page.client.tsx @@ -51,7 +51,7 @@ function RepoDropdown({ + +
+ Don't have an account?{' '} + + Request Access + +
+ + + + +
+ By clicking continue, you agree to our Terms of Service and{' '} + Privacy Policy. +
+ + ) +} diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx index 6b2e2ab..d05bbc6 100644 --- a/src/components/ui/card.tsx +++ b/src/components/ui/card.tsx @@ -1,78 +1,92 @@ -import * as React from 'react' +import * as React from "react" -import { cn } from '@/lib/utils' +import { cn } from "@/lib/utils" -function Card({ className, ...props }: React.ComponentProps<'div'>) { +function Card({ className, ...props }: React.ComponentProps<"div">) { return (
) } -function CardHeader({ className, ...props }: React.ComponentProps<'div'>) { +function CardHeader({ className, ...props }: React.ComponentProps<"div">) { return (
) } -function CardTitle({ className, ...props }: React.ComponentProps<'div'>) { +function CardTitle({ className, ...props }: React.ComponentProps<"div">) { return (
+ ) +} + +function CardDescription({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardAction({ className, ...props }: React.ComponentProps<"div">) { + return ( +
) } -function CardDescription({ className, ...props }: React.ComponentProps<'div'>) { +function CardContent({ className, ...props }: React.ComponentProps<"div">) { return (
) } -function CardAction({ className, ...props }: React.ComponentProps<'div'>) { - return ( -
- ) -} - -function CardContent({ className, ...props }: React.ComponentProps<'div'>) { - return
-} - -function CardFooter({ className, ...props }: React.ComponentProps<'div'>) { +function CardFooter({ className, ...props }: React.ComponentProps<"div">) { return (
) } -export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent } +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardAction, + CardDescription, + CardContent, +} diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx new file mode 100644 index 0000000..03295ca --- /dev/null +++ b/src/components/ui/input.tsx @@ -0,0 +1,21 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Input({ className, type, ...props }: React.ComponentProps<"input">) { + return ( + + ) +} + +export { Input } diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx new file mode 100644 index 0000000..fb5fbc3 --- /dev/null +++ b/src/components/ui/label.tsx @@ -0,0 +1,24 @@ +"use client" + +import * as React from "react" +import * as LabelPrimitive from "@radix-ui/react-label" + +import { cn } from "@/lib/utils" + +function Label({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Label } diff --git a/src/components/ui/ripple-button.tsx b/src/components/ui/ripple-button.tsx new file mode 100644 index 0000000..15e5d89 --- /dev/null +++ b/src/components/ui/ripple-button.tsx @@ -0,0 +1,80 @@ +'use client' + +import { cn } from '@/lib/utils' +import React, { MouseEvent, useEffect, useState } from 'react' + +interface RippleButtonProps extends React.ButtonHTMLAttributes { + rippleColor?: string + duration?: string +} + +export const RippleButton = React.forwardRef( + ( + { className, children, rippleColor = '#ffffff', duration = '600ms', onClick, ...props }, + ref, + ) => { + const [buttonRipples, setButtonRipples] = useState< + Array<{ x: number; y: number; size: number; key: number }> + >([]) + + const handleClick = (event: MouseEvent) => { + createRipple(event) + onClick?.(event) + } + + const createRipple = (event: MouseEvent) => { + const button = event.currentTarget + const rect = button.getBoundingClientRect() + const size = Math.max(rect.width, rect.height) + const x = event.clientX - rect.left - size / 2 + const y = event.clientY - rect.top - size / 2 + + const newRipple = { x, y, size, key: Date.now() } + setButtonRipples((prevRipples) => [...prevRipples, newRipple]) + } + + useEffect(() => { + if (buttonRipples.length > 0) { + const lastRipple = buttonRipples[buttonRipples.length - 1] + const timeout = setTimeout(() => { + setButtonRipples((prevRipples) => + prevRipples.filter((ripple) => ripple.key !== lastRipple.key), + ) + }, parseInt(duration)) + return () => clearTimeout(timeout) + } + }, [buttonRipples, duration]) + + return ( + + ) + }, +) + +RippleButton.displayName = 'RippleButton' diff --git a/src/lib/utils.ts b/src/lib/utils.ts index bd0c391..5e7aa98 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,6 +1,14 @@ import { clsx, type ClassValue } from "clsx" +import { PayloadRequest } from "payload" import { twMerge } from "tailwind-merge" export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } + +export const defaultAccess = { + admin: ({ req }: { req: PayloadRequest }) => { + if (req.user?.role === 'admin') return true + else return false + } +} diff --git a/src/payload-types.ts b/src/payload-types.ts index 126820b..90eaf46 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -155,9 +155,10 @@ export interface UserAuthOperations { */ export interface User { id: number; - role?: ('admin' | 'user') | null; + role?: ('admin' | 'user' | 'unclaimed') | null; firstName?: string | null; lastName?: string | null; + isOwnershipClaimed?: boolean | null; updatedAt: string; createdAt: string; email: string; @@ -462,6 +463,7 @@ export interface UsersSelect { role?: T; firstName?: T; lastName?: T; + isOwnershipClaimed?: T; updatedAt?: T; createdAt?: T; email?: T;