diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..bffb357 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/.gitignore b/.gitignore index 25ff9cf..6638176 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,11 @@ *.log .DS_Store .env +.env.* /.cache /public/build /build /.netlify -/.vscode \ No newline at end of file +/.vscode +/.next +/.idea \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/.prettierrc @@ -0,0 +1 @@ +{} diff --git a/.sanity/runtime/app.js b/.sanity/runtime/app.js new file mode 100644 index 0000000..1ec2bc2 --- /dev/null +++ b/.sanity/runtime/app.js @@ -0,0 +1,11 @@ + +// This file is auto-generated on 'sanity dev' +// Modifications to this file is automatically discarded +import {renderStudio} from "sanity" +import studioConfig from "../../sanity.config.ts" + +renderStudio( + document.getElementById("sanity"), + studioConfig, + {reactStrictMode: false, basePath: "/"} +) diff --git a/.sanity/runtime/index.html b/.sanity/runtime/index.html new file mode 100644 index 0000000..65584ba --- /dev/null +++ b/.sanity/runtime/index.html @@ -0,0 +1,203 @@ + + +Sanity Studio
\ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 1478334..0000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,19 +0,0 @@ -##Principles - -React Denver is dedicated to providing a harassment-free experience for everyone regardless of gender, gender identity and expression, sexual orientation, disability, physical appearance, body size, race, or religion. We do not tolerate harassment of React Denver participants in any form. Sexual language and imagery is not appropriate for any venue, including talks, workshops, parties, Twitter and other online media. - -Harassment includes offensive verbal comments related to gender, gender identity and expression, sexual orientation, disability, physical appearance, body size, race, religion, sexual images in public spaces, deliberate intimidation, stalking, following, harassing photography or recording, sustained disruption of talks or other events, inappropriate physical contact, and unwelcome sexual attention. - -Participants asked to stop any harassing behavior are expected to comply immediately. - -If a participant engages in harassing behavior, the Denver React organizers may take any action they deem appropriate, including warning the offender or expulsion from React Denver. If you are being harassed, notice that someone else is being harassed, or have any other concerns, please contact a member of React Denver staff immediately. - -##Events - -React Denver staff will be happy to help participants contact venue security or local law enforcement, provide escorts, or otherwise assist those experiencing harassment. - -##Online - -In addition to the principles outlined above, participants are also asked to refrain from recruiting through React Denver communications. Because demand for developers greatly outstrips supply, allowing recruiting through React Denver would reduce the quality of the community and its discussions. - --The React Denver Team diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..c1b9cfb --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,129 @@ +# EULA (End User License Agreement) + +## Personal License + +Web3Templates grants you an on-going, non-exclusive license to use this Template. + +The license grants permission to **one individual** (the Licensee) to access and use this Template. + +You **can**: + +- Use this Template to create unlimited End Products. +- Modify this Template to create derivative components and templates. Those components and templates are subject to this license. +- Use this Template to create unlimited End Products for unlimited Clients. +- Use this Template to create End Products where the End Product is sold to End Users. +- Use this Template to create End Products that are open source and freely available to End Users. + +You **cannot**: + +- Use this Template to create End Products that are designed to allow an End User to build their own End Products using this Template or derivatives of this Template. +- Re-distribute this Template or derivatives of this Template separately from an End Product, neither in code or as design assets. +- Share your access to this Template with any other individuals. +- Use this Template to produce anything that may be deemed by Web3Templates, in their sole and absolute discretion, to be competitive or in conflict with the business of Web3Templates. + +### Example usage + +Examples of usage **allowed** by the license: + +- Creating a personal website by yourself. +- Creating a website or web application for a client that will be owned by that client. +- Creating a commercial SaaS application (like an invoicing app for example) where end users have to pay a fee to use the application. +- Creating a commercial self-hosted web application that is sold to end users for a one-time fee. +- Creating a web application where the primary purpose is clearly not to simply re-distribute the template that is free and open source, where the source code is publicly available. + +Examples of usage **not allowed** by the license: + +- Creating a repository of your favorite Web3Templates components or templates (or derivatives based on Web3Templates components or templates) and publishing it publicly. +- Creating a React or Vue version of Web3Templates and making it available either for sale or for free. +- Create a Figma or Sketch UI kit based on the Web3Templates component designs. +- Creating a "website builder" project where end users can build their own websites using components or templates included with or derived from Web3Templates. +- Creating a theme, template, or project starter kit using the components or templates and making it available either for sale or for free. +- Creating an admin panel tool that is made available either for sale or for free. + +In simple terms, use Web3Templates for anything you like as long as it doesn't compete with Web3Templates. + +### Personal License Definitions + +Licensee is the individual who has purchased a Personal License. + +Components and Templates are the source code and design assets made available to the Licensee after purchasing a Web3Templates license. + +End Product is any artifact produced that incorporates the Components or Templates or derivatives of the Components or Templates. + +End User is a user of an End Product. + +Client is an individual or entity receiving custom professional services directly from the Licensee, produced specifically for that individual or entity. Customers of software-as-a-service products are not considered clients for the purpose of this document. + +## Team License + +Web3Templates grants you an on-going, non-exclusive license to use this Template. + +The license grants permission for **up to 25 Employees and Contractors of the Licensee** to access and use this Template. + +You **can**: + +- Use this Template to create unlimited End Products. +- Modify this Template to create derivative components and templates. Those components and templates are subject to this license. +- Use this Template to create unlimited End Products for unlimited Clients. +- Use this Template to create End Products where the End Product is sold to End Users. +- Use this Template to create End Products that are open source and freely available to End Users. + +You **cannot**: + +- Use the Components or Templates to create End Products that are designed to allow an End User to build their own End Products using the Components or Templates or derivatives of the Components or Templates. +- Re-distribute the Components or Templates or derivatives of the Components or Templates separately from an End Product. +- Use the Components or Templates to create End Products that are the property of any individual or entity other than the Licensee or Clients of the Licensee. +- Use the Components or Templates to produce anything that may be deemed by Web3Templates, in their sole and absolute discretion, to be competitive or in conflict with the business of Web3Templates. + +### Example usage + +Examples of usage **allowed** by the license: + +- Creating a website for your company +- Creating a website or web application for a client that will be owned by that client +- Creating a commercial SaaS application (like an invoicing app for example) where end users have to pay a fee to use the application +- Creating a commercial self-hosted web application that is sold to end users for a one-time fee +- Creating a web application where the primary purpose is clearly not to simply re-distribute the components or templates (like a conference organization app that uses the components or a template for its UI for example) that is free and open source, where the source code is publicly available + +Examples of use **not allowed** by the license: + +- Creating a repository of your favorite Web3Templates components or template (or derivatives based on Web3Templates components or templates) and publishing it publicly +- Creating a React or Vue version of Web3Templates and making it available either for sale or for free +- Creating a "website builder" project where end users can build their own websites using components or templates included with or derived from Web3Templates +- Creating a theme or template using the components or templates and making it available either for sale or for free +- Creating an admin panel tool that is made available either for sale or for free +- Creating any End Product that is not the sole property of either your company or a client of your company. For example your employees/contractors can't use your company Web3Templates license to build their own websites or side projects. + +### Team License Definitions + +Licensee is the business entity who has purchased a Team License. + +Components and Templates are the source code and design assets made available to the Licensee after purchasing a Web3Templates license. + +End Product is any artifact produced that incorporates the Components or Templates or derivatives of the Components or Templates. + +End User is a user of an End Product. + +Employee is a full-time or part-time employee of the Licensee. + +Contractor is an individual or business entity contracted to perform services for the Licensee. + +Client is an individual or entity receiving custom professional services directly from the Licensee, produced specifically for that individual or entity. Customers of software-as-a-service products are not considered clients for the purpose of this document. + +## Enforcement + +If you are found to be in violation of the license, access to your Web3Templates account will be terminated, and a refund may be issued at our discretion. When license violation is blatant and malicious (such as intentionally redistributing the Components or Templates through private warez channels), no refund will be issued. + +The copyright of this Template is owned by You are granted only the permissions described in this license; all other rights are reserved. reserves the right to pursue legal remedies for any unauthorized use of the Components or Templates outside the scope of this license. + +## Liability + +Web3Templates.’s liability to you for costs, damages, or other losses arising from your use of the Components or Templates — including third-party claims against you — is limited to a refund of your license fee. may not be held liable for any consequential damages related to your use of the Components or Templates. + +This Agreement is governed by the laws of the Province of Ontario and the applicable laws of Canada. Legal proceedings related to this Agreement may only be brought in the courts of Ontario. You agree to service of process at the e-mail address on your original order. + +## Questions? + +Unsure which license you need, or unsure if your use case is covered by our licenses? + +Email us at [hello@web3templates.com](mailto:hello@web3templates.com) with your questions. diff --git a/README.md b/README.md index 4f88e14..3a4ab5c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,97 @@ -[![Netlify Status](https://api.netlify.com/api/v1/badges/04f740a0-2e14-413b-a402-be395d6fa786/deploy-status)](https://app.netlify.com/sites/react-denver/deploys) +# Stablo Pro - Next.js & Sanity CMS Blog Template -# React Denver 🥳 +Thank you for purchasing Stablo Pro. The advanced Pro version of Stablo Blog Template. Stablo is a JAMStack Blog Template built with Next.js, Tailwind CSS & Sanity CMS by [Web3Templates](https://web3templates.com/). -All contributions welcome. +**[Click here to see live demo →](https://stablo-pro.web3templates.com/)** -I'm really bad at docs :(. So please reach out to us on [Discord](https://discord.gg/fFngBEwNmV) and we will be happy to get you going. +#### Template Preview +[![Next.js Front-end Preview](https://user-images.githubusercontent.com/1884712/169838344-e32b7426-621a-45a4-aba8-afedf3377e1f.jpeg)](https://stablo-template.vercel.app/) + +#### Sanity CMS Preview + +[![Backend Sanity CMS Preview](https://user-images.githubusercontent.com/1884712/170030678-c6e32d47-0b92-42b7-ac2d-f3cf800c0969.png)](https://stablo-template.vercel.app/studio) + +# Installation + +Follow the installation guide to setup the stablo template. + +## Step 1. Clone the Repo + +Unzip the downloaded zip or clone the github repo to your local machine. Then open the project fodler in your favorite code editor. We prefer VSCode. + +## Step 2. Setup `.env` Variables. + +Open the project folder and rename `.env.local.example` placed in the root folder into `.env.local` and add your sanity project ID. You can create a new project by visiting this link: https://www.sanity.io/get-started/create-project + +If you already have a project, copy the project ID from https://sanity.io/manage + +``` +NEXT_PUBLIC_SANITY_PROJECT_ID=xxyyzz +``` + +## Step 3. Allow CORS Origins + +To make the studio work properly, you must add CORS origin in Sanity. Visit `https://www.sanity.io/manage/personal/project//api` in your browser to add CORS origin. + +Click `Add CORS origin` button and enter the URL as `http://localhost:3000` and check the Allow credentials checkbox. + +## Step 4: Import Demo Data (Optional) + +To look like what you have seen in the demo, with all the content and images, follow the below steps: + +1. if you have not installed `@sanity/cli` install it globally first. + +```bash +npm install -g @sanity/cli +# or +pnpm install -g @sanity/cli +``` + +Then login to sanity using `sanity login` command + +``` +sanity login +``` + +Now, you will be able to import demo content by running the `sanity-import` command. The files are located at `/lib/sanity/data/production.tar.gz` and will load automatically by running the below command. + +```bash +npm run sanity-import +# or +pnpm sanity-import +``` + +## Step 5: Finish it up! + +Now, run your project using the below command. + +```bash +npm run dev +# or +pnpm dev +``` + +Now your project should be up and the Next.js frontend will be running on http://localhost:3000. + +Sanity Studio can be accessed using http://localhost:3000/studio or you can run it on a separate https://localhost:3333 server using the following command. + +```bash +npm run sanity +# or +pnpm sanity +``` + +## Step 6. Deploy Changes + +Once all of the above changes is made, make sure to redeploy to vercel once again to see all of your changes in production. + +You can `git push` the changes and it should automatically trigger a new deployment. If not, you can also deploy to vercel using the following command. + +``` +npx vercel --prod +``` + +## Help and Support + +If you need support or help, please contact us via https://web3templates.com/support. diff --git a/app/(sanity)/layout.tsx b/app/(sanity)/layout.tsx new file mode 100644 index 0000000..a14e64f --- /dev/null +++ b/app/(sanity)/layout.tsx @@ -0,0 +1,16 @@ +export const metadata = { + title: 'Next.js', + description: 'Generated by Next.js', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} diff --git a/app/(website)/archive/archive.js b/app/(website)/archive/archive.js new file mode 100644 index 0000000..503b2c1 --- /dev/null +++ b/app/(website)/archive/archive.js @@ -0,0 +1,234 @@ +"use client"; + +import Container from "@/components/container"; +import { useRouter, useSearchParams } from "next/navigation"; +import { paginatedeventsquery } from "@/lib/sanity/groq"; +import EventList from "@/components/eventlist"; +import useSWR, { SWRConfig } from "swr"; +import { + ChevronLeftIcon, + ChevronRightIcon +} from "@heroicons/react/24/outline"; +import { useState, useEffect } from "react"; +import { fetcher } from "@/lib/sanity/client"; + +export default function Event({ events: initialevents }) { + const router = useRouter(); + const searchParams = useSearchParams(); + const page = searchParams.get("page"); + const pageIndex = parseInt(page) || 1; + + const EVENTS_PER_PAGE = 6; + + const [isLoading, setIsLoading] = useState(false); + const [isFirstPage, setIsFirstPage] = useState(false); + const [isLastPage, setIsLastPage] = useState(false); + + // [(($pageIndex - 1) * 10)...$pageIndex * 10]{ + const params = { + pageIndex: (pageIndex - 1) * EVENTS_PER_PAGE, + limit: pageIndex * EVENTS_PER_PAGE + }; + + // const fetcher = (query, params) => + // client && client.fetch(query, params); + + const { + data: events, + error, + isValidating + } = useSWR([paginatedeventsquery, params], fetcher, { + fallbackData: initialevents, + onSuccess: () => { + setIsLoading(false); + } + }); + + useEffect(() => { + setIsFirstPage(pageIndex < 2); + }, [pageIndex]); + + useEffect(() => { + setIsLastPage(events.length < EVENTS_PER_PAGE); + }, [events]); + + const handleNextPage = () => { + router.push(`/archive?page=${pageIndex + 1}`); + }; + + const handlePrevPage = () => { + router.push(`/archive?page=${pageIndex - 1}`); + }; + + return ( + <> + +

