diff --git a/components.json b/components.json new file mode 100644 index 0000000..664d9b9 --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/app/globals.css", + "baseColor": "stone", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c2316d3..80d9efa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,15 +15,24 @@ "@payloadcms/next": "3.31.0", "@payloadcms/payload-cloud": "3.31.0", "@payloadcms/richtext-lexical": "3.31.0", + "@radix-ui/react-slot": "^1.2.0", + "@radix-ui/react-tabs": "^1.1.4", "@tailwindcss/cli": "^4.1.4", "@tailwindcss/postcss": "^4.1.4", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "cross-env": "^7.0.3", + "framer-motion": "^12.7.4", "graphql": "^16.8.1", + "lucide-react": "^0.488.0", "next": "15.2.3", + "next-themes": "^0.4.6", "payload": "3.31.0", "react": "19.0.0", "react-dom": "19.0.0", - "sharp": "0.32.6" + "sharp": "0.32.6", + "tailwind-merge": "^3.2.0", + "tw-animate-css": "^1.2.5" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", @@ -4029,6 +4038,275 @@ "react-dom": "^19.0.0 || ^19.0.0-rc-65a56d0e-20241020" } }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.3.tgz", + "integrity": "sha512-mM2pxoQw5HJ49rkzwOs7Y6J4oYH22wS8BfK2/bBxROlI4xuR0c4jEenQP63LlTlDkO6Buj2Vt+QYAYcOgqtrXA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.0.3", + "@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-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "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", + "integrity": "sha512-IrVLIhskYhH3nLvtcBLQFZr61tBG7wx7O3kEmdzcYwRGAEBmBicGGL7ATzNgruYJ3xBTbuzEEq9OXJM3PAX3tA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "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-primitive": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.3.tgz", + "integrity": "sha512-Pf/t/GkndH7CQ8wE2hbkXA+WyZ83fhQQn5DDmwDiDo6AwN/fhaH8oqZ0jRjMrO2iaMhDi6P1HRx6AZwyMinY1g==", + "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-roving-focus": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.3.tgz", + "integrity": "sha512-ufbpLUjZiOg4iYgb2hQrWXEPYX6jOLBbR27bDyAff5GYMRrCzcze8lukjuXVUQvJ6HZe8+oL+hhswDcjmcgVyg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-collection": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.0.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.1.1" + }, + "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-slot": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", + "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.4.tgz", + "integrity": "sha512-fuHMHWSf5SRhXke+DbHXj2wVMo+ghVH30vhX3XVacdXqDl+J4XWafMIGOOER861QpBx1jxgwKXL2dQnfrsd8MQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.3", + "@radix-ui/react-primitive": "2.0.3", + "@radix-ui/react-roving-focus": "1.1.3", + "@radix-ui/react-use-controllable-state": "1.1.1" + }, + "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-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.1.tgz", + "integrity": "sha512-YnEXIy8/ga01Y1PN0VfaNH//MhA91JlEGVBDxDzROqwrAtG5Yr2QGEPz8A/rJA3C7ZAHryOYGaUv8fLSW2H/mg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@react-aria/focus": { "version": "3.20.2", "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.20.2.tgz", @@ -6665,6 +6943,18 @@ "node": ">=8" } }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, "node_modules/classnames": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", @@ -8297,6 +8587,33 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/framer-motion": { + "version": "12.7.4", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.7.4.tgz", + "integrity": "sha512-jX0bPsTmU0oPZTYz/dVyD0dmOyEOEJvdn0TaZBE5I8g2GvVnnQnW9f65cJnoVfUkY3WZWNXGXnPbVA9YnaIfVA==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.7.4", + "motion-utils": "^12.7.2", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -9758,6 +10075,15 @@ "loose-envify": "cli.js" } }, + "node_modules/lucide-react": { + "version": "0.488.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.488.0.tgz", + "integrity": "sha512-ronlL0MyKut4CEzBY/ai2ZpKPxyWO4jUqdAkm2GNK5Zn3Rj+swDz+3lvyAUXN0PNqPKIX6XM9Xadwz/skLs/pQ==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -10461,6 +10787,21 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "license": "MIT" }, + "node_modules/motion-dom": { + "version": "12.7.4", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.7.4.tgz", + "integrity": "sha512-1ZUHAoSUMMxP6jPqyxlk9XUfb6NxMsnWPnH2YGhrOhTURLcXWbETi6eemoKb60Pe32NVJYduL4B62VQSO5Jq8Q==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.7.2" + } + }, + "node_modules/motion-utils": { + "version": "12.7.2", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.7.2.tgz", + "integrity": "sha512-XhZwqctxyJs89oX00zn3OGCuIIpVevbTa+u82usWBC6pSHUd2AoNWiYa7Du8tJxJy9TFbZ82pcn5t7NOm1PHAw==", + "license": "MIT" + }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", @@ -10561,6 +10902,16 @@ } } }, + "node_modules/next-themes": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", + "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -12705,6 +13056,16 @@ "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", "license": "MIT" }, + "node_modules/tailwind-merge": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.2.0.tgz", + "integrity": "sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.4.tgz", @@ -13343,6 +13704,15 @@ "node": "*" } }, + "node_modules/tw-animate-css": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.2.5.tgz", + "integrity": "sha512-ABzjfgVo+fDbhRREGL4KQZUqqdPgvc5zVrLyeW9/6mVqvaDepXc7EvedA+pYmMnIOsUAQMwcWzNvom26J2qYvQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Wombosvideo" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 7ef2c43..7f246fb 100644 --- a/package.json +++ b/package.json @@ -22,15 +22,24 @@ "@payloadcms/next": "3.31.0", "@payloadcms/payload-cloud": "3.31.0", "@payloadcms/richtext-lexical": "3.31.0", + "@radix-ui/react-slot": "^1.2.0", + "@radix-ui/react-tabs": "^1.1.4", "@tailwindcss/cli": "^4.1.4", "@tailwindcss/postcss": "^4.1.4", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "cross-env": "^7.0.3", + "framer-motion": "^12.7.4", "graphql": "^16.8.1", + "lucide-react": "^0.488.0", "next": "15.2.3", + "next-themes": "^0.4.6", "payload": "3.31.0", "react": "19.0.0", "react-dom": "19.0.0", - "sharp": "0.32.6" + "sharp": "0.32.6", + "tailwind-merge": "^3.2.0", + "tw-animate-css": "^1.2.5" }, "devDependencies": { "@eslint/eslintrc": "^3.2.0", diff --git a/providers/ThemeProvider.tsx b/providers/ThemeProvider.tsx new file mode 100644 index 0000000..5140481 --- /dev/null +++ b/providers/ThemeProvider.tsx @@ -0,0 +1,11 @@ +'use client' + +import * as React from 'react' +import { ThemeProvider as NextThemesProvider } from 'next-themes' + +export function ThemeProvider({ + children, + ...props +}: React.ComponentProps) { + return {children} +} diff --git a/src/app/(frontend)/layout.tsx b/src/app/(frontend)/layout.tsx index b99896f..0444697 100644 --- a/src/app/(frontend)/layout.tsx +++ b/src/app/(frontend)/layout.tsx @@ -1,18 +1,159 @@ +import { Avatar } from '@/components/avatar' +import { + Dropdown, + DropdownButton, + DropdownDivider, + DropdownItem, + DropdownLabel, + DropdownMenu, +} from '@/components/dropdown' +import { Navbar, NavbarDivider, NavbarItem, NavbarLabel, NavbarSection, NavbarSpacer } from '@/components/navbar' +import { Sidebar, SidebarBody, SidebarHeader, SidebarItem, SidebarLabel, SidebarSection } from '@/components/sidebar' +import { StackedLayout } from '@/components/stacked-layout' +import { + ArrowRightStartOnRectangleIcon, + ChevronDownIcon, + Cog8ToothIcon, + LightBulbIcon, + PlusIcon, + ShieldCheckIcon, + UserIcon, +} from '@heroicons/react/16/solid' +import { InboxIcon, MagnifyingGlassIcon } from '@heroicons/react/20/solid' +import { ThemeProvider } from 'next-themes' import React from 'react' export const metadata = { - description: 'A blank template using Payload in a Next.js app.', - title: 'Payload Blank Template', + description: "A Community's Collection", + title: 'Midrashim', } +const navItems = [ + { label: 'Home', url: '/' }, + { label: 'Events', url: '/events' }, + { label: 'Orders', url: '/orders' }, + { label: 'Broadcasts', url: '/broadcasts' }, + { label: 'Settings', url: '/settings' }, +] + +function TeamDropdownMenu() { + return ( + + + + Settings + + + + + Tailwind Labs + + + + Workcation + + + + + New team… + + + ) +} + + export default async function RootLayout(props: { children: React.ReactNode }) { const { children } = props return ( - - -
{children}
- - + + + + + + Tailwind Labs + + + + + + + {navItems.map(({ label, url }) => ( + + {label} + + ))} + + + + + + + + + + + + + + + + + My profile + + + + Settings + + + + + Privacy policy + + + + Share feedback + + + + + Sign out + + + + + + } + sidebar={ + + + + + + Tailwind Labs + + + + + + + + {navItems.map(({ label, url }) => ( + + {label} + + ))} + + + + } + > + {children} + + + + ) } diff --git a/src/app/(frontend)/page.tsx b/src/app/(frontend)/page.tsx index 46c9536..33eba98 100644 --- a/src/app/(frontend)/page.tsx +++ b/src/app/(frontend)/page.tsx @@ -1,12 +1,13 @@ import { headers as getHeaders } from 'next/headers.js' import Image from 'next/image' -import { getPayload } from 'payload' +import { getPayload, PaginatedDocs } from 'payload' import React from 'react' import { fileURLToPath } from 'url' import config from '@/payload.config' import BookList from '@/components/BookList' import { Book } from '@/payload-types' +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' export default async function HomePage() { const headers = await getHeaders() @@ -16,10 +17,10 @@ export default async function HomePage() { const fileURL = `vscode://file/${fileURLToPath(import.meta.url)}` - const books = await payload.find({ + const initBrowseBooks = (await payload.find({ collection: 'books', - depth: 2, - limit: 12, + depth: 10, + limit: 25, overrideAccess: false, select: { title: true, @@ -28,43 +29,50 @@ export default async function HomePage() { lcc: true, genre: true, isbn: true, + copies: true, }, - }) + })) as PaginatedDocs + + //let safariaQuote = '' + //const randomSefariaQuoteRequest = await fetch('https://www.sefaria.org/api/texts/random?categories=english') + //if (randomSefariaQuoteRequest.body) safariaQuote = await randomSefariaQuoteRequest.json() + //console.log(safariaQuote) return (
-
- - - Payload Logo - - {!user &&

Welcome to your new project.

} - {user &&

Welcome back, {user.email}

} -
- - Go to admin panel - - - Documentation - +
+
+
+

