chore: restructures developer-portfolio files between app & payload
@@ -4,6 +4,4 @@ PAYLOAD_PUBLIC_SERVER_URL=http://localhost:3000
|
||||
PAYLOAD_PUBLIC_DRAFT_SECRET=EXAMPLE_DRAFT_SECRET
|
||||
COOKIE_DOMAIN=localhost
|
||||
REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY
|
||||
PAYLOAD_SEED=false
|
||||
PAYLOAD_DROP_DATABASE=false
|
||||
ENABLE_PAYLOAD_CLOUD=false
|
||||
ENABLE_PAYLOAD_CLOUD=false
|
||||
|
||||
@@ -3,7 +3,4 @@ module.exports = {
|
||||
extends: ['plugin:@next/next/recommended', '@payloadcms'],
|
||||
ignorePatterns: ['**/payload-types.ts'],
|
||||
plugins: ['prettier'],
|
||||
rules: {
|
||||
'prettier/prettier': 'error',
|
||||
},
|
||||
}
|
||||
|
||||
4
templates/developer-portfolio/.gitignore
vendored
@@ -5,5 +5,5 @@ package-lock.json
|
||||
.env
|
||||
.next
|
||||
.vercel
|
||||
src/media
|
||||
.DS_Store
|
||||
/media
|
||||
.DS_Store
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
# Payload Developer Portfolio Example
|
||||
|
||||
This example demonstrates a complete professional portfolio application using [Payload](https://github.com/payloadcms/payload) and NextJS in a single Express server.
|
||||
This example demonstrates a complete professional portfolio application using [Payload](https://github.com/payloadcms/payload) and NextJS in a single Express server.
|
||||
|
||||
## Highlights
|
||||
|
||||
- 💪 **Batteries-Included**
|
||||
- 💪 **Batteries-Included**
|
||||
- Design beautiful pages, describe portfolio projects, and build forms dynamically without writing code
|
||||
- 🔎 **SEO-Friendly**
|
||||
- 🔎 **SEO-Friendly**
|
||||
- Includes [SEO plugin](https://github.com/payloadcms/plugin-seo) integration to author and preview page metadata
|
||||
- 🪭 **Customization-Friendly**
|
||||
- 🪭 **Customization-Friendly**
|
||||
- Light/dark mode, [@shadcn/ui](https://ui.shadcn.com/) integration, prebuilt animations, and modular CMS "blocks" encourage extension and re-use
|
||||
- 🏎️ **Performance-Focused**
|
||||
- 🏎️ **Performance-Focused**
|
||||
- Uses React Server Components, App Router, and `next/image` to optimize Web Vitals metrics
|
||||
- 🦯 **Accessibility-Minded**
|
||||
- 🦯 **Accessibility-Minded**
|
||||
- Navigation, contrast, dialogs, and forms built with [WCAG 2](https://www.w3.org/WAI/standards-guidelines/wcag/) in mind
|
||||
|
||||
## Quick Start
|
||||
@@ -22,21 +22,24 @@ This example demonstrates a complete professional portfolio application using [P
|
||||
- [Node](https://nodejs.org/en) 18.x or newer
|
||||
- [MongoDB](https://www.mongodb.com/try/download/community)
|
||||
|
||||
### Setup
|
||||
To spin up this example locally, follow these steps:
|
||||
|
||||
1. First, clone the repo
|
||||
1. Then `cd YOUR_PROJECT_REPO && cp .env.example .env`
|
||||
1. Next `yarn && yarn seed` to start the app and seed it with example data
|
||||
1. Now `open http://localhost:3000` to view the site
|
||||
### Clone
|
||||
|
||||
That's it! Changes made in `./src` will be reflected in your app. See the [Development](#development) section for more details.
|
||||
If you have not done so already, you need to have standalone copy of this repo on your machine. If you've already cloned this repo, skip to [Development](#development).
|
||||
|
||||
### Editing Content
|
||||
#### Method 1 (recommended)
|
||||
|
||||
To access the Admin interface, where you can edit content:
|
||||
Go to Payload Cloud and [clone this template](https://payloadcms.com/new/clone/developer-portfolio). This will create a new repository on your GitHub account with this template's code which you can then clone to your own machine.
|
||||
|
||||
1. Go to `http://localhost:3000/admin`
|
||||
1. Login with `dev@payloadcms.com` / `test`
|
||||
### Development
|
||||
|
||||
1. First [clone the repo](#clone) if you have not done so already
|
||||
1. `cd my-project && cp .env.example .env` to copy the example environment variables
|
||||
1. `yarn && yarn dev` to install dependencies and start the dev server
|
||||
1. `open http://localhost:3000` to open the app in your browser
|
||||
|
||||
That's it! Changes made in `./src` will be reflected in your app. Follow the on-screen instructions to login and create your first admin user. Then check out [Production](#production) once you're ready to build and serve your app, and [Deployment](#deployment) when you're ready to go live.
|
||||
|
||||
## How it works
|
||||
|
||||
@@ -44,15 +47,118 @@ When you use Payload, you plug it into _**your**_ Express server. That's a funda
|
||||
|
||||
One of the strengths of this pattern is that it lets you do powerful things like integrate your Payload instance directly with your front-end. This will allow you to host Payload alongside a fully dynamic, CMS-integrated website or app on a single, combined server—while still getting all of the benefits of a headless CMS.
|
||||
|
||||
## Development
|
||||
### Collections
|
||||
|
||||
To spin up this example locally, follow the [Quick Start](#quick-start).
|
||||
See the [Collections](https://payloadcms.com/docs/configuration/collections) docs for details on how to extend this functionality.
|
||||
|
||||
- #### Users (Authentication)
|
||||
|
||||
Users in the platform can authenticate to interact with features such as adding projects comments and updating pages. Everyone has the same level of access on the platform. See [Access Control](#access-control) for more details.
|
||||
|
||||
For additional help, see the official [Auth Example](https://github.com/payloadcms/payload/tree/master/examples/auth) or the [Authentication](https://payloadcms.com/docs/authentication/overview#authentication-overview) docs.
|
||||
|
||||
- #### Projects
|
||||
|
||||
Projects are used to showcase your work. All projects are layout builder enabled so you can generate unique layouts for each project using layout-building blocks, see [Layout Builder](#layout-builder) for more details. Projects are also draft-enabled so you can preview them before publishing them to your website, see [Draft Preview](#draft-preview) for more details.
|
||||
|
||||
- #### Pages
|
||||
|
||||
All pages are layout builder enabled so you can generate unique layouts for each page using layout-building blocks, see [Layout Builder](#layout-builder) for more details. Pages are also draft-enabled so you can preview them before publishing them to your website, see [Draft Preview](#draft-preview) for more details.
|
||||
|
||||
- #### Media
|
||||
|
||||
This is the uploads enabled collection used by pages, posts, and projects to contain media like images, videos, downloads, and other assets.
|
||||
|
||||
- #### Technologies
|
||||
|
||||
A taxonomy used on projects to display what technologies were utilized on each project.
|
||||
|
||||
### Globals
|
||||
|
||||
See the [Globals](https://payloadcms.com/docs/configuration/globals) docs for details on how to extend this functionality.
|
||||
|
||||
- `Header`
|
||||
|
||||
The data required by the header on your front-end like nav links.
|
||||
|
||||
- `Profile`
|
||||
|
||||
The data required for the Developer in question of the portfolio.
|
||||
|
||||
## Layout Builder
|
||||
|
||||
Create unique page and project layouts for any type of content using a powerful layout builder. This template comes pre-configured with the following layout building blocks:
|
||||
|
||||
- ### Pages
|
||||
|
||||
- Content
|
||||
- Form Block
|
||||
- Media Block
|
||||
- Media Content
|
||||
- Profile Call to Action
|
||||
- Project Grid
|
||||
|
||||
- ### Projects
|
||||
|
||||
- Content
|
||||
- Form Block
|
||||
- Media Block
|
||||
- Media Content
|
||||
|
||||
Each block is fully designed and built into the front-end website that comes with this template. See [Developer Portfolio](#developer-portfolio) for more details.
|
||||
|
||||
## Draft Preview
|
||||
|
||||
All pages and projects are draft-enabled so you can preview them before publishing them to your website. To do this, these collections use [Versions](https://payloadcms.com/docs/configuration/collections#versions) with `drafts` set to `true`. This means that when you create a new page or project, it will be saved as a draft and will not be visible on your portfolio until you publish it. This also means that you can preview your draft before publishing it to your portfolio. To do this, we automatically format a custom URL which redirects to your front-end to securely fetch the draft version of your content.
|
||||
|
||||
Since the front-end of this template is statically generated, this also means that pages and projects will need to be regenerated as changes are made to published documents. To do this, we use an `afterChange` hook to regenerate the front-end when a document has changed and its `_status` is `published`.
|
||||
|
||||
For more details on how to extend this functionality, see the official [Draft Preview Example](https://github.com/payloadcms/payload/tree/master/examples/draft-preview).
|
||||
|
||||
## SEO
|
||||
|
||||
This template comes pre-configured with the official [Payload SEO Plugin](https://github.com/payloadcms/plugin-seo) for complete SEO control from the admin panel. All SEO data is fully integrated into the front-end website that comes with this template. See [Developer Portfolio](#developer-potfolio) for more details.
|
||||
|
||||
## Developer Portfolio
|
||||
|
||||
This template includes a beautifully designed, production-ready front-end built with the [Next.js App Router](https://nextjs.org), served right alongside your Payload app in a single Express server. This makes is so that you can deploy both apps simultaneously and host them together. If you prefer a different front-end framework, this pattern works for any framework that supports a custom server. If you prefer to host your website separately from Payload, you can easily [Eject](#eject) the front-end out from this template to swap in your own, or to use it as a standalone CMS. For more details, see the official [Custom Server Example](https://github.com/payloadcms/payload/tree/master/examples/custom-server).
|
||||
|
||||
Core features:
|
||||
|
||||
- [Next.js App Router](https://nextjs.org)
|
||||
- [TypeScript](https://www.typescriptlang.org)
|
||||
- [React Hook Form](https://react-hook-form.com)
|
||||
- Authentication
|
||||
- Fully featured projects
|
||||
- Publication workflow
|
||||
- User accounts
|
||||
- Dark mode
|
||||
- Pre-made layout building blocks
|
||||
- SEO
|
||||
|
||||
### Cache
|
||||
|
||||
Although Next.js includes a robust set of caching strategies out of the box, Payload Cloud proxies and caches all files through Cloudflare using the [Official Cloud Plugin](https://github.com/payloadcms/plugin-cloud). This means that Next.js caching is not needed and is disabled by default. For more details, see the official [Next.js Caching Docs](https://nextjs.org/docs/app/building-your-application/caching).
|
||||
|
||||
### Eject
|
||||
|
||||
If you prefer another front-end framework or would like to use Payload as a standalone CMS, you can easily eject the front-end from this template. To eject, simply run `yarn eject`. This will uninstall all Next.js related dependencies and delete all files and folders related to the Next.js front-end. It also removes all custom routing from your `server.ts` file and updates your `eslintrc.js`.
|
||||
|
||||
> Note: Your eject script may not work as expected if you've made significant modifications to your project. If you run into any issues, compare your project's dependencies and file structure with this template. See [./src/eject](./src/eject) for full details.
|
||||
|
||||
For more details on how setup a custom server, see the official [Custom Server Example](https://github.com/payloadcms/payload/tree/master/examples/custom-server).
|
||||
|
||||
## Development
|
||||
|
||||
To spin up this example locally, follow the [Quick Start](#quick-start). Then [Seed](#seed) the database with a few pages, posts, and projects.
|
||||
|
||||
### Seed
|
||||
|
||||
On boot, a seed script is included to scaffold a basic database for you to use as an example. This is done by setting the `PAYLOAD_DROP_DATABASE` and `PAYLOAD_SEED` environment variables which are included in the `.env.example` by default. You can remove these from your `.env` to prevent this behavior. You can also freshly seed your project at any time by running `yarn seed`. This seed creates:
|
||||
To seed the database, you can run `yarn seed`. This template also comes with a `GET /api/seed` endpoint you can use to seed the database from the admin panel.
|
||||
|
||||
- An admin user with email `dev@payloadcms.com`, password `test`,
|
||||
This seed creates:
|
||||
|
||||
- An admin user with email `dev@payloadcms.com`, password `test`,
|
||||
- A `home` page with Profile CTA, project grid, and contact form
|
||||
- Example header and profile data
|
||||
- Example media assets
|
||||
|
||||
@@ -11,6 +11,6 @@
|
||||
},
|
||||
"aliases": {
|
||||
"components": "src/app/_components",
|
||||
"utils": "src/utilities"
|
||||
"utils": "src/payload/utilities"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
{
|
||||
"watch": ["server.ts"],
|
||||
"exec": "ts-node --project tsconfig.server.json src/server.ts",
|
||||
"ext": "js ts"
|
||||
}
|
||||
"ext": "ts",
|
||||
"exec": "ts-node src/server.ts",
|
||||
"ignore": [
|
||||
"src/app"
|
||||
]
|
||||
}
|
||||
@@ -1,26 +1,27 @@
|
||||
{
|
||||
"name": "payload-developer-portfolio-template",
|
||||
"description": "Payload developer portfolio template.",
|
||||
"description": "Developer portfolio template for Payload",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/server.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "cross-env PAYLOAD_SEED=false PAYLOAD_DROP_DATABASE=false PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon -V",
|
||||
"seed": "rm -rf src/media .next && cross-env PAYLOAD_SEED=true PAYLOAD_DROP_DATABASE=true PAYLOAD_CONFIG_PATH=src/payload.config.ts ts-node src/server.ts",
|
||||
"build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload build",
|
||||
"dev": "cross-env PAYLOAD_CONFIG_PATH=src/payload/payload.config.ts nodemon",
|
||||
"seed": "rm -rf src/media .next && cross-env PAYLOAD_SEED=true PAYLOAD_DROP_DATABASE=true PAYLOAD_CONFIG_PATH=src/payload/payload.config.ts ts-node src/server.ts",
|
||||
"build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload/payload.config.ts payload build",
|
||||
"build:server": "tsc --project tsconfig.server.json",
|
||||
"build:next": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js NEXT_BUILD=true node dist/server.js",
|
||||
"build:next": "cross-env PAYLOAD_CONFIG_PATH=dist/payload/payload.config.js NEXT_BUILD=true node dist/server.js",
|
||||
"build": "cross-env NODE_ENV=production yarn build:payload && yarn build:server && yarn copyfiles && yarn build:next",
|
||||
"serve": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js NODE_ENV=production node dist/server.js",
|
||||
"serve": "cross-env PAYLOAD_CONFIG_PATH=dist/payload/payload.config.js NODE_ENV=production node dist/server.js",
|
||||
"eject": "yarn remove next react react-dom @next/eslint-plugin-next && ts-node eject.ts",
|
||||
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png}\" dist/",
|
||||
"generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types",
|
||||
"generate:graphQLSchema": "PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:graphQLSchema",
|
||||
"generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload/payload.config.ts payload generate:types",
|
||||
"generate:graphQLSchema": "PAYLOAD_CONFIG_PATH=src/payload/payload.config.ts payload generate:graphQLSchema",
|
||||
"lint": "eslint src",
|
||||
"lint:fix": "eslint --fix --ext .ts,.tsx src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^3.2.0",
|
||||
"@payloadcms/plugin-cloud": "^2.0.0",
|
||||
"@payloadcms/plugin-form-builder": "^1.0.15",
|
||||
"@payloadcms/plugin-seo": "^1.0.14-canary.0",
|
||||
"@radix-ui/react-dialog": "^1.0.4",
|
||||
@@ -34,7 +35,7 @@
|
||||
"escape-html": "^1.0.3",
|
||||
"express": "^4.17.1",
|
||||
"lucide-react": "^0.263.1",
|
||||
"next": "^13.4.19",
|
||||
"next": "13.4.19",
|
||||
"next-themes": "^0.2.1",
|
||||
"nodemailer": "^6.9.4",
|
||||
"payload": "1.15.2",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Metadata, ResolvingMetadata } from 'next'
|
||||
import { notFound, redirect } from 'next/navigation'
|
||||
import { notFound } from 'next/navigation'
|
||||
|
||||
import { Media } from '../../payload-types'
|
||||
import { Media } from '../../payload/payload-types'
|
||||
import { ContentLayout } from '../_components/content/contentLayout'
|
||||
import { fetchPage } from '../_utils/api'
|
||||
import { parsePreviewOptions } from '../_utils/preview'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { cn } from '../../utilities'
|
||||
import { cn } from '../utilities'
|
||||
|
||||
export const PayloadLogo = ({ className = '' }: { className?: string }) => (
|
||||
<svg
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { FC } from 'react'
|
||||
|
||||
import { Form, Page, Profile, Project } from '../../../payload-types'
|
||||
import { cn } from '../../../utilities'
|
||||
import { Form, Page, Profile, Project } from '../../../payload/payload-types'
|
||||
import { cn } from '../../utilities'
|
||||
import { ContentBlock } from './contentBlock'
|
||||
import { FormBlock } from './formBlock'
|
||||
import { MediaBlock } from './mediaBlock'
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { FC, Fragment, useState } from 'react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { Data } from 'payload/dist/admin/components/forms/Form/types'
|
||||
|
||||
import { Form as FormTypes } from '../../../payload-types'
|
||||
import { Form as FormTypes } from '../../../payload/payload-types'
|
||||
import { serverUrl } from '../../_utils/api'
|
||||
import { Block } from '../ui/block'
|
||||
import { Button } from '../ui/button'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AnchorHTMLAttributes, ReactNode } from 'react'
|
||||
import Link, { LinkProps } from 'next/link'
|
||||
|
||||
import { Header } from '../../../payload-types'
|
||||
import { Header } from '../../../payload/payload-types'
|
||||
|
||||
export type PayloadLinkType = Header['navItems'][0]['link']
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ import { FC, Fragment } from 'react'
|
||||
import { cva } from 'class-variance-authority'
|
||||
import Image from 'next/image'
|
||||
|
||||
import { Media } from '../../../payload-types'
|
||||
import { cn } from '../../../utilities'
|
||||
import { Media } from '../../../payload/payload-types'
|
||||
import { cn } from '../../utilities'
|
||||
import { Block, BlockProps } from '../ui/block'
|
||||
import { MediaDialog } from './mediaDialog'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { FC, Fragment } from 'react'
|
||||
|
||||
import { Media } from '../../../payload-types'
|
||||
import { Media } from '../../../payload/payload-types'
|
||||
import { ContentBlock } from './contentBlock'
|
||||
import { PayloadLink, PayloadLinkType } from './link'
|
||||
import { MediaBlock } from './mediaBlock'
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { FC, useState } from 'react'
|
||||
import Image from 'next/image'
|
||||
|
||||
import { cn } from '../../../utilities'
|
||||
import { cn } from '../../utilities'
|
||||
import { Dialog, DialogContent, DialogTrigger } from '../ui/dialog'
|
||||
|
||||
interface MediaDialogProps {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { cva } from 'class-variance-authority'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
|
||||
import { Media, Profile } from '../../../payload-types'
|
||||
import { Media, Profile } from '../../../payload/payload-types'
|
||||
import { Block } from '../ui/block'
|
||||
import { RichText } from './richText'
|
||||
import { SocialIcons } from './socialIcons'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { FC } from 'react'
|
||||
|
||||
import { Profile, Project } from '../../../../payload-types'
|
||||
import { Profile, Project } from '../../../../payload/payload-types'
|
||||
import { FadeInContent } from '../../ui/fadeInContent'
|
||||
import { ContentLayout } from '../contentLayout'
|
||||
import { ProfileCTABlock } from '../profileCTABlock'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
import { FC } from 'react'
|
||||
|
||||
import { Project } from '../../../../payload-types'
|
||||
import { Project } from '../../../../payload/payload-types'
|
||||
import { formatMonth } from '../../../_utils/format'
|
||||
import { ProjectRoles } from './projectRole'
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
import { FC } from 'react'
|
||||
|
||||
import { Project } from '../../../../payload-types'
|
||||
import { Project } from '../../../../payload/payload-types'
|
||||
import { MediaBlock } from '../../../_components/content/mediaBlock'
|
||||
import { TechnologiesUsed } from './technologiesUsed'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { FC } from 'react'
|
||||
|
||||
import { Project } from '../../../../payload-types'
|
||||
import { Project } from '../../../../payload/payload-types'
|
||||
|
||||
interface ProjectRolesProps {
|
||||
roles: Project['role']
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { FC } from 'react'
|
||||
|
||||
import { Project, Technology } from '../../../../payload-types'
|
||||
import { Project, Technology } from '../../../../payload/payload-types'
|
||||
|
||||
export interface TechnologiesUsedProps {
|
||||
technologies: Project['technologiesUsed']
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { FC } from 'react'
|
||||
import Link from 'next/link'
|
||||
|
||||
import { Media, Project } from '../../../payload-types'
|
||||
import { Media, Project } from '../../../payload/payload-types'
|
||||
import { formatYear } from '../../_utils/format'
|
||||
import { Block } from '../ui/block'
|
||||
import { MediaBlock } from './mediaBlock'
|
||||
|
||||
@@ -2,9 +2,9 @@ import { FC, Suspense } from 'react'
|
||||
import { EnvelopeOpenIcon } from '@radix-ui/react-icons'
|
||||
import { GithubIcon, LinkedinIcon, TwitterIcon } from 'lucide-react'
|
||||
|
||||
import { Profile } from '../../../payload-types'
|
||||
import { cn } from '../../../utilities'
|
||||
import { Profile } from '../../../payload/payload-types'
|
||||
import { fetchProfile } from '../../_utils/api'
|
||||
import { cn } from '../../utilities'
|
||||
import { SocialLink } from '../ui/socialLink'
|
||||
|
||||
interface SocialIconsContentProps {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { FC } from 'react'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
|
||||
import { Profile } from '../../../payload-types'
|
||||
import { Profile } from '../../../payload/payload-types'
|
||||
import { PayloadLogo } from '../../_assets/payloadLogo'
|
||||
import { SocialIcons } from '../content/socialIcons'
|
||||
import { ThemeToggle } from './themeToggle'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
|
||||
import { Header, Media, Profile } from '../../../payload-types'
|
||||
import { Header, Media, Profile } from '../../../payload/payload-types'
|
||||
import { PayloadLink } from '../content/link'
|
||||
import { SkipToMainContentLink } from './skipToMainContent'
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as React from 'react'
|
||||
import { Moon, Sun } from 'lucide-react'
|
||||
import { useTheme } from 'next-themes'
|
||||
|
||||
import { cn } from '../../../utilities'
|
||||
import { cn } from '../../utilities'
|
||||
import { Button } from '../ui/button'
|
||||
import {
|
||||
DropdownMenu,
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as React from 'react'
|
||||
import { Slot } from '@radix-ui/react-slot'
|
||||
import { cva, VariantProps } from 'class-variance-authority'
|
||||
|
||||
import { cn } from '../../../utilities'
|
||||
import { cn } from '../../utilities'
|
||||
import { FadeInContent } from './fadeInContent'
|
||||
|
||||
const blockVariants = cva('flex col-span-6 justify-center lg:justify-start', {
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as React from 'react'
|
||||
import { Slot } from '@radix-ui/react-slot'
|
||||
import { cva, type VariantProps } from 'class-variance-authority'
|
||||
|
||||
import { cn } from '../../../utilities'
|
||||
import { cn } from '../../utilities'
|
||||
|
||||
const buttonVariants = cva(
|
||||
'inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as React from 'react'
|
||||
import * as DialogPrimitive from '@radix-ui/react-dialog'
|
||||
import { cva } from 'class-variance-authority'
|
||||
|
||||
import { cn } from '../../../utilities'
|
||||
import { cn } from '../../utilities'
|
||||
|
||||
const dialogContentVariants = cva(
|
||||
'p-4 lg:p-12 fixed left-[50%] top-[50%] z-50 translate-x-[-50%] translate-y-[-50%] duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg flex flex-col justify-center items-center',
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as React from 'react'
|
||||
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
|
||||
import { Check, ChevronRight, Circle } from 'lucide-react'
|
||||
|
||||
import { cn } from '../../../utilities'
|
||||
import { cn } from '../../utilities'
|
||||
|
||||
const DropdownMenu = DropdownMenuPrimitive.Root
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
import * as LabelPrimitive from '@radix-ui/react-label'
|
||||
import { Slot } from '@radix-ui/react-slot'
|
||||
|
||||
import { cn } from '../../../utilities'
|
||||
import { cn } from '../../utilities'
|
||||
import { Label } from './label'
|
||||
|
||||
const Form = FormProvider
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from 'react'
|
||||
|
||||
import { cn } from '../../../utilities'
|
||||
import { cn } from '../../utilities'
|
||||
|
||||
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as React from 'react'
|
||||
import * as LabelPrimitive from '@radix-ui/react-label'
|
||||
import { cva, type VariantProps } from 'class-variance-authority'
|
||||
|
||||
import { cn } from '../../../utilities'
|
||||
import { cn } from '../../utilities'
|
||||
|
||||
const labelVariants = cva(
|
||||
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from 'react'
|
||||
|
||||
import { cn } from '../../../utilities'
|
||||
import { cn } from '../../utilities'
|
||||
|
||||
export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Header, Page, Profile, Project } from '../../payload-types'
|
||||
import type { Header, Page, Profile, Project } from '../../payload/payload-types'
|
||||
import type { DraftOptions } from './preview'
|
||||
|
||||
export const serverUrl = process.env.PAYLOAD_PUBLIC_SERVER_URL ?? 'http://localhost:3000'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
import { Metadata, ResolvingMetadata } from 'next'
|
||||
|
||||
import { Media } from '../payload-types'
|
||||
import { Media } from '../payload/payload-types'
|
||||
import { ContentLayout } from './_components/content/contentLayout'
|
||||
import { fetchPage, fetchProfile } from './_utils/api'
|
||||
import { parsePreviewOptions } from './_utils/preview'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Metadata, ResolvingMetadata } from 'next'
|
||||
import { notFound, redirect } from 'next/navigation'
|
||||
import { notFound } from 'next/navigation'
|
||||
|
||||
import { Media } from '../../../payload-types'
|
||||
import { Media } from '../../../payload/payload-types'
|
||||
import { ProjectDetails } from '../../_components/content/projectDetails/projectDetails'
|
||||
import { fetchProfile, fetchProject } from '../../_utils/api'
|
||||
import { parsePreviewOptions } from '../../_utils/preview'
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import { loggedIn } from '../../access/loggedIn'
|
||||
import { publishedOrLoggedIn } from '../../access/publishedOrLoggedIn'
|
||||
import { serverUrl } from '../../app/_utils/api'
|
||||
import { Content } from '../../blocks/Content'
|
||||
import { Form } from '../../blocks/Form'
|
||||
import { MediaBlock } from '../../blocks/Media'
|
||||
@@ -14,7 +13,7 @@ import { tagRevalidator } from '../../utilities/tagRevalidator'
|
||||
|
||||
const formatAppURL = ({ doc }): string => {
|
||||
const pathToUse = doc.slug === 'home' ? '' : doc.slug
|
||||
const { pathname } = new URL(`${serverUrl}/${pathToUse}`)
|
||||
const { pathname } = new URL(`${process.env.PAYLOAD_PUBLIC_SERVER_URL}/${pathToUse}`)
|
||||
return pathname
|
||||
}
|
||||
|
||||
@@ -23,7 +22,7 @@ export const Pages: CollectionConfig = {
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
preview: doc => {
|
||||
return `${serverUrl}${formatAppURL({ doc })}?preview=true`
|
||||
return `${process.env.PAYLOAD_PUBLIC_SERVER_URL}${formatAppURL({ doc })}?preview=true`
|
||||
},
|
||||
},
|
||||
versions: {
|
||||
@@ -2,7 +2,6 @@ import type { CollectionConfig } from 'payload/types'
|
||||
|
||||
import { loggedIn } from '../../access/loggedIn'
|
||||
import { publishedOrLoggedIn } from '../../access/publishedOrLoggedIn'
|
||||
import { serverUrl } from '../../app/_utils/api'
|
||||
import { Content } from '../../blocks/Content'
|
||||
import { Form } from '../../blocks/Form'
|
||||
import { MediaBlock } from '../../blocks/Media'
|
||||
@@ -15,7 +14,7 @@ export const Projects: CollectionConfig = {
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
preview: doc => {
|
||||
return `${serverUrl}/projects/${doc.slug}?preview=true`
|
||||
return `${process.env.PAYLOAD_PUBLIC_SERVER_URL}/projects/${doc.slug}?preview=true`
|
||||
},
|
||||
},
|
||||
versions: {
|
||||
@@ -0,0 +1,40 @@
|
||||
import React, { Fragment, useCallback, useState } from 'react'
|
||||
|
||||
export const SeedButton: React.FC = () => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [seeded, setSeeded] = useState(false)
|
||||
const [error, setError] = useState(null)
|
||||
|
||||
const handleClick = useCallback(
|
||||
async e => {
|
||||
e.preventDefault()
|
||||
if (loading || seeded) return
|
||||
|
||||
setLoading(true)
|
||||
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
await fetch('/api/seed')
|
||||
setSeeded(true)
|
||||
} catch (err) {
|
||||
setError(err)
|
||||
}
|
||||
}, 1000)
|
||||
},
|
||||
[loading, seeded],
|
||||
)
|
||||
|
||||
let message = ''
|
||||
if (loading) message = ' (seeding...)'
|
||||
if (seeded) message = ' (done!)'
|
||||
if (error) message = ` (error: ${error})`
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<a href="/api/seed" target="_blank" rel="noopener noreferrer" onClick={handleClick}>
|
||||
Seed your database
|
||||
</a>
|
||||
{message}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
@import '~payload/scss';
|
||||
|
||||
.dashboard .before-dashboard {
|
||||
margin-bottom: base(1.5);
|
||||
|
||||
&__banner {
|
||||
& h4 {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__instructions {
|
||||
list-style: decimal;
|
||||
margin-bottom: base(0.5);
|
||||
|
||||
& li {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
& a:hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import React from 'react'
|
||||
import { Banner } from 'payload/components'
|
||||
|
||||
import { SeedButton } from './SeedButton'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
const baseClass = 'before-dashboard'
|
||||
|
||||
const BeforeDashboard: React.FC = () => {
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<Banner className={`${baseClass}__banner`} type="success">
|
||||
<h4>Welcome to your dashboard!</h4>
|
||||
</Banner>
|
||||
Here's what to do next:
|
||||
<ul className={`${baseClass}__instructions`}>
|
||||
<li>
|
||||
<SeedButton />
|
||||
{
|
||||
' with a few pages, projects, and technologies to jump-start your new developer portfolio, then '
|
||||
}
|
||||
<a href="/">visit your portfolio</a>
|
||||
{' to see the results.'}
|
||||
</li>
|
||||
<li>
|
||||
If you created this repo using Payload Cloud, head over to GitHub and clone it to your
|
||||
local machine. It will be under the <i>GitHub Scope</i> that you selected when creating
|
||||
this project.
|
||||
</li>
|
||||
<li>
|
||||
{'Modify your '}
|
||||
<a
|
||||
href="https://payloadcms.com/docs/configuration/collections"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
collections
|
||||
</a>
|
||||
{' and add more '}
|
||||
<a
|
||||
href="https://payloadcms.com/docs/fields/overview"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
fields
|
||||
</a>
|
||||
{' as needed. If you are new to Payload, we also recommend you check out the '}
|
||||
<a
|
||||
href="https://payloadcms.com/docs/getting-started/what-is-payload"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Getting Started
|
||||
</a>
|
||||
{' docs.'}
|
||||
</li>
|
||||
<li>
|
||||
Commit and push your changes to the repository to trigger a redeployment of your project.
|
||||
</li>
|
||||
</ul>
|
||||
{'Pro Tip: This block is a '}
|
||||
<a
|
||||
href={'https://payloadcms.com/docs/admin/components#base-component-overrides'}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
custom component
|
||||
</a>
|
||||
, you can remove it at any time by updating your <strong>payload.config</strong>.
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default BeforeDashboard
|
||||
21
templates/developer-portfolio/src/payload/endpoints/seed.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import type { PayloadHandler } from 'payload/config'
|
||||
|
||||
import { seed as seedScript } from '../seed'
|
||||
|
||||
export const seed: PayloadHandler = async (req, res): Promise<void> => {
|
||||
const { user, payload } = req
|
||||
|
||||
if (!user) {
|
||||
res.status(401).json({ error: 'Unauthorized' })
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await seedScript(payload)
|
||||
res.json({ success: true })
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : 'Unknown error'
|
||||
payload.logger.error(message)
|
||||
res.json({ error: message })
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,24 @@
|
||||
import { payloadCloud } from '@payloadcms/plugin-cloud'
|
||||
import formBuilder from '@payloadcms/plugin-form-builder'
|
||||
import seo from '@payloadcms/plugin-seo'
|
||||
import dotenv from 'dotenv'
|
||||
import path from 'path'
|
||||
|
||||
dotenv.config({
|
||||
path: path.resolve(__dirname, '../.env'),
|
||||
})
|
||||
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
import { serverUrl } from './app/_utils/api'
|
||||
import { Media } from './collections/Media'
|
||||
import { Pages } from './collections/Pages'
|
||||
import { Projects } from './collections/Projects'
|
||||
import { Technologies } from './collections/Technologies'
|
||||
import { Users } from './collections/Users'
|
||||
import BeforeDashboard from './components/BeforeDashboard'
|
||||
import { seed } from './endpoints/seed'
|
||||
import { Header } from './globals/Header'
|
||||
import { Profile } from './globals/Profile'
|
||||
|
||||
dotenv.config({
|
||||
path: path.resolve(__dirname, '../../.env'),
|
||||
})
|
||||
|
||||
const plugins = [
|
||||
formBuilder({
|
||||
fields: {
|
||||
@@ -37,14 +38,31 @@ const plugins = [
|
||||
collections: ['pages', 'projects'],
|
||||
uploadsCollection: 'media',
|
||||
}),
|
||||
payloadCloud(),
|
||||
]
|
||||
|
||||
export default buildConfig({
|
||||
serverURL: serverUrl || '',
|
||||
admin: {
|
||||
components: {
|
||||
// The `BeforeDashboard` component renders the 'welcome' block that you see after logging into your admin panel.
|
||||
// Feel free to delete this at any time. Simply remove the line below and the import `BeforeDashboard` statement on line 15.
|
||||
beforeDashboard: [BeforeDashboard],
|
||||
},
|
||||
},
|
||||
serverURL: process.env.PAYLOAD_PUBLIC_SERVER_URL,
|
||||
collections: [Media, Pages, Projects, Technologies, Users],
|
||||
globals: [Header, Profile],
|
||||
plugins,
|
||||
typescript: {
|
||||
outputFile: path.resolve(__dirname, 'payload-types.ts'),
|
||||
},
|
||||
endpoints: [
|
||||
// The seed endpoint is used to populate the database with some example data
|
||||
// You should delete this endpoint before deploying your site to production
|
||||
{
|
||||
path: '/seed',
|
||||
method: 'get',
|
||||
handler: seed,
|
||||
},
|
||||
],
|
||||
})
|
||||
99
templates/developer-portfolio/src/payload/seed/index.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import type { Payload } from 'payload'
|
||||
|
||||
import type { Header, Profile } from '../payload-types'
|
||||
import { seedForms } from './forms'
|
||||
import { seedGlobals } from './globals'
|
||||
import { seedMedia } from './media'
|
||||
import { seedPages } from './pages'
|
||||
import { seedProjects } from './projects'
|
||||
import { seedTechnologies } from './technologies'
|
||||
|
||||
type GlobalType = 'profile' | 'header'
|
||||
|
||||
const collections = ['forms', 'media', 'pages', 'projects', 'technologies']
|
||||
// const globals = ['profile', 'header']
|
||||
const globals: GlobalType[] = ['profile', 'header']
|
||||
|
||||
const getDefaultData = (globalName: GlobalType): Header | Profile => {
|
||||
switch (globalName) {
|
||||
case 'profile':
|
||||
return {
|
||||
id: '',
|
||||
name: 'Default',
|
||||
}
|
||||
case 'header':
|
||||
return {
|
||||
id: '',
|
||||
}
|
||||
default:
|
||||
throw new Error('Invalid global name')
|
||||
}
|
||||
}
|
||||
|
||||
// Next.js revalidation errors are normal when seeding the database without a server running
|
||||
// i.e. running `yarn seed` locally instead of using the admin UI within an active app
|
||||
// The app is not running to revalidate the pages and so the API routes are not available
|
||||
// These error messages can be ignored: `Error hitting revalidate route for...`
|
||||
export const seed = async (payload: Payload): Promise<void> => {
|
||||
payload.logger.info('Seeding database...')
|
||||
|
||||
// we need to clear the media directory before seeding
|
||||
// as well as the collections and globals
|
||||
// this is because while `yarn seed` drops the database
|
||||
// the custom `/api/seed` endpoint does not
|
||||
|
||||
payload.logger.info(`— Clearing media...`)
|
||||
|
||||
const mediaDir = path.resolve(__dirname, '../../media')
|
||||
if (fs.existsSync(mediaDir)) {
|
||||
fs.rmdirSync(mediaDir, { recursive: true })
|
||||
}
|
||||
|
||||
payload.logger.info(`— Clearing collections and globals...`)
|
||||
|
||||
// clear the database
|
||||
await Promise.all([
|
||||
...collections.map(async collection =>
|
||||
payload.delete({
|
||||
collection,
|
||||
where: {},
|
||||
}),
|
||||
), // eslint-disable-line function-paren-newline
|
||||
globals.map(async global => {
|
||||
const defaultData = getDefaultData(global)
|
||||
|
||||
await payload.updateGlobal({
|
||||
slug: global,
|
||||
data: defaultData,
|
||||
})
|
||||
}),
|
||||
])
|
||||
|
||||
payload.logger.info(`— Seeding form...`)
|
||||
|
||||
const forms = await seedForms()
|
||||
|
||||
payload.logger.info(`— Seeding media...`)
|
||||
|
||||
const media = await seedMedia()
|
||||
|
||||
payload.logger.info(`— Seeding globals...`)
|
||||
|
||||
await seedGlobals(media)
|
||||
|
||||
payload.logger.info(`— Seeding technologies...`)
|
||||
|
||||
const technologies = await seedTechnologies()
|
||||
|
||||
payload.logger.info(`— Seeding projects...`)
|
||||
|
||||
const projects = await seedProjects(media, technologies)
|
||||
|
||||
payload.logger.info(`— Seeding pages...`)
|
||||
|
||||
await seedPages(forms, projects)
|
||||
|
||||
payload.logger.info('Seeded database successfully!')
|
||||
}
|
||||
@@ -7,7 +7,7 @@ export async function seedMedia() {
|
||||
data: {
|
||||
alt: 'Profile picture',
|
||||
},
|
||||
filePath: `${__dirname}/media/headshot.png`,
|
||||
filePath: `${__dirname}/media/headshot.jpg`,
|
||||
})
|
||||
|
||||
const designDesignFeaturedScreenshot = await payload.create({
|
||||
@@ -15,7 +15,7 @@ export async function seedMedia() {
|
||||
data: {
|
||||
alt: 'Marketing Image for Pre-Launch',
|
||||
},
|
||||
filePath: `${__dirname}/media/design-design-featured.png`,
|
||||
filePath: `${__dirname}/media/design-design-featured.jpg`,
|
||||
})
|
||||
|
||||
const outsideAppFeaturedScreenshot = await payload.create({
|
||||
@@ -23,7 +23,7 @@ export async function seedMedia() {
|
||||
data: {
|
||||
alt: 'Marketing Image for Pre-Launch',
|
||||
},
|
||||
filePath: `${__dirname}/media/outside-app-featured.png`,
|
||||
filePath: `${__dirname}/media/outside-app-featured.jpg`,
|
||||
})
|
||||
|
||||
const designAppFeaturedScreenshot = await payload.create({
|
||||
@@ -31,7 +31,7 @@ export async function seedMedia() {
|
||||
data: {
|
||||
alt: 'Marketing Image for Pre-Launch',
|
||||
},
|
||||
filePath: `${__dirname}/media/design-app-featured.png`,
|
||||
filePath: `${__dirname}/media/design-app-featured.jpg`,
|
||||
})
|
||||
|
||||
const artAppFeaturedScreenshot = await payload.create({
|
||||
@@ -39,7 +39,7 @@ export async function seedMedia() {
|
||||
data: {
|
||||
alt: 'Marketing Image for Pre-Launch',
|
||||
},
|
||||
filePath: `${__dirname}/media/art-app-featured.png`,
|
||||
filePath: `${__dirname}/media/art-app-featured.jpg`,
|
||||
})
|
||||
|
||||
const genericMarketingImageOne = await payload.create({
|
||||
@@ -47,7 +47,7 @@ export async function seedMedia() {
|
||||
data: {
|
||||
alt: 'Marketing Image for Pre-Launch',
|
||||
},
|
||||
filePath: `${__dirname}/media/generic-1.png`,
|
||||
filePath: `${__dirname}/media/generic-1.jpg`,
|
||||
})
|
||||
|
||||
const genericMarketingImageTwo = await payload.create({
|
||||
@@ -55,7 +55,7 @@ export async function seedMedia() {
|
||||
data: {
|
||||
alt: 'Marketing Image for Pre-Launch',
|
||||
},
|
||||
filePath: `${__dirname}/media/generic-2.png`,
|
||||
filePath: `${__dirname}/media/generic-2.jpg`,
|
||||
})
|
||||
|
||||
const genericMarketingImageThree = await payload.create({
|
||||
@@ -63,7 +63,7 @@ export async function seedMedia() {
|
||||
data: {
|
||||
alt: 'UI/UX Examples',
|
||||
},
|
||||
filePath: `${__dirname}/media/generic-3.png`,
|
||||
filePath: `${__dirname}/media/generic-3.jpg`,
|
||||
})
|
||||
|
||||
return {
|
||||
|
Before Width: | Height: | Size: 280 KiB After Width: | Height: | Size: 280 KiB |
|
Before Width: | Height: | Size: 333 KiB After Width: | Height: | Size: 333 KiB |
|
Before Width: | Height: | Size: 266 KiB After Width: | Height: | Size: 266 KiB |
|
Before Width: | Height: | Size: 337 KiB After Width: | Height: | Size: 337 KiB |
|
Before Width: | Height: | Size: 308 KiB After Width: | Height: | Size: 308 KiB |
|
Before Width: | Height: | Size: 371 KiB After Width: | Height: | Size: 371 KiB |
|
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 234 KiB After Width: | Height: | Size: 234 KiB |
@@ -1,6 +1,15 @@
|
||||
import payload from 'payload'
|
||||
|
||||
export const seedUsers = async (): Promise<void> => {
|
||||
await payload.delete({
|
||||
collection: 'users',
|
||||
where: {
|
||||
email: {
|
||||
equals: 'dev@payloadcms.com',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// create admin
|
||||
await payload.create({
|
||||
collection: 'users',
|
||||
@@ -1,9 +1,7 @@
|
||||
import type { AfterChangeHook, TypeWithID } from 'payload/dist/collections/config/types'
|
||||
|
||||
import { serverUrl } from '../app/_utils/api'
|
||||
|
||||
export const formatAppURL = ({ doc }): string => {
|
||||
const { pathname } = new URL(`${serverUrl}/projects/${doc.slug}`)
|
||||
const { pathname } = new URL(`${process.env.PAYLOAD_PUBLIC_SERVER_URL}/projects/${doc.slug}`)
|
||||
return pathname
|
||||
}
|
||||
|
||||
@@ -18,7 +16,7 @@ export const tagRevalidator =
|
||||
const tag = getTag(doc)
|
||||
try {
|
||||
const res = await fetch(
|
||||
`${serverUrl}/api/revalidate?secret=${process.env.REVALIDATION_KEY}&tag=${tag}`,
|
||||
`${process.env.PAYLOAD_PUBLIC_SERVER_URL}/api/revalidate?secret=${process.env.REVALIDATION_KEY}&tag=${tag}`,
|
||||
)
|
||||
|
||||
if (res.ok) {
|
||||
@@ -1,24 +0,0 @@
|
||||
import type { Payload } from 'payload'
|
||||
|
||||
import { seedForms } from './forms'
|
||||
import { seedGlobals } from './globals'
|
||||
import { seedMedia } from './media'
|
||||
import { seedPages } from './pages'
|
||||
import { seedProjects } from './projects'
|
||||
import { seedTechnologies } from './technologies'
|
||||
import { seedUsers } from './users'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export const seed = async (_payload: Payload): Promise<void> => {
|
||||
await seedUsers()
|
||||
|
||||
const forms = await seedForms()
|
||||
const media = await seedMedia()
|
||||
|
||||
await seedGlobals(media)
|
||||
|
||||
const technologies = await seedTechnologies()
|
||||
const projects = await seedProjects(media, technologies)
|
||||
|
||||
await seedPages(forms, projects)
|
||||
}
|
||||
@@ -12,8 +12,7 @@ dotenv.config({
|
||||
import express from 'express'
|
||||
import payload from 'payload'
|
||||
|
||||
import { serverUrl } from './app/_utils/api'
|
||||
import { seed } from './seed'
|
||||
import { seed } from './payload/seed'
|
||||
|
||||
const app = express()
|
||||
const PORT = process.env.PORT || 3000
|
||||
@@ -36,10 +35,11 @@ const start = async (): Promise<void> => {
|
||||
if (process.env.PAYLOAD_SEED === 'true') {
|
||||
payload.logger.info('---- SEEDING DATABASE ----')
|
||||
await seed(payload)
|
||||
process.exit()
|
||||
}
|
||||
|
||||
app.listen(PORT, async () => {
|
||||
payload.logger.info(`App URL: ${serverUrl}`)
|
||||
payload.logger.info(`App URL: ${process.env.PAYLOAD_PUBLIC_SERVER_URL}`)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,7 @@ dotenv.config({
|
||||
import express from 'express'
|
||||
import payload from 'payload'
|
||||
|
||||
import { serverUrl } from './app/_utils/api'
|
||||
import { seed } from './seed'
|
||||
import { seed } from './payload/seed'
|
||||
|
||||
const app = express()
|
||||
const PORT = process.env.PORT || 3000
|
||||
@@ -29,6 +28,7 @@ const start = async (): Promise<void> => {
|
||||
if (process.env.PAYLOAD_SEED === 'true') {
|
||||
payload.logger.info('---- SEEDING DATABASE ----')
|
||||
await seed(payload)
|
||||
process.exit()
|
||||
}
|
||||
|
||||
if (process.env.NEXT_BUILD) {
|
||||
@@ -54,7 +54,7 @@ const start = async (): Promise<void> => {
|
||||
payload.logger.info('Next.js started')
|
||||
|
||||
app.listen(PORT, async () => {
|
||||
payload.logger.info(`Next.js App URL: ${serverUrl}`)
|
||||
payload.logger.info(`Next.js App URL: ${process.env.PAYLOAD_PUBLIC_SERVER_URL}`)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
},
|
||||
"include": [
|
||||
"src/server.ts",
|
||||
"src/payload.config.ts",
|
||||
"src/payload/payload.config.ts",
|
||||
]
|
||||
}
|
||||
|
||||