From e7dbc683ac1e64d3d265cd7a3dcb8a1335baf4f4 Mon Sep 17 00:00:00 2001 From: Xavier Mirabelli-Montan Date: Fri, 12 Apr 2024 11:05:32 +0100 Subject: [PATCH] Fixing provision scripts and adding puck to frontend --- .docksal/docksal.yml | 6 +- .../powerstack_api/powerstack_api.install | 2 + .../development/app/blocks/Hero/Dots.tsx | 119 ++++++++++++++++++ .../app/blocks/Hero/Hero.module.css | 101 +++++++++++++++ .../development/app/blocks/Hero/Hero.tsx | 40 ++++++ .../app/blocks/Stats/Stats.module.css | 58 +++++++++ .../development/app/blocks/Stats/Stats.tsx | 14 +++ .../app/blocks/Text/Text.module.css | 43 +++++++ .../development/app/blocks/Text/Text.tsx | 17 +++ frontend/starters/development/app/layout.tsx | 18 ++- .../app/puck/[...puckPath]/client.tsx | 20 +++ .../app/puck/[...puckPath]/page.tsx | 40 ++++++ .../development/app/puck/api/route.ts | 25 ++++ .../starters/development/app/puck/page.tsx | 1 + .../components/Footer/Footer.module.css | 89 +++++++++++++ .../development/components/Footer/Footer.tsx | 89 +++++++++++++ .../components/Header/Header.module.css | 31 +++++ .../development/components/Header/Header.tsx | 86 +++++++++++++ .../components/drupal/BasicPage.tsx | 2 +- frontend/starters/development/database.json | 1 + .../development/images/logo-nav-light.svg | 70 +++++++++++ frontend/starters/development/lib/get-page.ts | 11 ++ frontend/starters/development/middleware.ts | 27 ++++ frontend/starters/development/package.json | 10 +- .../starters/development/postcss.config.cjs | 14 +++ .../starters/development/postcss.config.js | 8 -- frontend/starters/development/puck.config.tsx | 88 +++++++++++++ .../starters/development/tailwind.config.ts | 18 --- frontend/starters/development/theme.ts | 21 ++++ scripts/misc/add_backend_creds_to_env_file.sh | 6 - scripts/setup/init.sh | 1 - scripts/setup/init_env_file.sh | 3 +- 32 files changed, 1032 insertions(+), 47 deletions(-) create mode 100644 frontend/starters/development/app/blocks/Hero/Dots.tsx create mode 100644 frontend/starters/development/app/blocks/Hero/Hero.module.css create mode 100644 frontend/starters/development/app/blocks/Hero/Hero.tsx create mode 100644 frontend/starters/development/app/blocks/Stats/Stats.module.css create mode 100644 frontend/starters/development/app/blocks/Stats/Stats.tsx create mode 100644 frontend/starters/development/app/blocks/Text/Text.module.css create mode 100644 frontend/starters/development/app/blocks/Text/Text.tsx create mode 100644 frontend/starters/development/app/puck/[...puckPath]/client.tsx create mode 100644 frontend/starters/development/app/puck/[...puckPath]/page.tsx create mode 100644 frontend/starters/development/app/puck/api/route.ts create mode 100644 frontend/starters/development/app/puck/page.tsx create mode 100644 frontend/starters/development/components/Footer/Footer.module.css create mode 100644 frontend/starters/development/components/Footer/Footer.tsx create mode 100644 frontend/starters/development/components/Header/Header.module.css create mode 100644 frontend/starters/development/components/Header/Header.tsx create mode 100644 frontend/starters/development/database.json create mode 100644 frontend/starters/development/images/logo-nav-light.svg create mode 100644 frontend/starters/development/lib/get-page.ts create mode 100644 frontend/starters/development/middleware.ts create mode 100644 frontend/starters/development/postcss.config.cjs delete mode 100644 frontend/starters/development/postcss.config.js create mode 100644 frontend/starters/development/puck.config.tsx delete mode 100644 frontend/starters/development/tailwind.config.ts create mode 100644 frontend/starters/development/theme.ts delete mode 100755 scripts/misc/add_backend_creds_to_env_file.sh diff --git a/.docksal/docksal.yml b/.docksal/docksal.yml index 12e7e2b..d96de14 100644 --- a/.docksal/docksal.yml +++ b/.docksal/docksal.yml @@ -72,10 +72,6 @@ services: extends: file: ${HOME}/.docksal/stacks/services.yml service: cli - volumes: - # Keep write-heavy folders in a volumes (improves fs performance on non-Linux platforms) - # - frontend_node_modules:/var/www/frontend/node_modules - - frontend_cache:/var/www/frontend/starters/${FRONTEND_STARTER}/.next # gatsby build and preview cannot share the same .cache folder labels: - io.docksal.virtual-host=frontend.${VIRTUAL_HOST} - io.docksal.virtual-port=3000 @@ -86,7 +82,7 @@ services: - NEXT_IMAGE_DOMAIN - NEXT_DRUPAL_HOST - NEXT_HOST - - DRUPAL_CLIENT_SECRET + - DRUPAL_CLIENT_ID - DRUPAL_CLIENT_SECRET - DRUPAL_PREVIEW_SECRET - NEXT_REVALIDATE_SECRET diff --git a/backend/starters/development/web/modules/custom/powerstack_api/powerstack_api.install b/backend/starters/development/web/modules/custom/powerstack_api/powerstack_api.install index 0ca2997..0c6296e 100644 --- a/backend/starters/development/web/modules/custom/powerstack_api/powerstack_api.install +++ b/backend/starters/development/web/modules/custom/powerstack_api/powerstack_api.install @@ -41,7 +41,9 @@ function powerstack_api_install() { // Create new consumer. Consumer::create([ 'label' => 'Next.js Consumer', + 'client_id' => 'next', 'description' => 'This is the consumer for your Power Stack site.', + 'uuid' => \Drupal::service('uuid')->generate(), 'new_secret' => getenv('NEXT_CLIENT_SECRET'), 'is_default' => TRUE, ])->save(); diff --git a/frontend/starters/development/app/blocks/Hero/Dots.tsx b/frontend/starters/development/app/blocks/Hero/Dots.tsx new file mode 100644 index 0000000..5c1a377 --- /dev/null +++ b/frontend/starters/development/app/blocks/Hero/Dots.tsx @@ -0,0 +1,119 @@ +export interface DotsProps extends React.ComponentPropsWithoutRef<'svg'> { + size?: number; + radius?: number; + } + + export function Dots({ size = 185, radius = 2.5, ...others }: DotsProps) { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); + } \ No newline at end of file diff --git a/frontend/starters/development/app/blocks/Hero/Hero.module.css b/frontend/starters/development/app/blocks/Hero/Hero.module.css new file mode 100644 index 0000000..16060bd --- /dev/null +++ b/frontend/starters/development/app/blocks/Hero/Hero.module.css @@ -0,0 +1,101 @@ +.wrapper { + position: relative; + padding-top: rem(120px); + padding-bottom: rem(80px); + + @media (max-width: $mantine-breakpoint-sm) { + padding-top: rem(80px); + padding-bottom: rem(60px); + } + } + + .inner { + position: relative; + z-index: 1; + } + + .dots { + position: absolute; + color: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5)); + + @media (max-width: $mantine-breakpoint-sm) { + display: none; + } + } + + .dotsLeft { + left: 0; + top: 0; + } + + .title { + text-align: center; + font-weight: 800; + font-size: rem(40px); + letter-spacing: -1px; + color: light-dark(var(--mantine-color-black), var(--mantine-color-white)); + margin-bottom: var(--mantine-spacing-xs); + font-family: + Greycliff CF, + var(--mantine-font-family); + + @media (max-width: $mantine-breakpoint-xs) { + font-size: rem(28px); + text-align: left; + } + } + .subtitle { + text-align: center; + font-weight: 800; + font-size: rem(32px); + letter-spacing: -1px; + color: light-dark(var(--mantine-color-black), var(--mantine-color-white)); + margin-bottom: var(--mantine-spacing-xs); + font-family: + Greycliff CF, + var(--mantine-font-family); + + @media (max-width: $mantine-breakpoint-xs) { + font-size: rem(24px); + text-align: left; + } + } + + .highlight { + color: light-dark(var(--mantine-color-blue-6), var(--mantine-color-blue-4)); + } + + .description { + text-align: center; + + @media (max-width: $mantine-breakpoint-xs) { + text-align: left; + font-size: var(--mantine-font-size-md); + } + } + + .controls { + margin-top: var(--mantine-spacing-lg); + display: flex; + justify-content: center; + + @media (max-width: $mantine-breakpoint-xs) { + flex-direction: column; + } + } + + .control { + &:not(:first-of-type) { + margin-left: var(--mantine-spacing-md); + } + + @media (max-width: $mantine-breakpoint-xs) { + height: rem(42px); + font-size: var(--mantine-font-size-md); + + &:not(:first-of-type) { + margin-top: var(--mantine-spacing-md); + margin-left: 0; + } + } + } \ No newline at end of file diff --git a/frontend/starters/development/app/blocks/Hero/Hero.tsx b/frontend/starters/development/app/blocks/Hero/Hero.tsx new file mode 100644 index 0000000..ee835a8 --- /dev/null +++ b/frontend/starters/development/app/blocks/Hero/Hero.tsx @@ -0,0 +1,40 @@ +import { Title, Text, Button, Container } from '@mantine/core'; +import { Dots } from './Dots'; +import classes from './Hero.module.css'; + +export function HeroBlock({title, subtitle, description, buttons}) { + return ( + + + + + + +
+ + <Text component="span" className={classes.highlight} inherit> + {title} + </Text> + + + {subtitle} + + + + + {description} + + + +
+ + +
+
+
+ ); +} \ No newline at end of file diff --git a/frontend/starters/development/app/blocks/Stats/Stats.module.css b/frontend/starters/development/app/blocks/Stats/Stats.module.css new file mode 100644 index 0000000..adb756f --- /dev/null +++ b/frontend/starters/development/app/blocks/Stats/Stats.module.css @@ -0,0 +1,58 @@ +.root { + margin: calc(var(--mantine-spacing-xl) * 1.5) 0 ; + display: flex; + background-image: linear-gradient( + -60deg, + var(--mantine-color-blue-4) 0%, + var(--mantine-color-blue-7) 100% + ); + padding: calc(var(--mantine-spacing-xl) * 1.5); + border-radius: var(--mantine-radius-md); + + @media (max-width: $mantine-breakpoint-sm) { + flex-direction: column; + } + } + + .title { + color: var(--mantine-color-white); + text-transform: uppercase; + font-weight: 700; + font-size: var(--mantine-font-size-sm); + } + + .count { + color: var(--mantine-color-white); + font-size: rem(32px); + line-height: 1; + font-weight: 700; + margin-bottom: var(--mantine-spacing-md); + font-family: + Greycliff CF, + var(--mantine-font-family); + } + + .description { + color: var(--mantine-color-blue-0); + font-size: var(--mantine-font-size-sm); + margin-top: rem(5px); + } + + .stat { + flex: 1; + + & + & { + padding-left: var(--mantine-spacing-xl); + margin-left: var(--mantine-spacing-xl); + border-left: rem(1px) solid var(--mantine-color-blue-3); + + @media (max-width: $mantine-breakpoint-sm) { + padding-left: 0; + margin-left: 0; + border-left: 0; + padding-top: var(--mantine-spacing-xl); + margin-top: var(--mantine-spacing-xl); + border-top: rem(1px) solid var(--mantine-color-blue-3); + } + } + } \ No newline at end of file diff --git a/frontend/starters/development/app/blocks/Stats/Stats.tsx b/frontend/starters/development/app/blocks/Stats/Stats.tsx new file mode 100644 index 0000000..bbd161f --- /dev/null +++ b/frontend/starters/development/app/blocks/Stats/Stats.tsx @@ -0,0 +1,14 @@ +import { Text, Container } from '@mantine/core'; +import classes from './Stats.module.css'; + +export function StatsBlock({data}) { + const stats = data.map((stat) => ( + +
+ {stat.stats} + {stat.title} + {stat.description} +
+ )); + return
{stats}
+} \ No newline at end of file diff --git a/frontend/starters/development/app/blocks/Text/Text.module.css b/frontend/starters/development/app/blocks/Text/Text.module.css new file mode 100644 index 0000000..bf155f0 --- /dev/null +++ b/frontend/starters/development/app/blocks/Text/Text.module.css @@ -0,0 +1,43 @@ +.wrapper { + position: relative; + padding-top: rem(40px); + padding-bottom: rem(40px); + + @media (max-width: $mantine-breakpoint-sm) { + padding-top: rem(30px); + padding-bottom: rem(30px); + } + } + + .inner { + position: relative; + z-index: 1; + } + + .title { + text-align: left; + font-weight: 800; + font-size: rem(40px); + letter-spacing: -1px; + color: light-dark(var(--mantine-color-black), var(--mantine-color-white)); + margin-bottom: var(--mantine-spacing-xs); + font-family: + Greycliff CF, + var(--mantine-font-family); + + @media (max-width: $mantine-breakpoint-xs) { + font-size: rem(28px); + } + } + + .highlight { + color: light-dark(var(--mantine-color-blue-6), var(--mantine-color-blue-4)); + } + + .description { + text-align: left; + + @media (max-width: $mantine-breakpoint-xs) { + font-size: var(--mantine-font-size-md); + } + } \ No newline at end of file diff --git a/frontend/starters/development/app/blocks/Text/Text.tsx b/frontend/starters/development/app/blocks/Text/Text.tsx new file mode 100644 index 0000000..09d724e --- /dev/null +++ b/frontend/starters/development/app/blocks/Text/Text.tsx @@ -0,0 +1,17 @@ +import { Title, Text, Button, Container } from '@mantine/core'; +import classes from './Text.module.css'; + +export function TextBlock({title, text}) { + return ( + +
+ + {title} + + + {text} + +
+
+ ); +} \ No newline at end of file diff --git a/frontend/starters/development/app/layout.tsx b/frontend/starters/development/app/layout.tsx index 3b3c965..c673195 100644 --- a/frontend/starters/development/app/layout.tsx +++ b/frontend/starters/development/app/layout.tsx @@ -2,15 +2,18 @@ import { DraftAlert } from "@/components/misc/DraftAlert" import { HeaderNav } from "@/components/navigation/HeaderNav" import type { Metadata } from "next" import type { ReactNode } from "react" +import "@mantine/core/styles.css"; +import { MantineProvider, ColorSchemeScript } from "@mantine/core"; +import { theme } from "../theme"; import "@/styles/globals.css" export const metadata: Metadata = { title: { - default: "Next.js for Drupal", - template: "%s | Next.js for Drupal", + default: "Power Stack Frontend", + template: "%s | Power Stack Frontend", }, - description: "A Next.js site powered by a Drupal backend.", + description: "Power Stack Frontend", icons: { icon: "/favicon.ico", }, @@ -25,11 +28,18 @@ export default function RootLayout({ }) { return ( + + + +
-
{children}
+ {children}
diff --git a/frontend/starters/development/app/puck/[...puckPath]/client.tsx b/frontend/starters/development/app/puck/[...puckPath]/client.tsx new file mode 100644 index 0000000..46bb808 --- /dev/null +++ b/frontend/starters/development/app/puck/[...puckPath]/client.tsx @@ -0,0 +1,20 @@ +"use client"; + +import type { Data } from "@measured/puck"; +import { Puck } from "@measured/puck"; +import config from "../../../puck.config"; + +export function Client({ path, data }: { path: string; data: Data }) { + return ( + { + await fetch("/puck/api", { + method: "post", + body: JSON.stringify({ data, path }), + }); + }} + /> + ); +} diff --git a/frontend/starters/development/app/puck/[...puckPath]/page.tsx b/frontend/starters/development/app/puck/[...puckPath]/page.tsx new file mode 100644 index 0000000..1310148 --- /dev/null +++ b/frontend/starters/development/app/puck/[...puckPath]/page.tsx @@ -0,0 +1,40 @@ +/** + * This file implements a *magic* catch-all route that renders the Puck editor. + * + * This route exposes /puck/[...puckPath], but is disabled by middleware.ts. The middleware + * then rewrites all URL requests ending in `/edit` to this route, allowing you to visit any + * page in your application and add /edit to the end to spin up a Puck editor. + * + * This approach enables public pages to be statically rendered whilst the /puck route can + * remain dynamic. + * + * NB this route is public, and you will need to add authentication + */ + +import "@measured/puck/puck.css"; +import { Client } from "./client"; +import { Metadata } from "next"; +import { getPage } from "../../../lib/get-page"; + +export async function generateMetadata({ + params: { puckPath = [] }, +}: { + params: { puckPath: string[] }; +}): Promise { + const path = `/${puckPath.join("/")}`; + + return { + title: "Puck: " + path, + }; +} + +export default async function Page({ + params: { puckPath = [] }, +}: { + params: { puckPath: string[] }; +}) { + const path = `/${puckPath.join("/")}`; + const data = getPage(path); + + return ; +} diff --git a/frontend/starters/development/app/puck/api/route.ts b/frontend/starters/development/app/puck/api/route.ts new file mode 100644 index 0000000..21f424f --- /dev/null +++ b/frontend/starters/development/app/puck/api/route.ts @@ -0,0 +1,25 @@ +import { revalidatePath } from "next/cache"; +import { NextResponse } from "next/server"; +import fs from "fs"; + +export async function POST(request: Request) { + const payload = await request.json(); + + const existingData = JSON.parse( + fs.existsSync("database.json") + ? fs.readFileSync("database.json", "utf-8") + : "{}" + ); + + const updatedData = { + ...existingData, + [payload.path]: payload.data, + }; + + fs.writeFileSync("database.json", JSON.stringify(updatedData)); + + // Purge Next.js cache + revalidatePath(payload.path); + + return NextResponse.json({ status: "ok" }); +} diff --git a/frontend/starters/development/app/puck/page.tsx b/frontend/starters/development/app/puck/page.tsx new file mode 100644 index 0000000..bf62683 --- /dev/null +++ b/frontend/starters/development/app/puck/page.tsx @@ -0,0 +1 @@ +export { default, generateMetadata } from "./[...puckPath]/page"; diff --git a/frontend/starters/development/components/Footer/Footer.module.css b/frontend/starters/development/components/Footer/Footer.module.css new file mode 100644 index 0000000..77ed202 --- /dev/null +++ b/frontend/starters/development/components/Footer/Footer.module.css @@ -0,0 +1,89 @@ +.footer { + padding-top: calc(var(--mantine-spacing-xl) * 2); + background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); + border-top: rem(1px) solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5)); + } + + .logo { + max-width: rem(200px); + + @media (max-width: $mantine-breakpoint-sm) { + display: flex; + flex-direction: column; + align-items: center; + } + } + + .description { + margin-top: rem(5px); + + @media (max-width: $mantine-breakpoint-sm) { + margin-top: var(--mantine-spacing-xs); + text-align: center; + } + } + + .inner { + display: flex; + justify-content: space-between; + + @media (max-width: $mantine-breakpoint-sm) { + flex-direction: column; + align-items: center; + } + } + + .groups { + display: flex; + flex-wrap: wrap; + + @media (max-width: $mantine-breakpoint-sm) { + display: none; + } + } + + .wrapper { + width: rem(160px); + } + + .link { + display: block; + color: light-dark(var(--mantine-color-gray-6), var(--mantine-color-dark-1)); + font-size: var(--mantine-font-size-sm); + padding-top: rem(3px); + padding-bottom: rem(3px); + + &:hover { + text-decoration: underline; + } + } + + .title { + font-size: var(--mantine-font-size-lg); + font-weight: 700; + font-family: + Greycliff CF, + var(--mantine-font-family); + margin-bottom: calc(var(--mantine-spacing-xs) / 2); + color: light-dark(var(--mantine-color-black), var(--mantine-color-white)); + } + + .afterFooter { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: var(--mantine-spacing-xl); + padding-top: var(--mantine-spacing-xl); + padding-bottom: var(--mantine-spacing-xl); + border-top: rem(1px) solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-4)); + + @media (max-width: $mantine-breakpoint-sm) { + flex-direction: column; + } + } + + .social { + @media (max-width: $mantine-breakpoint-sm) { + margin-top: var(--mantine-spacing-xs); + } + } \ No newline at end of file diff --git a/frontend/starters/development/components/Footer/Footer.tsx b/frontend/starters/development/components/Footer/Footer.tsx new file mode 100644 index 0000000..19625f7 --- /dev/null +++ b/frontend/starters/development/components/Footer/Footer.tsx @@ -0,0 +1,89 @@ +import { Text, Container, ActionIcon, Group, rem } from '@mantine/core'; +import { IconBrandTwitter, IconBrandYoutube, IconBrandInstagram } from '@tabler/icons-react'; +import classes from './Footer.module.css'; +import Image from 'next/image'; +import logo from '../../images/logo-nav-light.svg' + +const data = [ + { + title: 'About', + links: [ + { label: 'Features', link: '#' }, + { label: 'Pricing', link: '#' }, + { label: 'Support', link: '#' }, + { label: 'Forums', link: '#' }, + ], + }, + { + title: 'Project', + links: [ + { label: 'Contribute', link: '#' }, + { label: 'Media assets', link: '#' }, + { label: 'Changelog', link: '#' }, + { label: 'Releases', link: '#' }, + ], + }, + { + title: 'Community', + links: [ + { label: 'Join Discord', link: '#' }, + { label: 'Follow on Twitter', link: '#' }, + { label: 'Email newsletter', link: '#' }, + { label: 'GitHub discussions', link: '#' }, + ], + }, +]; + +export function FooterLinks() { + const groups = data.map((group) => { + const links = group.links.map((link, index) => ( + + key={index} + className={classes.link} + component="a" + href={link.link} + onClick={(event) => event.preventDefault()} + > + {link.label} + + )); + + return ( +
+ {group.title} + {links} +
+ ); + }); + + return ( +
+ +
+ Logo + + Build fully functional accessible web applications faster than ever + +
+
{groups}
+
+ + + ©{new Date().getFullYear()} powerstack.dev. All rights reserved. + + + + + + + + + + + + + + +
+ ); +} \ No newline at end of file diff --git a/frontend/starters/development/components/Header/Header.module.css b/frontend/starters/development/components/Header/Header.module.css new file mode 100644 index 0000000..1fc3a72 --- /dev/null +++ b/frontend/starters/development/components/Header/Header.module.css @@ -0,0 +1,31 @@ +.header { + height: rem(72px); + background-color: var(--mantine-color-body); + border-bottom: rem(1px) solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4)); + } + + .inner { + height: rem(72px); + display: flex; + justify-content: space-between; + align-items: center; + } + + .link { + display: block; + line-height: 1; + padding: rem(8px) rem(12px); + border-radius: var(--mantine-radius-sm); + text-decoration: none; + color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0)); + font-size: var(--mantine-font-size-sm); + font-weight: 500; + + @mixin hover { + background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6)); + } + } + + .linkLabel { + margin-right: rem(5px); + } \ No newline at end of file diff --git a/frontend/starters/development/components/Header/Header.tsx b/frontend/starters/development/components/Header/Header.tsx new file mode 100644 index 0000000..cb22244 --- /dev/null +++ b/frontend/starters/development/components/Header/Header.tsx @@ -0,0 +1,86 @@ +import { Menu, Group, Center, Burger, Container } from '@mantine/core'; +import { useDisclosure } from '@mantine/hooks'; +import { IconChevronDown } from '@tabler/icons-react'; +import classes from './Header.module.css' +import Image from 'next/image'; +import logo from '../../images/logo-nav-light.svg' + +const links = [ + { link: '/about', label: 'Features' }, + { + link: '#1', + label: 'Learn', + links: [ + { link: '/docs', label: 'Documentation' }, + { link: '/resources', label: 'Resources' }, + { link: '/community', label: 'Community' }, + { link: '/blog', label: 'Blog' }, + ], + }, + { link: '/about', label: 'About' }, + { link: '/pricing', label: 'Pricing' }, + { + link: '#2', + label: 'Support', + links: [ + { link: '/faq', label: 'FAQ' }, + { link: '/demo', label: 'Book a demo' }, + { link: '/forums', label: 'Forums' }, + ], + }, +]; + +export function HeaderMenu() { + const [opened, { toggle }] = useDisclosure(false); + + const items = links.map((link) => { + const menuItems = link.links?.map((item) => ( + {item.label} + )); + + if (menuItems) { + return ( + + + event.preventDefault()} + > +
+ {link.label} + +
+
+
+ {menuItems} +
+ ); + } + + return ( + event.preventDefault()} + > + {link.label} + + ); + }); + + return ( +
+ +
+ Logo + + {items} + + +
+
+
+ ); +} \ No newline at end of file diff --git a/frontend/starters/development/components/drupal/BasicPage.tsx b/frontend/starters/development/components/drupal/BasicPage.tsx index 9438e69..9c4dae1 100644 --- a/frontend/starters/development/components/drupal/BasicPage.tsx +++ b/frontend/starters/development/components/drupal/BasicPage.tsx @@ -13,7 +13,7 @@ export function BasicPage({ node, ...props }: BasicPageProps) {

{node.title}

- {node?.field_page_builder[0].field_section_fields[0].field_column_fields[0].field_text.processed && ( + {node?.field_page_builder[0]?.field_section_fields[0].field_column_fields[0].field_text.processed && (
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/starters/development/lib/get-page.ts b/frontend/starters/development/lib/get-page.ts new file mode 100644 index 0000000..43b3ce6 --- /dev/null +++ b/frontend/starters/development/lib/get-page.ts @@ -0,0 +1,11 @@ +import { Data } from "@measured/puck"; +import fs from "fs"; + +// Replace with call to your database +export const getPage = (path: string) => { + const allData: Record | null = fs.existsSync("database.json") + ? JSON.parse(fs.readFileSync("database.json", "utf-8")) + : null; + + return allData ? allData[path] : null; +}; diff --git a/frontend/starters/development/middleware.ts b/frontend/starters/development/middleware.ts new file mode 100644 index 0000000..b1df834 --- /dev/null +++ b/frontend/starters/development/middleware.ts @@ -0,0 +1,27 @@ +import { NextResponse } from "next/server"; + +import type { NextRequest } from "next/server"; + +export async function middleware(req: NextRequest) { + const res = NextResponse.next(); + + if (req.method === "GET") { + // Rewrite routes that match "/[...puckPath]/edit" to "/puck/[...puckPath]" + if (req.nextUrl.pathname.endsWith("/edit")) { + const pathWithoutEdit = req.nextUrl.pathname.slice( + 0, + req.nextUrl.pathname.length - 5 + ); + const pathWithEditPrefix = `/puck${pathWithoutEdit}`; + + return NextResponse.rewrite(new URL(pathWithEditPrefix, req.url)); + } + + // Disable "/puck/[...puckPath]" + if (req.nextUrl.pathname.startsWith("/puck")) { + return NextResponse.redirect(new URL("/", req.url)); + } + } + + return res; +} diff --git a/frontend/starters/development/package.json b/frontend/starters/development/package.json index dac0a4b..df15355 100644 --- a/frontend/starters/development/package.json +++ b/frontend/starters/development/package.json @@ -16,10 +16,15 @@ "next": "^14", "next-drupal": "workspace:^", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "@mantine/core": "^7.7.1", + "@mantine/hooks": "^7.7.1", + "@measured/puck": "*", + "@tabler/icons-react": "^3.1.0", + "classnames": "^2.3.2", + "postcss-preset-mantine": "^1.13.0" }, "devDependencies": { - "@tailwindcss/typography": "^0.5.10", "@types/node": "^20.10.0", "@types/react": "^18.2.39", "@types/react-dom": "^18.2.17", @@ -28,7 +33,6 @@ "eslint-config-next": "^14.0.3", "postcss": "^8.4.31", "prettier": "^3.1.0", - "tailwindcss": "^3.3.5", "typescript": "^5.3.2" } } diff --git a/frontend/starters/development/postcss.config.cjs b/frontend/starters/development/postcss.config.cjs new file mode 100644 index 0000000..e817f56 --- /dev/null +++ b/frontend/starters/development/postcss.config.cjs @@ -0,0 +1,14 @@ +module.exports = { + plugins: { + "postcss-preset-mantine": {}, + "postcss-simple-vars": { + variables: { + "mantine-breakpoint-xs": "36em", + "mantine-breakpoint-sm": "48em", + "mantine-breakpoint-md": "62em", + "mantine-breakpoint-lg": "75em", + "mantine-breakpoint-xl": "88em", + }, + }, + }, +}; diff --git a/frontend/starters/development/postcss.config.js b/frontend/starters/development/postcss.config.js deleted file mode 100644 index 3fa0a95..0000000 --- a/frontend/starters/development/postcss.config.js +++ /dev/null @@ -1,8 +0,0 @@ -// If you want to use other PostCSS plugins, see the following: -// https://tailwindcss.com/docs/using-with-preprocessors -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} diff --git a/frontend/starters/development/puck.config.tsx b/frontend/starters/development/puck.config.tsx new file mode 100644 index 0000000..bb4f280 --- /dev/null +++ b/frontend/starters/development/puck.config.tsx @@ -0,0 +1,88 @@ +import type { Config } from "@measured/puck"; +import { HeaderMenu } from "./components/Header/Header"; +import { FooterLinks } from "./components/Footer/Footer"; +import { HeroBlock } from "./app/blocks/Hero/Hero"; +import { TextBlock } from "./app/blocks/Text/Text"; +import { StatsBlock } from "./app/blocks/Stats/Stats"; + +type Props = { + HeadingBlock: { title: string }; +}; + +export const config: Config = { + components: { + Hero: { + fields: { + title: { type: "text" }, + subtitle: { type: "text" }, + description: {type: "textarea"} + }, + defaultProps: { + title: "Heading", + }, + render: ({ title, subtitle, description }) => ( + + ), + }, + Text: { + fields: { + title: { type: "text" }, + text: { type: "textarea" } + }, + defaultProps: { + title: "Heading", + text: "Enter text here", + }, + render: ({ title, text }) => ( + + ), + }, + Stats: { + fields: { + stats: { + type: "array", + arrayFields: { + stats: { type: "text" }, + title: { type: "text" }, + description: {type: "textarea"} + } + }, + }, + defaultProps: { + stats: [ + { + title: 'Page views', + stats: '456,133', + description: '24% more than in the same month last year, 33% more that two years ago', + }, + { + title: 'New users', + stats: '2,175', + description: '13% less compared to last month, new user engagement up by 6%', + }, + { + title: 'Completed orders', + stats: '1,994', + description: '1994 orders were completed this month, 97% satisfaction rate', + }, + ] + }, + render: ({ stats }) => ( + + ), + }, + }, + root: { + render: ({ children }) => { + return ( + <> + + {children} + + + ); + }, + }, +}; + +export default config; diff --git a/frontend/starters/development/tailwind.config.ts b/frontend/starters/development/tailwind.config.ts deleted file mode 100644 index c7f5c8a..0000000 --- a/frontend/starters/development/tailwind.config.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Config } from "tailwindcss" - -const config: Config = { - content: [ - "./pages/**/*.{js,ts,jsx,tsx,mdx}", - "./components/**/*.{js,ts,jsx,tsx,mdx}", - "./app/**/*.{js,ts,jsx,tsx,mdx}", - ], - theme: { - extend: {}, - }, - variants: { - extend: {}, - }, - plugins: [require("@tailwindcss/typography")], -} - -export default config diff --git a/frontend/starters/development/theme.ts b/frontend/starters/development/theme.ts new file mode 100644 index 0000000..66965bc --- /dev/null +++ b/frontend/starters/development/theme.ts @@ -0,0 +1,21 @@ +"use client"; +import { createTheme, MantineColorsTuple } from '@mantine/core'; + +const myColor: MantineColorsTuple = [ + "#fff8e1", + "#ffefcc", + "#ffdd9b", + "#ffca64", + "#ffba38", + "#ffb01b", + "#ffab09", + "#e39500", + "#ca8500", + "#af7100" +] + +const theme = createTheme({ + colors: { + myColor, + } +}); \ No newline at end of file diff --git a/scripts/misc/add_backend_creds_to_env_file.sh b/scripts/misc/add_backend_creds_to_env_file.sh deleted file mode 100755 index 4ed400c..0000000 --- a/scripts/misc/add_backend_creds_to_env_file.sh +++ /dev/null @@ -1,6 +0,0 @@ -#! /bin/bash -. ./scripts/misc/globals.sh - -cd "${PROJECT_ROOT}/backend/starters/${BACKEND_STARTER}" - -update_env_file "DRUPAL_CLIENT_ID" $(vendor/bin/drush eval "echo \Drupal::entityTypeManager()->getStorage('consumer')->load(2)->uuid->value;") \ No newline at end of file diff --git a/scripts/setup/init.sh b/scripts/setup/init.sh index 89199a0..5709c18 100644 --- a/scripts/setup/init.sh +++ b/scripts/setup/init.sh @@ -61,7 +61,6 @@ fin exec "/var/www/scripts/setup/backend/install_cms.sh" # Configuring frontend to link to newly provisioned backend environment title "STEP 5" "Linking environments" -fin exec "/scripts/misc/add_backend_creds_to_env_file.sh" fin project restart frontend # All done! show the urls. diff --git a/scripts/setup/init_env_file.sh b/scripts/setup/init_env_file.sh index 5aaa27b..5b238a6 100755 --- a/scripts/setup/init_env_file.sh +++ b/scripts/setup/init_env_file.sh @@ -10,4 +10,5 @@ update_env_file "NEXT_IMAGE_DOMAIN" "http://backend.${VIRTUAL_HOST}" update_env_file "NEXT_HOST" "http://frontend.${VIRTUAL_HOST}" update_env_file "DRUPAL_CLIENT_SECRET" "${secret}" update_env_file "DRUPAL_PREVIEW_SECRET" "${prewview_secret}" -update_env_file "NEXT_REVALIDATE_SECRET" "${revalidate_secret}" \ No newline at end of file +update_env_file "NEXT_REVALIDATE_SECRET" "${revalidate_secret}" +update_env_file "DRUPAL_CLIENT_ID" "next" \ No newline at end of file