Get the help you need

+

+ Welcome {user && {`user.firstName`}} +

+

+ Anim aute id magna aliqua ad ad non deserunt sunt. Qui irure qui lorem cupidatat + commodo. Elit sunt amet fugiat veniam occaecat fugiat. +

+
- + + + + Your Feed + Browse + Search + + + + {initBrowseBooks && } + + + + {initBrowseBooks && } + + + Search + +

Update this page by editing

diff --git a/src/app/(payload)/custom.scss b/src/app/(payload)/custom.scss index e69de29..ddf46f8 100644 --- a/src/app/(payload)/custom.scss +++ b/src/app/(payload)/custom.scss @@ -0,0 +1,69 @@ +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.147 0.004 49.25); + --card: oklch(1 0 0); + --card-foreground: oklch(0.147 0.004 49.25); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.147 0.004 49.25); + --primary: oklch(0.216 0.006 56.043); + --primary-foreground: oklch(0.985 0.001 106.423); + --secondary: oklch(0.97 0.001 106.424); + --secondary-foreground: oklch(0.216 0.006 56.043); + --muted: oklch(0.97 0.001 106.424); + --muted-foreground: oklch(0.553 0.013 58.071); + --accent: oklch(0.97 0.001 106.424); + --accent-foreground: oklch(0.216 0.006 56.043); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.923 0.003 48.717); + --input: oklch(0.923 0.003 48.717); + --ring: oklch(0.709 0.01 56.259); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0.001 106.423); + --sidebar-foreground: oklch(0.147 0.004 49.25); + --sidebar-primary: oklch(0.216 0.006 56.043); + --sidebar-primary-foreground: oklch(0.985 0.001 106.423); + --sidebar-accent: oklch(0.97 0.001 106.424); + --sidebar-accent-foreground: oklch(0.216 0.006 56.043); + --sidebar-border: oklch(0.923 0.003 48.717); + --sidebar-ring: oklch(0.709 0.01 56.259); +} + +[data-theme='dark'] { + --background: oklch(0.147 0.004 49.25); + --foreground: oklch(0.985 0.001 106.423); + --card: oklch(0.216 0.006 56.043); + --card-foreground: oklch(0.985 0.001 106.423); + --popover: oklch(0.216 0.006 56.043); + --popover-foreground: oklch(0.985 0.001 106.423); + --primary: oklch(0.923 0.003 48.717); + --primary-foreground: oklch(0.216 0.006 56.043); + --secondary: oklch(0.268 0.007 34.298); + --secondary-foreground: oklch(0.985 0.001 106.423); + --muted: oklch(0.268 0.007 34.298); + --muted-foreground: oklch(0.709 0.01 56.259); + --accent: oklch(0.268 0.007 34.298); + --accent-foreground: oklch(0.985 0.001 106.423); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.553 0.013 58.071); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.216 0.006 56.043); + --sidebar-foreground: oklch(0.985 0.001 106.423); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0.001 106.423); + --sidebar-accent: oklch(0.268 0.007 34.298); + --sidebar-accent-foreground: oklch(0.985 0.001 106.423); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.553 0.013 58.071); +} + diff --git a/src/app/globals.css b/src/app/globals.css index 599ab3d..72d707a 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,5 +1,128 @@ @import "tailwindcss"; +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); -@tailwind base; -@tailwind components; @tailwind utilities; + +@theme inline { + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} + +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.147 0.004 49.25); + --card: oklch(1 0 0); + --card-foreground: oklch(0.147 0.004 49.25); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.147 0.004 49.25); + --primary: oklch(0.216 0.006 56.043); + --primary-foreground: oklch(0.985 0.001 106.423); + --secondary: oklch(0.97 0.001 106.424); + --secondary-foreground: oklch(0.216 0.006 56.043); + --muted: oklch(0.97 0.001 106.424); + --muted-foreground: oklch(0.553 0.013 58.071); + --accent: oklch(0.97 0.001 106.424); + --accent-foreground: oklch(0.216 0.006 56.043); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.923 0.003 48.717); + --input: oklch(0.923 0.003 48.717); + --ring: oklch(0.709 0.01 56.259); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0.001 106.423); + --sidebar-foreground: oklch(0.147 0.004 49.25); + --sidebar-primary: oklch(0.216 0.006 56.043); + --sidebar-primary-foreground: oklch(0.985 0.001 106.423); + --sidebar-accent: oklch(0.97 0.001 106.424); + --sidebar-accent-foreground: oklch(0.216 0.006 56.043); + --sidebar-border: oklch(0.923 0.003 48.717); + --sidebar-ring: oklch(0.709 0.01 56.259); +} + +.dark { + --background: oklch(0.147 0.004 49.25); + --foreground: oklch(0.985 0.001 106.423); + --card: oklch(0.216 0.006 56.043); + --card-foreground: oklch(0.985 0.001 106.423); + --popover: oklch(0.216 0.006 56.043); + --popover-foreground: oklch(0.985 0.001 106.423); + --primary: oklch(0.923 0.003 48.717); + --primary-foreground: oklch(0.216 0.006 56.043); + --secondary: oklch(0.268 0.007 34.298); + --secondary-foreground: oklch(0.985 0.001 106.423); + --muted: oklch(0.268 0.007 34.298); + --muted-foreground: oklch(0.709 0.01 56.259); + --accent: oklch(0.268 0.007 34.298); + --accent-foreground: oklch(0.985 0.001 106.423); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.553 0.013 58.071); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.216 0.006 56.043); + --sidebar-foreground: oklch(0.985 0.001 106.423); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0.001 106.423); + --sidebar-accent: oklch(0.268 0.007 34.298); + --sidebar-accent-foreground: oklch(0.985 0.001 106.423); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.553 0.013 58.071); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} + + +html[data-theme='dark'], +html[data-theme='light'] { + opacity: initial; +} diff --git a/src/collections/Books/Books.ts b/src/collections/Books/Books.ts index b578547..48cb529 100644 --- a/src/collections/Books/Books.ts +++ b/src/collections/Books/Books.ts @@ -18,7 +18,6 @@ export const Books: CollectionConfig = { unique: true, admin: { placeholder: "This is only for books that have been imported.", - //readOnly: true, } }, { @@ -76,5 +75,11 @@ export const Books: CollectionConfig = { name: 'description', type: 'richText' }, + { + name: 'copies', + type: 'join', + collection: 'copies', + on: 'book', + } ], } diff --git a/src/collections/Copies/Copies.ts b/src/collections/Copies/Copies.ts index 197b969..8ad3e86 100644 --- a/src/collections/Copies/Copies.ts +++ b/src/collections/Copies/Copies.ts @@ -1,6 +1,8 @@ -import { CollectionAfterReadHook, CollectionConfig } from "payload"; +import { Copy } from "@/payload-types"; +import { CollectionBeforeValidateHook, CollectionConfig } from "payload"; -const afterReadHook: CollectionAfterReadHook = async ({ doc, req }) => { +const beforeValidate: CollectionBeforeValidateHook = async ({ data, req }) => { + const doc = data as Copy if (doc.label) return doc; let bookName; @@ -8,7 +10,7 @@ const afterReadHook: CollectionAfterReadHook = async ({ doc, req }) => { const relatedBook = await req.payload.findByID({ req, collection: 'books', - id: doc.book, + id: doc.book as number, depth: 2, }) bookName = relatedBook.title @@ -19,7 +21,7 @@ const afterReadHook: CollectionAfterReadHook = async ({ doc, req }) => { const relatedRepo = await req.payload.findByID({ req, collection: 'repositories', - id: doc.repository, + id: doc.repository as number, depth: 2, }) repositoryName = relatedRepo.abbreviation || relatedRepo.name @@ -40,7 +42,7 @@ export const Copies: CollectionConfig = { admin: { useAsTitle: 'label', pagination: { - limits: [10, 20, 50, 100, 250], + limits: [10, 20], }, }, fields: [ @@ -74,6 +76,6 @@ export const Copies: CollectionConfig = { } ], hooks: { - afterRead: [afterReadHook] + beforeValidate: [beforeValidate] }, } diff --git a/src/components/BookList/index.tsx b/src/components/BookList/index.tsx index 786a716..1d8bb95 100644 --- a/src/components/BookList/index.tsx +++ b/src/components/BookList/index.tsx @@ -1,9 +1,20 @@ -import { Badge } from '@/components/badge' +import { Badge } from '@/components/ui/badge' +import { + Pagination, + PaginationContent, + PaginationEllipsis, + PaginationItem, + PaginationLink, + PaginationNext, + PaginationPrevious, +} from '@/components/ui/pagination' import { Author, Book, Genre } from '@/payload-types' import { Avatar } from '../avatar' +import { PaginatedDocs } from 'payload' +import Link from 'next/link' type Props = { - books: Book[] + books: PaginatedDocs } const makeAuthorsLabel = (book: Book) => { @@ -33,52 +44,110 @@ const makeGenreBadges = (book: Book) => { } export default function BookList(props: Props) { - const { books } = props + const { docs, hasNextPage, hasPrevPage, limit, totalPages, page, prevPage, nextPage, totalDocs } = + props.books + + console.log(props.books) + + const currentPage = page || 0 + const books = docs return (
    - {books.map((b) => ( -
  • -
    - -
    -

    - {b.title} - {b.lcc} -

    -

    {makeGenreBadges(b)}

    -
    -
    -
    -

    {makeAuthorsLabel(b)}

    - { - /*b.lastBorrowed*/ false ? ( -

    - Last Borrowed{' '} - + {books?.map((b) => ( +

  • + +
    + +
    +

    + {b.title} + {b.lcc}

    +

    + {makeGenreBadges(b)} +

    +
    +
    +
    +

    {makeAuthorsLabel(b)}

    + {!b.copies?.docs?.length ? ( +

    No copies found

    ) : (
    -

    Available

    +

    + {b.copies?.docs?.length} {b.copies?.docs?.length > 1 ? 'copy' : 'copies'} +

    - ) - } -
    + )} +
    +
  • ))}
+ + + + + + + + {hasPrevPage && currentPage > totalPages / 2 && ( + + + + )} + + {prevPage && ( + + {prevPage} + + )} + + + + {currentPage} + + + + {nextPage && ( + + {nextPage} + + )} + + {hasNextPage && currentPage < totalPages / 2 && ( + + + + )} + + + + + + + +

+ viewing + + {currentPage * limit}-{currentPage * limit + (limit - 1)} + + of + {totalDocs} +

) } diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx new file mode 100644 index 0000000..0205413 --- /dev/null +++ b/src/components/ui/badge.tsx @@ -0,0 +1,46 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + secondary: + "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + destructive: + "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Badge({ + className, + variant, + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : "span" + + return ( + + ) +} + +export { Badge, badgeVariants } diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx new file mode 100644 index 0000000..a2df8dc --- /dev/null +++ b/src/components/ui/button.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", + destructive: + "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", + secondary: + "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", + ghost: + "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2 has-[>svg]:px-3", + sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", + lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + icon: "size-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Button({ + className, + variant, + size, + asChild = false, + ...props +}: React.ComponentProps<"button"> & + VariantProps & { + asChild?: boolean + }) { + const Comp = asChild ? Slot : "button" + + return ( + + ) +} + +export { Button, buttonVariants } diff --git a/src/components/ui/pagination.tsx b/src/components/ui/pagination.tsx new file mode 100644 index 0000000..0d18541 --- /dev/null +++ b/src/components/ui/pagination.tsx @@ -0,0 +1,127 @@ +import * as React from "react" +import { + ChevronLeftIcon, + ChevronRightIcon, + MoreHorizontalIcon, +} from "lucide-react" + +import { cn } from "@/lib/utils" +import { Button, buttonVariants } from "@/components/ui/button" + +function Pagination({ className, ...props }: React.ComponentProps<"nav">) { + return ( +