+ Archive +

+
+

+ See all talks we have ever written. +

+
+ {events && events?.length === 0 && ( +
+ + End of the result! + +
+ )} + + {isValidating && ( +
+ {new Array(6).fill().map((item, index) => ( +
+ +
+ ))} +
+ )} + {events && !isLoading && !isValidating && ( +
+ {events.map(event => ( + + ))} +
+ )} +
+ +
+
+ + ); +} + +const SkeletonImg = () => { + const style = ` + .dark svg#skeleton #colorbase { + stop-color: #2d2d2d; + } + .dark svg#skeleton #colorhighlight { + stop-color: #3d3d3d; + } +`; + return ( + + Loading... + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/app/(website)/archive/page.js b/app/(website)/archive/page.js new file mode 100644 index 0000000..6bafd24 --- /dev/null +++ b/app/(website)/archive/page.js @@ -0,0 +1,17 @@ +import { Suspense } from "react"; +import Archive from "./archive"; + +import { getPaginatedEvents } from "@/lib/sanity/client"; + +const EVENTS_PER_PAGE = 6; + +export default async function ArchivePage() { + const events = await getPaginatedEvents(EVENTS_PER_PAGE); + return ( + + + + ); +} + +export const revalidate = 60; diff --git a/app/(website)/code-of-conduct/code-of-conduct.js b/app/(website)/code-of-conduct/code-of-conduct.js new file mode 100644 index 0000000..e3dd7f2 --- /dev/null +++ b/app/(website)/code-of-conduct/code-of-conduct.js @@ -0,0 +1,53 @@ +import Container from "@/components/container"; + +export default function CodeofConduct() { + return ( + +

+ Principles +

+
+

+ React Denver is dedicated to providing a harassment-free experience + for everyone regardless of gender, gender identity and expression, + sexual orientation, disability, physical appearance, body size, race, + or religion. We do not tolerate harassment of React Denver + participants in any form. Sexual language and imagery is not + appropriate for any venue, including talks, workshops, parties, + Twitter and other online media. +

+

+ Harassment includes offensive verbal comments related to gender, + gender identity and expression, sexual orientation, disability, + physical appearance, body size, race, religion, sexual images in + public spaces, deliberate intimidation, stalking, following, harassing + photography or recording, sustained disruption of talks or other + events, inappropriate physical contact, and unwelcome sexual + attention. +

+

+ Participants asked to stop any harassing behavior are expected to + comply immediately. +

+

+ If a participant engages in harassing behavior, the Denver React + organizers may take any action they deem appropriate, including + warning the offender or expulsion from React Denver. If you are being + harassed, notice that someone else is being harassed, or have any + other concerns, please contact a member of React Denver staff + immediately. +

+
+

+ Events +

+
+

+ React Denver staff will be happy to help participants contact venue + security or local law enforcement, provide escorts, or otherwise + assist those experiencing harassment. +

+
+
+ ); +} diff --git a/app/(website)/code-of-conduct/page.js b/app/(website)/code-of-conduct/page.js new file mode 100644 index 0000000..05677a7 --- /dev/null +++ b/app/(website)/code-of-conduct/page.js @@ -0,0 +1,5 @@ +import CodeofConduct from "./code-of-conduct"; + +export default async function CodeofConductPage() { + return ; +} diff --git a/app/(website)/event/[slug]/default.js b/app/(website)/event/[slug]/default.js new file mode 100644 index 0000000..25c4dfa --- /dev/null +++ b/app/(website)/event/[slug]/default.js @@ -0,0 +1,112 @@ +import Image from "next/image"; +import Link from "next/link"; +import Container from "@/components/container"; +import EventSignup from "@/components/event-signup"; +import { notFound } from "next/navigation"; +import { PortableText } from "@/lib/sanity/plugins/portabletext"; +import { urlForImage } from "@/lib/sanity/image"; +import SpeakerCard from "@/components/blog/speakerCard"; +import { formatWithOffset } from "@/utils/all"; +import { MapIcon } from "@heroicons/react/24/outline"; + +export default function Event(props) { + const { loading, event, nextEventData } = props; + + const slug = event?.slug; + + if (!loading && !slug) { + notFound(); + } + + const imageProps = event?.mainImage ? urlForImage(event?.mainImage) : null; + + const SpeakerimageProps = event?.speaker?.image + ? urlForImage(event.speaker.image) + : null; + + return ( + <> + +
+

+ {event.title} +

+ +
+
+
+ {SpeakerimageProps && ( + + {event?.speaker?.name} + + )} +
+
+

+ + {event.speaker.name} + +

+
+ +
+
+
+
+ {event.host ? ( +
+ +
+ ) : null} +
+
+ +
+ {imageProps && ( + {event.mainImage?.alt + )} +
+ + +
+ +
+ {event.body && } +
+
+ + ← View all talks + +
+ {event.speaker && } +
+
+ + ); +} diff --git a/app/(website)/event/[slug]/opengraph-todo.js b/app/(website)/event/[slug]/opengraph-todo.js new file mode 100644 index 0000000..9ca2ad6 --- /dev/null +++ b/app/(website)/event/[slug]/opengraph-todo.js @@ -0,0 +1,39 @@ +import { ImageResponse } from "@vercel/og"; +import { getEventBySlug } from "@/lib/sanity/client"; +import OgImage from "@/components/ogimage"; + +// const InterRegular = fetch( +// new URL("../../../../public/fonts/Inter.ttf", import.meta.url) +// ).then(res => res.arrayBuffer()); + +const InterBold = fetch( + new URL("../../../../public/fonts/Inter-Bold.otf", import.meta.url) +).then(res => res.arrayBuffer()); + +export default async function handler({ params }) { + const event = await getEventBySlug(params.slug); + + const fontData = await InterBold; + // const [interRegularFont, interBoldFont] = await Promise.all([ + // InterRegular, + // InterBold + // ]); + + return new ImageResponse(, { + width: 1200, + height: 630, + fonts: [ + { + name: "Inter", + data: fontData, + style: "normal" + } + // { + // name: "Inter", + // data: interBoldFont, + // style: "normal", + // weight: 700 + // } + ] + }); +} diff --git a/app/(website)/event/[slug]/page.js b/app/(website)/event/[slug]/page.js new file mode 100644 index 0000000..e3ea96c --- /dev/null +++ b/app/(website)/event/[slug]/page.js @@ -0,0 +1,44 @@ +import EventPage from "./default"; +import { getAllEventSlugs, getEventBySlug } from "@/lib/sanity/client"; +import { urlForImage } from "@/lib/sanity/image"; +import checkTitoSlug from "@/app/api/tito-check-slug"; + +export async function generateStaticParams() { + return await getAllEventSlugs(); +} + +export async function generateMetadata({ params }) { + const event = await getEventBySlug(params.slug); + return { + title: event.title, + keywords: ["React Denver", "React Meetup Denver", event.title], + authors: [{ name: event.speaker.name }], + openGraph: { + title: event.title, + description: event.excerpt, + images: [ + { + url: urlForImage(event?.mainImage)?.src || "/img/opengraph.jpg", + width: 800, + height: 600, + }, + ], + }, + twitter: { + title: event?.title || "React Denver", + card: "summary_large_image", + }, + robots: { + index: true, + follow: true, + }, + }; +} + +export default async function EventDefault({ params }) { + const event = await getEventBySlug(params.slug); + const nextEventData = await checkTitoSlug(event.titoSlug); + return ; +} + +export const revalidate = 60; diff --git a/app/(website)/event/lifestyle/[slug]/lifestyle.js b/app/(website)/event/lifestyle/[slug]/lifestyle.js new file mode 100644 index 0000000..4cba1bc --- /dev/null +++ b/app/(website)/event/lifestyle/[slug]/lifestyle.js @@ -0,0 +1,116 @@ +import Image from "next/image"; +import Link from "next/link"; +import Container from "@/components/container"; +import { notFound } from "next/navigation"; +import { PortableText } from "@/lib/sanity/plugins/portabletext"; +import { urlForImage } from "@/lib/sanity/image"; +import SpeakerCard from "@/components/blog/speakerCard"; +import EventSignup from "@/components/event-signup"; +import { formatWithOffset } from "@/utils/all"; +import { MapIcon } from "@heroicons/react/24/outline"; + +export default function Event(props) { + const { loading, event, nextEventData } = props; + + const slug = event?.slug; + + if (!loading && !slug) { + notFound(); + } + + const imageProps = event?.mainImage ? urlForImage(event?.mainImage) : null; + + const SpeakerimageProps = event?.speaker?.image + ? urlForImage(event.speaker.image) + : null; + + return ( + <> +
+ {imageProps && ( +
+ {event.mainImage?.alt +
+ )} + +
+

+ {event.title} +

+ +
+
+
+
+ {SpeakerimageProps && ( + + {event?.speaker?.name} + + )} +
+

+ + {event.speaker.name} + {" "} + · +

+
+ +
+
+ +
+
+
+
+ {event.host ? ( + + ) : null} +
+
+ + +
+ +
+ {event.body && } +
+
+ + ← View all talks + +
+ {event.speaker && } +
+
+ + ); +} diff --git a/app/(website)/event/lifestyle/[slug]/page.js b/app/(website)/event/lifestyle/[slug]/page.js new file mode 100644 index 0000000..efda0ea --- /dev/null +++ b/app/(website)/event/lifestyle/[slug]/page.js @@ -0,0 +1,45 @@ +import EventPage from "./lifestyle"; + +import { getAllEventSlugs, getEventBySlug } from "@/lib/sanity/client"; +import { urlForImage } from "@/lib/sanity/image"; +import checkTitoSlug from "@/app/api/tito-check-slug"; + +export async function generateStaticParams() { + return await getAllEventSlugs(); +} + +export async function generateMetadata({ params }) { + const event = await getEventBySlug(params.slug); + return { + title: event.title, + keywords: ["React Denver", "React Meetup Denver", event.title], + authors: [{ name: event.speaker.name }], + openGraph: { + title: event.title, + description: event.excerpt, + images: [ + { + url: urlForImage(event?.mainImage)?.src || "/img/opengraph.jpg", + width: 800, + height: 600, + }, + ], + }, + twitter: { + title: event?.title || "React Denver", + card: "summary_large_image", + }, + robots: { + index: true, + follow: true, + }, + }; +} + +export default async function EventDefault({ params }) { + const event = await getEventBySlug(params.slug); + const nextEventData = await checkTitoSlug(event.titoSlug); + return ; +} + +export const revalidate = 60; diff --git a/app/(website)/event/minimal/[slug]/minimal.js b/app/(website)/event/minimal/[slug]/minimal.js new file mode 100644 index 0000000..b5eec7b --- /dev/null +++ b/app/(website)/event/minimal/[slug]/minimal.js @@ -0,0 +1,87 @@ +import Image from "next/image"; +import Link from "next/link"; +import Container from "@/components/container"; +import { notFound } from "next/navigation"; +import { PortableText } from "@/lib/sanity/plugins/portabletext"; +import { urlForImage } from "@/lib/sanity/image"; +import SpeakerCard from "@/components/blog/speakerCard"; +import { formatWithOffset } from "@/utils/all"; + +export default function Event(props) { + const { loading, event } = props; + + const slug = event?.slug; + + if (!loading && !slug) { + notFound(); + } + + const imageProps = event?.mainImage ? urlForImage(event?.mainImage) : null; + + const SpeakerimageProps = event?.speaker?.image + ? urlForImage(event.speaker.image) + : null; + + return ( + <> + +
+

+ {event.title} +

+ +
+
+
+ {SpeakerimageProps && ( + + {event?.speaker?.name} + + )} +
+
+
+

+ + {event.speaker.name} + + · +

+ +
+
+
+
+
+
+ + +
+
+ {event.body && } +
+
+ + ← View all talks + +
+ {event.speaker && } +
+
+ + ); +} diff --git a/app/(website)/event/minimal/[slug]/page.js b/app/(website)/event/minimal/[slug]/page.js new file mode 100644 index 0000000..b4a69ba --- /dev/null +++ b/app/(website)/event/minimal/[slug]/page.js @@ -0,0 +1,41 @@ +import EventPage from "./minimal"; +import { urlForImage } from "@/lib/sanity/image"; +import { getAllEventSlugs, getEventBySlug } from "@/lib/sanity/client"; + +export async function generateStaticParams() { + return await getAllEventSlugs(); +} + +export async function generateMetadata({ params }) { + const event = await getEventBySlug(params.slug); + return { + title: event.title, + keywords: ["React Denver", "React Meetup Denver", event.title], + authors: [{ name: event.speaker.name }], + openGraph: { + title: event.title, + description: event.excerpt, + images: [ + { + url: urlForImage(event?.mainImage)?.src || "/img/opengraph.jpg", + width: 800, + height: 600, + }, + ], + }, + twitter: { + title: event?.title || "React Denver", + card: "summary_large_image", + }, + robots: { + index: true, + follow: true, + }, + }; +} +export default async function EventDefault({ params }) { + const event = await getEventBySlug(params.slug); + return ; +} + +export const revalidate = 60; diff --git a/app/(website)/event/sidebar/[slug]/page.js b/app/(website)/event/sidebar/[slug]/page.js new file mode 100644 index 0000000..bad57bf --- /dev/null +++ b/app/(website)/event/sidebar/[slug]/page.js @@ -0,0 +1,42 @@ +import EventPage from "./sidebar"; +import { urlForImage } from "@/lib/sanity/image"; +import { getAllEventSlugs, getEventBySlug } from "@/lib/sanity/client"; + +export async function generateStaticParams() { + return await getAllEventSlugs(); +} + +export async function generateMetadata({ params }) { + const event = await getEventBySlug(params.slug); + return { + title: event.title, + keywords: ["React Denver", "React Meetup Denver", event.title], + authors: [{ name: event.speaker.name }], + openGraph: { + title: event.title, + description: event.excerpt, + images: [ + { + url: urlForImage(event?.mainImage)?.src || "/img/opengraph.jpg", + width: 800, + height: 600, + }, + ], + }, + twitter: { + title: event?.title || "React Denver", + card: "summary_large_image", + }, + robots: { + index: true, + follow: true, + }, + }; +} + +export default async function EventDefault({ params }) { + const event = await getEventBySlug(params.slug); + return ; +} + +export const revalidate = 60; diff --git a/app/(website)/event/sidebar/[slug]/sidebar.js b/app/(website)/event/sidebar/[slug]/sidebar.js new file mode 100644 index 0000000..59c2bc3 --- /dev/null +++ b/app/(website)/event/sidebar/[slug]/sidebar.js @@ -0,0 +1,109 @@ +import { notFound } from "next/navigation"; +import { PortableText } from "@/lib/sanity/plugins/portabletext"; +import { urlForImage } from "@/lib/sanity/image"; +import Image from "next/image"; +import Link from "next/link"; +import SpeakerCard from "@/components/blog/speakerCard"; +import Sidebar from "@/components/sidebar"; +import { formatWithOffset } from "@/utils/all"; + +export default function Event(props) { + const { loading, event } = props; + + const slug = event?.slug; + + if (!loading && !slug) { + notFound(); + } + const imageProps = event?.mainImage ? urlForImage(event?.mainImage) : null; + + const SpeakerimageProps = event?.speaker?.image + ? urlForImage(event.speaker.image) + : null; + + return ( + <> +
+ {imageProps && ( +
+ {event.mainImage?.alt +
+ )} + +
+

+ {event.title} +

+ +
+
+
+
+ {SpeakerimageProps && ( + + {event?.speaker?.name} + + )} +
+

+ + {event.speaker.name} + {" "} + · +

+
+ +
+
+ +
+
+
+
+
+
+ + {/* {event?.mainImage && } */} +
+
+
+ {event.body && } +
+
+ + ← View all talks + +
+ {event.speaker && } +
+ +
+ + ); +} diff --git a/app/(website)/home.js b/app/(website)/home.js new file mode 100644 index 0000000..1daab08 --- /dev/null +++ b/app/(website)/home.js @@ -0,0 +1,35 @@ +import Link from "next/link"; +import Container from "@/components/container"; +import EventList from "@/components/eventlist"; +import Featured from "@/components/featured"; +import { EmailSubscriptionPopup } from "@/components/component/email-subscription-popup"; + +export default function Event({ events }) { + return ( + <> + {events && ( + +
+ {events.slice(0, 1).map((event) => ( + + ))} +
+
+ {events.slice(1, 14).map((event) => ( + + ))} +
+
+ + View all Events + +
+
+ )} + + + ); +} diff --git a/app/(website)/home/2-col/page.js b/app/(website)/home/2-col/page.js new file mode 100644 index 0000000..c1755c3 --- /dev/null +++ b/app/(website)/home/2-col/page.js @@ -0,0 +1,10 @@ +import HomeTwoCol from "./two-col"; + +import { getAllEvents } from "@/lib/sanity/client"; + +export default async function MinimalHomePage() { + const events = await getAllEvents(); + return ; +} + +export const revalidate = 60; diff --git a/app/(website)/home/2-col/two-col.js b/app/(website)/home/2-col/two-col.js new file mode 100644 index 0000000..f869bab --- /dev/null +++ b/app/(website)/home/2-col/two-col.js @@ -0,0 +1,43 @@ +import Container from "@/components/container"; +import EventAlt from "@/components/eventalt"; + +export default function HomeTwoCol({ events }) { + return ( + <> +
+
+

+ Our Events +

+

+ This is where we talk react & share our ideas. +

+
+
+ +
+

Most Recent

+
+
+ {events.slice(0, 1).map((event) => ( + + ))} +
+
+

Earlier Events

+
+
+ {events.slice(1).map((event) => ( + + ))} +
+
+ + ); +} diff --git a/app/(website)/home/alt/alternate.js b/app/(website)/home/alt/alternate.js new file mode 100644 index 0000000..08bde4f --- /dev/null +++ b/app/(website)/home/alt/alternate.js @@ -0,0 +1,28 @@ +import Container from "@/components/container"; +import EventList from "@/components/eventlist"; + +export default function AltHome({ events }) { + return ( + <> + +
+ {events.slice(0, 1).map(event => ( + + ))} +
+
+ {events.slice(1).map(event => ( + + ))} +
+
+ + ); +} diff --git a/app/(website)/home/alt/page.js b/app/(website)/home/alt/page.js new file mode 100644 index 0000000..c8221f9 --- /dev/null +++ b/app/(website)/home/alt/page.js @@ -0,0 +1,10 @@ +import AltHome from "./alternate"; + +import { getAllEvents } from "@/lib/sanity/client"; + +export default async function AltHomePage() { + const events = await getAllEvents(); + return ; +} + +export const revalidate = 60; diff --git a/app/(website)/home/lifestyle/lifestyle.js b/app/(website)/home/lifestyle/lifestyle.js new file mode 100644 index 0000000..cecaec2 --- /dev/null +++ b/app/(website)/home/lifestyle/lifestyle.js @@ -0,0 +1,70 @@ +import Container from "@/components/container"; +import EventList from "@/components/eventlist"; +import Featured from "@/components/featured"; + +export default function HomeLifeStyle({ events }) { + const featuredEvents = events.filter(item => item.featured) || null; + + return ( + <> + {featuredEvents && featuredEvents.length && ( + + )} + + + {featuredEvents.length > 4 && ( + <> +
+

+ Featured Events +

+
+
+ {featuredEvents.slice(1, 2).map(event => ( +
+ +
+ ))} + {featuredEvents.slice(2, 6).map(event => ( + + ))} +
+ + )} + +
+

+ Our Latest +

+
+
+ {events.map(event => ( + + ))} +
+
+ + ); +} diff --git a/app/(website)/home/lifestyle/page.js b/app/(website)/home/lifestyle/page.js new file mode 100644 index 0000000..4194e99 --- /dev/null +++ b/app/(website)/home/lifestyle/page.js @@ -0,0 +1,10 @@ +import HomeLifeStyle from "./lifestyle"; + +import { getAllEvents } from "@/lib/sanity/client"; + +export default async function LifeStyleHomePage() { + const events = await getAllEvents(); + return ; +} + +export const revalidate = 60; diff --git a/app/(website)/home/minimal/minimal.js b/app/(website)/home/minimal/minimal.js new file mode 100644 index 0000000..ebe8a95 --- /dev/null +++ b/app/(website)/home/minimal/minimal.js @@ -0,0 +1,27 @@ +import Container from "@/components/container"; +import EventList from "@/components/eventlist"; + +export default function MinimalHome({ events }) { + return ( + +
+

+ Our Events +

+
+
+ {events.map((event) => ( + + ))} +
+
+ ); +} diff --git a/app/(website)/home/minimal/page.js b/app/(website)/home/minimal/page.js new file mode 100644 index 0000000..fcb3927 --- /dev/null +++ b/app/(website)/home/minimal/page.js @@ -0,0 +1,10 @@ +import HomePage from "./minimal"; + +import { getAllEvents } from "@/lib/sanity/client"; + +export default async function MinimalHomePage() { + const events = await getAllEvents(); + return ; +} + +export const revalidate = 60; diff --git a/app/(website)/layout.tsx b/app/(website)/layout.tsx new file mode 100644 index 0000000..1701c88 --- /dev/null +++ b/app/(website)/layout.tsx @@ -0,0 +1,60 @@ +import { getSettings } from "@/lib/sanity/client"; +import Footer from "@/components/footer"; +import GetNavbar from "@/components/getnavbar"; +import { urlForImage } from "@/lib/sanity/image"; + +export async function sharedMetaData(params) { + const settings = await getSettings(); + + return { + // metadataBase: new URL(settings.url), + title: { + default: + settings?.title || + "React Denver", + template: "%s" + }, + description: + settings?.description || + "", + keywords: ["React Denver", "React Meetup Denver"], + authors: [{ name: "React Denver" }], + canonical: settings?.url, + openGraph: { + images: [ + { + url: + urlForImage(settings?.openGraphImage)?.src || + "/img/opengraph.jpg", + width: 800, + height: 600 + } + ] + }, + twitter: { + title: settings?.title || "React Denver", + card: "summary_large_image" + }, + robots: { + index: true, + follow: true + } + }; +} + +export async function generateMetadata({ params }) { + return await sharedMetaData(params); +} + +export default async function Layout({ children, params }) { + const settings = await getSettings(); + return ( + <> + + +
{children}
+ +