diff --git a/app/(authenticated)/account/page.jsx b/app/(authenticated)/account/page.jsx new file mode 100644 index 0000000..2fab39f --- /dev/null +++ b/app/(authenticated)/account/page.jsx @@ -0,0 +1,36 @@ +import React from "react" +import PageHeader from "@/sections/PageHeader" +import { cookies } from "next/headers" +import { getUserFromCookie } from "@/lib/auth" +import AccountContent from "@/sections/AccountContent" +import { redirect } from "next/navigation" +import pb from "@/lib/pocketbase" +import Background from "@/components/Utilities/Background" +import Footer from "@/components/Footer" +import Spacer from "@/components/Utilities/Spacer" + +export default async function AccountPage() { + const user = await getUserFromCookie(cookies()) + const cookie = cookies().get("pb_auth") + //server side + pb.authStore.loadFromCookie(cookie?.value || "") + !pb.authStore.isValid && redirect("/") + return ( + user && ( +
+ +
+ } /> + {/* */} + +
+
+
+ ) + ) +} diff --git a/app/(public)/[ignore]/auth/confirm-email-change/[token]/page.jsx b/app/(public)/[ignore]/auth/confirm-email-change/[token]/page.jsx new file mode 100644 index 0000000..4c58fd6 --- /dev/null +++ b/app/(public)/[ignore]/auth/confirm-email-change/[token]/page.jsx @@ -0,0 +1,89 @@ +"use client" + +import React from "react" +import { useForm } from "react-hook-form" +import { yupResolver } from "@hookform/resolvers/yup" +import { changeEmailValidationSchema } from "@/utils/form" +import { toast } from "react-toastify" +import PocketBase from "pocketbase" +import PageWrapper from "@/components/Utilities/PageWrapper" +import { usePathname } from "next/navigation" +import Background from "@/components/Utilities/Background" +import PageHeader from "@/sections/PageHeader" + +const pb = new PocketBase(process.env.NEXT_PUBLIC_POCKETBASE_URL) + +export default function ConfirmEmailChangePage() { + const pathName = usePathname() + const token = pathName.split("/").at(-1) + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + reset + } = useForm({ + resolver: yupResolver(changeEmailValidationSchema) + }) + + const onSubmit = async data => { + try { + await pb.collection("user").confirmEmailChange(token ?? "", data.password) + reset() + document.getElementById("sign-in-modal")?.click() + } catch (error) { + if (error instanceof Error) { + toast.error("There was a problem. Please try change your email again", { + position: "bottom-left", + autoClose: 5000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "colored" + }) + } + } + } + + return ( + + +
+ } + /> +
+
+ +
+ {errors.password?.message}  +
+
+
+ +
+
+
+
+
+ ) +} diff --git a/app/(public)/[ignore]/auth/confirm-password-reset/[token]/page.jsx b/app/(public)/[ignore]/auth/confirm-password-reset/[token]/page.jsx new file mode 100644 index 0000000..7399fb4 --- /dev/null +++ b/app/(public)/[ignore]/auth/confirm-password-reset/[token]/page.jsx @@ -0,0 +1,108 @@ +"use client" + +import React from "react" +import { useForm } from "react-hook-form" +import { yupResolver } from "@hookform/resolvers/yup" +import { passwordValidationSchema } from "@/utils/form" +import { toast } from "react-toastify" +import PocketBase from "pocketbase" +import PageWrapper from "@/components/Utilities/PageWrapper" +import { usePathname } from "next/navigation" +import Background from "@/components/Utilities/Background" +import PageHeader from "@/sections/PageHeader" + +const pb = new PocketBase(process.env.NEXT_PUBLIC_POCKETBASE_URL) + +export default function ConfirmPasswordResetPage() { + const pathName = usePathname() + const token = pathName.split("/").at(-1) + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + reset + } = useForm({ + resolver: yupResolver(passwordValidationSchema) + }) + + const onSubmit = async data => { + try { + await pb + .collection("user") + .confirmPasswordReset( + token ?? "", + data.newPassword, + data.newPasswordConfirm + ) + reset() + document.getElementById("sign-in-modal")?.click() + } catch (error) { + if (error instanceof Error) { + toast.error( + "There was a problem. Please try reset your password again", + { + position: "bottom-left", + autoClose: 5000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "colored" + } + ) + } + } + } + + return ( + + +
+ } /> +
+
+ +
+ {errors.newPassword?.message}  +
+
+
+ +
+ {errors.newPasswordConfirm?.message}  +
+
+
+ +
+
+
+
+
+ ) +} diff --git a/app/(public)/[ignore]/auth/confirm-verification/[token]/page.jsx b/app/(public)/[ignore]/auth/confirm-verification/[token]/page.jsx new file mode 100644 index 0000000..7399fb4 --- /dev/null +++ b/app/(public)/[ignore]/auth/confirm-verification/[token]/page.jsx @@ -0,0 +1,108 @@ +"use client" + +import React from "react" +import { useForm } from "react-hook-form" +import { yupResolver } from "@hookform/resolvers/yup" +import { passwordValidationSchema } from "@/utils/form" +import { toast } from "react-toastify" +import PocketBase from "pocketbase" +import PageWrapper from "@/components/Utilities/PageWrapper" +import { usePathname } from "next/navigation" +import Background from "@/components/Utilities/Background" +import PageHeader from "@/sections/PageHeader" + +const pb = new PocketBase(process.env.NEXT_PUBLIC_POCKETBASE_URL) + +export default function ConfirmPasswordResetPage() { + const pathName = usePathname() + const token = pathName.split("/").at(-1) + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + reset + } = useForm({ + resolver: yupResolver(passwordValidationSchema) + }) + + const onSubmit = async data => { + try { + await pb + .collection("user") + .confirmPasswordReset( + token ?? "", + data.newPassword, + data.newPasswordConfirm + ) + reset() + document.getElementById("sign-in-modal")?.click() + } catch (error) { + if (error instanceof Error) { + toast.error( + "There was a problem. Please try reset your password again", + { + position: "bottom-left", + autoClose: 5000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "colored" + } + ) + } + } + } + + return ( + + +
+ } /> +
+
+ +
+ {errors.newPassword?.message}  +
+
+
+ +
+ {errors.newPasswordConfirm?.message}  +
+
+
+ +
+
+
+
+
+ ) +} diff --git a/app/(public)/about/page.jsx b/app/(public)/about/page.jsx new file mode 100644 index 0000000..268d73c --- /dev/null +++ b/app/(public)/about/page.jsx @@ -0,0 +1,110 @@ +import PageHeader from "@/sections/PageHeader" +import React from "react" +import Footer from "@/components/Footer" +import Image from "next/image" +import Link from "next/link" + +export default async function About() { + return ( +
+ {/* Page sections */} + +
+

+ In 2023 I built{" "} + + Sign365 + {" "} + using pocketbase and setup an open source library to help people get + setup with{" "} + + Stripe + Pocketbase + + . As 2024 has come around I have had more and more requests for + applications and features on the existing code. I built a codebase + that would save me 20 hours + in the bootstrapping time to get my + applications ready. That is why I had to build{" "} + + FastPocket + {" "} + an easy solution to give everyone a head start. +

+

+ After reflecting on the challenges that developers face building their + applications, I've seen that there will never be one codebase + that suits all developers. But I am sure for those who are + opensourcing, self-hosting and want to spin up an application quickly + they will be able to do it using{" "} + + FastPocket + +

+

+ I am taking all of the knowledge that I have gained across 20+ + different React projects and combining it into 1 single codebase. It + includes personal philosophies on styling, theming and building and + will help indie-hackers, startups make complex technical decisions + that have been battle tested in enterprise code +

+

+ So I've committed to building{" "} + + FastPocket + {" "} + to help you build your projects faster to get paid. +

+

+ + FastPocket + {" "} + will get you producing more with less development. +

+

+ I know that your next project will grow exponentially because of the + work I have already done. +

+

+ Ready to launch your next product? +

+ + {"samuel + + + Let's go! + +
+
+ ) +} diff --git a/app/(public)/blogs/[slug]/page.jsx b/app/(public)/blogs/[slug]/page.jsx new file mode 100644 index 0000000..7159902 --- /dev/null +++ b/app/(public)/blogs/[slug]/page.jsx @@ -0,0 +1,101 @@ +import BlogContent from "@/sections/BlogContent" +import getPostMetadata from "@/utils/getPostMetaData" +import { headers } from "next/headers" +import React from "react" +import Spacer from "@/components/Utilities/Spacer" +import pb from "@/lib/pocketbase" +import Footer from "@/components/Footer" +import Background from "@/components/Utilities/Background" + +export async function generateMetadata({ params }) { + const { slug } = params + const headersList = headers() + const siteURL = headersList.get("host") + pb.autoCancellation(false) + const post = await pb.collection("blog").getFirstListItem(`slug="${slug}"`, { + requestKey: "metaData" + }) + pb.autoCancellation(true) + + return { + title: `${post.title} | FastPocket`, + authors: [ + { + name: post.author || "Samuel Wyndham" + } + ], + description: post.description, + keywords: post.keywords, + openGraph: { + title: `${post.title} | FastPocket`, + description: post.description, + type: "article", + url: `https://${siteURL}/blogs/${post.slug}`, + publishedTime: post.created, + modifiedTime: post.updated, + authors: [`https://${siteURL}/about`], + images: [ + { + url: `${post.imageUrl}`, + width: 1024, + height: 576, + alt: post.title, + type: "image/png" + } + ] + }, + twitter: { + card: "summary_large_image", + site: "@meinbiz", + creator: "@meinbiz", + title: `${post.title} | FastPocket`, + description: post.description, + images: [ + { + url: `${post.imageUrl}`, + width: 1024, + height: 576, + alt: post.title + } + ] + }, + alternates: { + canonical: `https://${siteURL}/blogs/${post.slug}` + } + } +} + +export const generateStaticParams = async () => { + const posts = await getPostMetadata() + console.log("static posts", posts.length) + const mappedPosts = posts.map(post => ({ + slug: post.slug + })) + return mappedPosts +} + +const PostPage = async props => { + console.log("params", props.params) + const post = await pb + .collection("blog") + .getFirstListItem(`slug="` + props.params.slug + `"`, { + requestKey: "post" + }) + return ( +
+ +
+ + +
+
+ ) +} + +export default PostPage diff --git a/app/(public)/blogs/page.jsx b/app/(public)/blogs/page.jsx new file mode 100644 index 0000000..20845c9 --- /dev/null +++ b/app/(public)/blogs/page.jsx @@ -0,0 +1,44 @@ +import PageHeader from "@/sections/PageHeader" +import BlogCard from "@/components/BlogCard" +import getPostMetadata from "@/utils/getPostMetaData" +import React from "react" +import Background from "@/components/Utilities/Background" +import Footer from "@/components/Footer" + +export default async function BlogsPage() { + const postMetadata = await getPostMetadata() + + console.log("blogs", postMetadata.length) + const postPreviews = postMetadata + .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()) + .map(post => ) + return ( +
+ + {/* Page sections */} + + {" "} +

+ Take a look as we show you how to make a great PocketBase + + React App +

+ + } + /> +
+
+ {postPreviews} +
+
+
+ +
+ ) +} diff --git a/app/(public)/contact/page.jsx b/app/(public)/contact/page.jsx new file mode 100644 index 0000000..a57ddda --- /dev/null +++ b/app/(public)/contact/page.jsx @@ -0,0 +1,31 @@ +import Footer from "@/components/Footer" +import Background from "@/components/Utilities/Background" +import Spacer from "@/components/Utilities/Spacer" +import FormLeftDescriptionRightContactUs from "@/sections/ContactUs/FormLeftDescriptionRightContactUs" +import PageHeader from "@/sections/PageHeader" +import React from "react" + +const page = () => { + return ( +
+ + + {" "} +

+ We'd love to talk about how we can help you. +

+ + } + /> + + +
+ +
+ ) +} + +export default page diff --git a/app/(public)/layout.jsx b/app/(public)/layout.jsx new file mode 100644 index 0000000..6b2d8d4 --- /dev/null +++ b/app/(public)/layout.jsx @@ -0,0 +1,11 @@ +import React from "react"; + +function layout({ children }) { + return ( + <> + {children} + + ); +} + +export default layout; diff --git a/app/(public)/pricing/action.js b/app/(public)/pricing/action.js new file mode 100644 index 0000000..e7bdc7d --- /dev/null +++ b/app/(public)/pricing/action.js @@ -0,0 +1,51 @@ +"use server" +import pb from "@/lib/pocketbase" + +export async function apiPrices() { + try { + const pocketbaseUrl = process.env.NEXT_PUBLIC_POCKETBASE_URL + if (!pocketbaseUrl) { + throw Error("Connection Timeout") + } + const productRequest = await fetch( + `${pocketbaseUrl}/api/collections/product/records?filter=(active=true)`, + { + cache: "no-cache", + method: "GET", + headers: { + "Content-Type": "application/json" + } + } + ) + const productResponse = await productRequest.json() + console.log("app/pricing/action", "productResponse", productResponse) + const unsortedProducts = productResponse.items + console.log("app/pricing/action", "unsortedProducts", unsortedProducts) + const prices = await pb.collection("price").getFullList() + console.log("app/pricing/action", "prices", prices) + for (const product of unsortedProducts) { + product.metadata.benefits = product?.metadata?.benefits + ? JSON.parse(product.metadata.benefits) + : [] + const pricesOfProduct = prices.filter( + price => price.product_id === product.product_id + ) + for (const priceOfProduct of pricesOfProduct) { + product.type = priceOfProduct.type + if (priceOfProduct.interval === "year") { + product.yearlyPrice = priceOfProduct + } else { + product.monthlyPrice = priceOfProduct + } + } + } + + const sortedProducts = unsortedProducts.sort( + (a, b) => a.product_order - b.product_order + ) + return sortedProducts + } catch (error) { + console.log(error) + return [] + } +} diff --git a/app/(public)/pricing/page.jsx b/app/(public)/pricing/page.jsx new file mode 100644 index 0000000..b45a68c --- /dev/null +++ b/app/(public)/pricing/page.jsx @@ -0,0 +1,48 @@ +"use client" + +import React from "react" +import PageHeader from "@/sections/PageHeader" +import Payment from "@/sections/Payment" +import Newsletter from "@/sections/Newsletter/Newsletter" +import Background from "@/components/Utilities/Background" +import Footer from "@/components/Footer" +import Spacer from "@/components/Utilities/Spacer" +import SolidBackgrondIconFeature from "@/sections/Features/SolidBackgrondIconFeature" + +export default function PricingPage() { + return ( +
+ + + {" "} +

+ Apply now to join us as we build apps fast, together! Be among + the first to start using FastPocket and lock in your lifetime + discount. +

+ + } + /> + +

+ What you will get! +

+ + + + + + + +
+ +
+ ) +} diff --git a/app/(public)/whatsnew/page.jsx b/app/(public)/whatsnew/page.jsx new file mode 100644 index 0000000..09c3914 --- /dev/null +++ b/app/(public)/whatsnew/page.jsx @@ -0,0 +1,34 @@ +"use client" + +import Footer from "@/components/Footer" +import Background from "@/components/Utilities/Background" +import Spacer from "@/components/Utilities/Spacer" +import SolidBackgrondIconFeature from "@/sections/Features/SolidBackgrondIconFeature" +import PageHeader from "@/sections/PageHeader" +import React from "react" + +const page = () => { + return ( +
+ + + {" "} +

+ Fastpocket gives you boilerplate to help you build. +

+ + } + /> + + + +
+ +
+ ) +} + +export default page diff --git a/app/actions.js b/app/actions.js new file mode 100644 index 0000000..8de57c1 --- /dev/null +++ b/app/actions.js @@ -0,0 +1,287 @@ +"use server" +import { redirect } from "next/navigation" +import pb from "@/lib/pocketbase" +import { cookies } from "next/headers" +import CryptoJS from "crypto-js" + +import { apiPrices } from "./(public)/pricing/action" + +export async function mailchimp(formData) { + const email = formData.email + const mailchimpBaseUrl = process.env.NEXT_PUBLIC_MAILCHIMP_BASE_URL + const mailchimpApiKey = process.env.NEXT_PUBLIC_MAILCHIMP_BASE64_API_KEY + const mailchimpList = process.env.NEXT_PUBLIC_MAILCHIMP_LIST_ID + if (!mailchimpApiKey) return + try { + const subscriberHash = CryptoJS.MD5(email.toLocaleLowerCase()) + const mailchimpResponse = await fetch( + `${mailchimpBaseUrl}/3.0/lists/${mailchimpList}/members/${subscriberHash}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + Authorization: mailchimpApiKey + }, + body: JSON.stringify({ + email_address: email, + status: "subscribed", + merge_fields: { + EMAIL: formData.email, + FNAME: formData.first_name, + LNAME: formData.last_name, + PHONE: !formData.phone_number ? "" : formData.phone_number, + CSIZE: formData.company_size, + SOURCE: formData.source + } + }) + } + ) + if (mailchimpResponse.status !== 200) { + throw new Error("couldn't complete the request") + } + return mailchimpResponse.json() + } catch (err) { + throw new Error("couldn't complete the request") + } +} + +export async function signup(formData) { + const email = formData.email + const password = formData.password + const organisation = formData.organisation + console.log("app/(authenticated)/actions", "organisation", organisation) + const pocketbaseUrl = process.env.NEXT_PUBLIC_POCKETBASE_URL + const adminToken = process.env.NEXT_PUBLIC_POCKETBASE_ADMIN_TOKEN + try { + const orgRes = await fetch( + `${pocketbaseUrl}/api/collections/organisation/records`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: adminToken + }, + body: JSON.stringify({ + name: organisation + }) + } + ) + console.log("orgRes.status: ", orgRes.status) + if (orgRes.status !== 200) { + throw new Error("Failed to create organisation") + } + const orgData = await orgRes.json() + console.log(orgData) + const userRes = await fetch( + `${pocketbaseUrl}/api/collections/user/records`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: adminToken + }, + body: JSON.stringify({ + firstName: formData.first_name, + lastName: formData.last_name, + displayName: formData.first_name + " " + formData.last_name, + email: email, + password: password, + passwordConfirm: password, + organisation: orgData?.id, + role: "Admin", + lastSeen: new Date() + }) + } + ) + console.log("userRes.status: ", userRes.status) + if (userRes.status !== 200) { + console.log(userRes) + throw new Error("Failed to create user") + } + } catch (err) { + if (err instanceof Error) { + throw new Error(err.message) + } + } +} + +export async function login(formData) { + console.log("login") + const email = formData.email + const password = formData.password + try { + const { token, record: data } = await pb + .collection("user") + .authWithPassword(email, password) + if (pb.authStore.isValid) { + const cookie = pb.authStore.exportToCookie() + + cookies().set("pb_auth", cookie, { + secure: true, + path: "/", + sameSite: "strict", + httpOnly: true + }) + } + return { + success: true, + error: "Failed to log in", + token: token, + data: data + } + } catch (error) { + console.log(error) + if (error.status == 403) { + await pb.collection("user").requestVerification(email) + } + return JSON.parse(JSON.stringify(error)) + } +} + +export async function getAuthCookie() { + try { + const cookie = cookies().get("pb_auth") + pb.authStore.loadFromCookie(cookie?.value || "") + return pb.authStore.token + } catch (error) { + return undefined + } +} + +export async function isAuthenticated() { + try { + const cookie = cookies().get("pb_auth") + if (!cookie) return false + pb.authStore.loadFromCookie(cookie?.value || "") + return pb.authStore.isValid || false + } catch (error) { + return undefined + } +} +export async function getUser() { + try { + const cookie = cookies().get("pb_auth") + if (!cookie) return false + pb.authStore.loadFromCookie(cookie?.value || "") + return pb.authStore.model + } catch (error) { + return undefined + } +} + +export async function logout() { + cookies().delete("pb_auth") + redirect("/") +} + +export async function createCheckoutSession(price_id, type) { + const pocketbaseUrl = process.env.NEXT_PUBLIC_POCKETBASE_URL + if (!pocketbaseUrl) { + throw Error("Connection Timeout") + } + if (!price_id) { + throw Error("There was an error during the payment processing") + } + const token = await getAuthCookie() + if (!token) { + throw Error("Could not authenticate") + } + console.log("token", token) + console.log("url ", `${pocketbaseUrl}/create-checkout-session`) + + const body = JSON.stringify({ + price: { + id: price_id, + type: type + }, + quantity: 1 + }) + console.log("body", body) + + try { + const createCheckoutSessionResponse = await fetch( + `${pocketbaseUrl}/create-checkout-session`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: token + }, + body: body + } + ) + if (createCheckoutSessionResponse.status !== 200) { + throw new Error("Failed to process Request") + } + + const createCheckoutSessionData = await createCheckoutSessionResponse.json() + + if (createCheckoutSessionData.url === "") { + throw new Error("Failed to process request an invalid URL was served") + } + return createCheckoutSessionData + } catch (error) { + throw error + } +} + +export async function getSubscriptions() { + const cookie = cookies().get("pb_auth") + pb.authStore.loadFromCookie(cookie?.value || "") + const userId = pb.authStore.model.id + const subscriptions = await pb + .collection("subscription") + .getFullList({ filter: `user_id="${userId}" && status="active"` }) + console.log("app/(authenticated)/actions", "subscriptions", subscriptions) + if (subscriptions.length === 0) { + return [] + } + const products = await apiPrices() + const subscriptionWithProducts = subscriptions.map(subscription => { + return { + ...subscription, + product: products.find( + product => + product?.yearlyPrice?.price_id === subscription.price_id || + product?.monthlyPrice?.price_id === subscription.price_id + ) + } + }) + return subscriptionWithProducts +} + +export async function createManagementSubscriptionSession() { + const pocketbaseUrl = process.env.NEXT_PUBLIC_POCKETBASE_URL + if (!pocketbaseUrl) { + throw Error("Connection Timeout") + } + const token = await getAuthCookie() + if (!token) { + throw Error("Could not authenticate") + } + console.log("token", token) + console.log("url ", `${pocketbaseUrl}/create-checkout-session`) + try { + const createManagementSessionResponse = await fetch( + `${pocketbaseUrl}/create-portal-link`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: token + }, + body: JSON.stringify({}) + } + ) + if (createManagementSessionResponse.status !== 200) { + console.log(createManagementSessionResponse.status) + throw new Error( + JSON.stringify(await createManagementSessionResponse.json()) + ) + } + const createManagementSessionData = await createManagementSessionResponse.json() + return createManagementSessionData + } catch (error) { + throw error + } +} diff --git a/app/fastpocket-diagram_352x316.webp b/app/fastpocket-diagram_352x316.webp new file mode 100644 index 0000000..d104c30 Binary files /dev/null and b/app/fastpocket-diagram_352x316.webp differ diff --git a/app/favicon.ico b/app/favicon.ico index 718d6fe..80d7e9f 100644 Binary files a/app/favicon.ico and b/app/favicon.ico differ diff --git a/app/globals.css b/app/globals.css index 875c01e..3026545 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,33 +1,9 @@ + + @tailwind base; @tailwind components; @tailwind utilities; -:root { - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; -} - -@media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; - } -} - -body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); -} - -@layer utilities { - .text-balance { - text-wrap: balance; - } -} +h1, h2, h3, h4, h5, h6 { + font-family: var(--heading-font); +} \ No newline at end of file diff --git a/app/layout.js b/app/layout.js deleted file mode 100644 index 9aef1df..0000000 --- a/app/layout.js +++ /dev/null @@ -1,17 +0,0 @@ -import { Inter } from "next/font/google"; -import "./globals.css"; - -const inter = Inter({ subsets: ["latin"] }); - -export const metadata = { - title: "Create Next App", - description: "Generated by create next app", -}; - -export default function RootLayout({ children }) { - return ( - - {children} - - ); -} diff --git a/app/layout.jsx b/app/layout.jsx new file mode 100644 index 0000000..f64ee4e --- /dev/null +++ b/app/layout.jsx @@ -0,0 +1,106 @@ +/* eslint-disable @next/next/no-before-interactive-script-outside-document */ +import "./globals.css"; +import "../app/globals.css"; +import { Arimo, Indie_Flower } from "next/font/google"; +import { ToastContainer } from "react-toastify"; +import "react-toastify/dist/ReactToastify.css"; +import Header from "@/components/Header"; +import { cookies } from "next/headers"; +import { isAuthenticated } from "@/lib/auth"; +import { GTagProvider, PHProvider, ThemeProvider } from "./providers"; +import Script from "next/script"; + +const raleway = Arimo({ + variable: "--body-font", + subsets: ["latin"], + display: "swap", +}); + +const arimo = Arimo({ + variable: "--body-font", + subsets: ["latin"], + display: "swap", +}); + +const indieFlower = Indie_Flower({ + variable: "--accent-font", + weight: "400", + subsets: ["latin"], + display: "swap", +}); + +export default async function RootLayout({ + children, +}) { + const isUserLoggedIn = await isAuthenticated(cookies()); + + return ( + + + + + +
+ {children} + + + + + + + ); +} + diff --git a/app/not-found.jsx b/app/not-found.jsx new file mode 100644 index 0000000..e5f7f28 --- /dev/null +++ b/app/not-found.jsx @@ -0,0 +1,72 @@ +import Background from "@/components/Utilities/Background" +import Link from "next/link" + +export default function NotFound() { + return ( + <> +
+ +
+
+
+

+ 404 +

+

+

+ Oops, something went wrong. +

+

+ Sorry, we couldn't find your page. +

+
+ + + + + Get the source code + + + + + + Back to home + +
+

+
+
+
+
+ + ) +} diff --git a/app/page.js b/app/page.js deleted file mode 100644 index a7c2036..0000000 --- a/app/page.js +++ /dev/null @@ -1,113 +0,0 @@ -import Image from "next/image"; - -export default function Home() { - return ( -
-
-

- Get started by editing  - app/page.js -

- -
- -
- Next.js Logo -
- - -
- ); -} diff --git a/app/page.jsx b/app/page.jsx new file mode 100644 index 0000000..8cff01b --- /dev/null +++ b/app/page.jsx @@ -0,0 +1,100 @@ +import "aos/dist/aos.css" +import React from "react" +import { SquaredBackgroundHero } from "@/sections/Hero" +import PageWrapper from "@/components/Utilities/PageWrapper" +import Footer from "@/components/Footer" +import PageHeader from "@/sections/PageHeader" +import ContainerImageIconBlocksFeature from "@/sections/Features/ContainerImageIconBlocksFeature" +import Background from "@/components/Utilities/Background" +import CardTestemonial from "@/sections/Testemonial/CardTestemonial" +import CardsFeature from "@/sections/Features/CardsFeature" +import FAQ from "@/sections/FAQ/RightAlignedBorderBottomFAQ" +import VerticalTabsFeature from "@/sections/Features/VerticalTabsFeature" +import Payment from "@/sections/Payment" + +export const metadata = { + title: "Pocketbase, Stripe, Next.js, Boilerplate | FastPocket", + description: + "FastPocket - Is a boilerplate codebase for everyone to build a product quickly.", + keywords: [ + "pocketbase", + "stripe", + "next.js", + "boilerplate", + "template", + "codebase" + ], + openGraph: { + url: "https://fastpocket.dev", + type: "website", + title: "Pocketbase, Stripe, Next.js, Boilerplate | FastPocket", + description: + "FastPocket - Is a boilerplate codebase for everyone to build a product quickly.", + images: [ + { + url: "https://fastpocket.dev/images/home/thumbnail.png", + width: 1200, + height: 630, + alt: "fastpocket.degv" + } + ] + }, + twitter: { + card: "summary_large_image", + title: "Pocketbase, Stripe, Next.js, Boilerplate | FastPocket", + description: + "fastpocket.dev - Programming blog for everyone to learn Elastic Stack, Next.js, Python, JavaScript, React, Machine Learning, Data Science, and more.", + creator: "@meinbiz", + site: "@meinbiz", + images: [ + { + url: "https://fastpocket.dev/images/home/thumbnail.png", + width: 1200, + height: 630, + alt: "fastpocket.degv" + } + ] + }, + alternates: { + canonical: "https://fastpocket.dev" + } +} + +export default function Home() { + return ( + <> + + + +
+ +
+ } + /> + + + + + + {" "} +

+ Purchase now to get early access at a discounted price and start + building with fly.io and docker compose templates immediately! +

+ + } + /> + + +