Skip to content

Commit

Permalink
feat: add graphql-starter
Browse files Browse the repository at this point in the history
  • Loading branch information
shadcn committed Oct 27, 2022
0 parents commit dbea732
Show file tree
Hide file tree
Showing 28 changed files with 2,873 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# See https://next-drupal.org/docs/environment-variables
NEXT_PUBLIC_DRUPAL_BASE_URL=https://dev.next-drupal.org
NEXT_IMAGE_DOMAIN=dev.next-drupal.org

# Authentication
DRUPAL_CLIENT_ID=
DRUPAL_CLIENT_SECRET=

# Required for Preview Mode
DRUPAL_PREVIEW_SECRET=secret
4 changes: 4 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "next",
"root": true
}
38 changes: 38 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/
.next

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local

# vercel
.vercel

/certificates/*
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# GraphQL Starter

A next-drupal starter for building your site with Next.js and GraphQL.

## How to use

`npx create-next-app -e https://github.com/chapter-three next-drupal-graphql-starter`

## Documentation

See https://next-drupal.org
28 changes: 28 additions & 0 deletions components/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Link from "next/link"

import { PreviewAlert } from "components/preview-alert"

export function Layout({ children }) {
return (
<>
<PreviewAlert />
<div className="max-w-screen-md px-6 mx-auto">
<header>
<div className="container flex items-center justify-between py-6 mx-auto">
<Link href="/" passHref>
<a className="text-2xl font-semibold no-underline">
Next.js for Drupal
</a>
</Link>
<Link href="https://next-drupal.org/docs" passHref>
<a target="_blank" rel="external" className="hover:text-blue-600">
Read the docs
</a>
</Link>
</div>
</header>
<main className="container py-10 mx-auto">{children}</main>
</div>
</>
)
}
58 changes: 58 additions & 0 deletions components/node--article--teaser.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import Image from "next/image"
import Link from "next/link"

import { absoluteUrl, formatDate } from "lib/utils"
import { Article } from "types"

interface NodeArticleTeaserProps {
node: Partial<Article>
}

export function NodeArticleTeaser({ node, ...props }: NodeArticleTeaserProps) {
return (
<article {...props}>
<Link href={node.path} passHref>
<a className="no-underline hover:text-blue-600">
<h2 className="mb-4 text-4xl font-bold">{node.title}</h2>
</a>
</Link>
<div className="mb-4 text-gray-600">
{node.author?.displayName ? (
<span>
Posted by{" "}
<span className="font-semibold">{node.author.displayName}</span>
</span>
) : null}
<span> - {formatDate(node.created)}</span>
</div>
{node.image && (
<figure className="my-4">
<Image
src={node.image.url}
width={768}
height={480}
layout="responsive"
objectFit="cover"
alt={node.title}
/>
</figure>
)}
<Link href={node.path} passHref>
<a className="inline-flex items-center px-6 py-2 border border-gray-600 rounded-full hover:bg-gray-100">
Read article
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="w-4 h-4 ml-2"
>
<path d="M5 12h14M12 5l7 7-7 7" />
</svg>
</a>
</Link>
</article>
)
}
43 changes: 43 additions & 0 deletions components/node--article.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import Image from "next/image"

import { formatDate } from "lib/utils"
import { Article } from "types"

interface NodeArticleProps {
node: Article
}

export function NodeArticle({ node, ...props }: NodeArticleProps) {
return (
<article {...props}>
<h1 className="mb-4 text-6xl font-black leading-tight">{node.title}</h1>
<div className="mb-4 text-gray-600">
{node.author?.displayName ? (
<span>
Posted by{" "}
<span className="font-semibold">{node.author.displayName}</span>
</span>
) : null}
<span> - {formatDate(node.created)}</span>
</div>
{node.image && (
<figure className="my-4">
<Image
src={node.image.url}
width={768}
height={480}
layout="responsive"
objectFit="cover"
alt={node.title}
/>
</figure>
)}
{node.body?.processed && (
<div
dangerouslySetInnerHTML={{ __html: node.body.processed }}
className="mt-6 font-serif text-xl leading-loose prose"
/>
)}
</article>
)
}
19 changes: 19 additions & 0 deletions components/node--basic-page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Page } from "types"

interface NodeBasicPageProps {
node: Page
}

export function NodeBasicPage({ node, ...props }: NodeBasicPageProps) {
return (
<article {...props}>
<h1 className="mb-4 text-6xl font-black leading-tight">{node.title}</h1>
{node.body?.processed && (
<div
dangerouslySetInnerHTML={{ __html: node.body?.processed }}
className="mt-6 font-serif text-xl leading-loose prose"
/>
)}
</article>
)
}
28 changes: 28 additions & 0 deletions components/preview-alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as React from "react"
import { useRouter } from "next/router"

export function PreviewAlert() {
const { isPreview } = useRouter()
const [showPreviewAlert, setShowPreviewAlert] = React.useState<boolean>(false)

React.useEffect(() => {
setShowPreviewAlert(isPreview && window.top === window.self)
}, [isPreview])

if (!showPreviewAlert) {
return null
}

return (
<div className="sticky top-0 left-0 z-50 w-full px-2 py-1 text-center text-white bg-black">
<p className="mb-0">
This page is a preview.{" "}
{/* eslint-disable @next/next/no-html-link-for-pages */}
<a href="/api/exit-preview" className="text-white underline">
Click here
</a>{" "}
to exit preview mode.
</p>
</div>
)
}
47 changes: 47 additions & 0 deletions lib/drupal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { DrupalClient } from "next-drupal"

export const drupal = new DrupalClient(
process.env.NEXT_PUBLIC_DRUPAL_BASE_URL,
{
previewSecret: process.env.DRUPAL_PREVIEW_SECRET,
auth: {
clientId: process.env.DRUPAL_CLIENT_ID,
clientSecret: process.env.DRUPAL_CLIENT_SECRET,
},
}
)

export const graphqlEndpoint = drupal.buildUrl("/graphql")

type QueryPayload = {
query: string
variables?: Record<string, string>
}

type QueryJsonResponse<DataType> = {
data?: DataType
errors?: { message: string }[]
}

// This is a wrapper around drupal.fetch.
// Acts as a query helper.
export async function query<DataType>(payload: QueryPayload) {
const response = await drupal.fetch(graphqlEndpoint.toString(), {
method: "POST",
body: JSON.stringify(payload),
withAuth: true, // Make authenticated requests using OAuth.
})

if (!response?.ok) {
throw new Error(response.statusText)
}

const { data, errors }: QueryJsonResponse<DataType> = await response.json()

if (errors) {
console.log(errors)
throw new Error(errors?.map((e) => e.message).join("\n") ?? "unknown")
}

return data
}
12 changes: 12 additions & 0 deletions lib/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export function formatDate(input: string): string {
const date = new Date(input)
return date.toLocaleDateString("en-US", {
month: "long",
day: "numeric",
year: "numeric",
})
}

export function absoluteUrl(input: string) {
return `${process.env.NEXT_PUBLIC_DRUPAL_BASE_URL}${input}`
}
5 changes: 5 additions & 0 deletions next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
8 changes: 8 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
domains: [process.env.NEXT_IMAGE_DOMAIN],
},
}

module.exports = nextConfig
31 changes: 31 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "graphql-starter",
"version": "0.0.1",
"private": true,
"license": "MIT",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"preview": "next build && next start",
"lint": "next lint"
},
"dependencies": {
"@tailwindcss/typography": "^0.5.2",
"next": "^12.2.3",
"next-drupal": "^1.5.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"sharp": "^0.30.7"
},
"devDependencies": {
"@types/node": "^17.0.23",
"@types/react": "^17.0.43",
"autoprefixer": "^10.4.4",
"eslint": "^8.12.0",
"eslint-config-next": "^12.1.4",
"postcss": "^8.4.12",
"tailwindcss": "^3.0.23",
"typescript": "^4.5.5"
}
}
Loading

0 comments on commit dbea732

Please sign in to comment.