diff --git a/.env.self-host.example b/.env.self-host.example index 8d97f19..1f173c7 100644 --- a/.env.self-host.example +++ b/.env.self-host.example @@ -18,6 +18,20 @@ DASHBOARD_DOMAIN=app.example.com LANDING_DOMAIN=www.example.com WIKI_DOMAIN=docs.example.com +# For local development: URIs for running services locally +# These are used by the applications at runtime +API_URI=http://localhost:8080 +DASHBOARD_URI=http://localhost:3000 +LANDING_URI=http://localhost:4000 +WIKI_URI=http://localhost:1000 + +# NEXT_PUBLIC_* variables are used for client-side code and sitemap generation +# Use placeholder URLs that will be replaced at Docker container runtime +NEXT_PUBLIC_API_URI=https://api.useplunk.com +NEXT_PUBLIC_DASHBOARD_URI=https://app.useplunk.com +NEXT_PUBLIC_LANDING_URI=https://www.useplunk.com +NEXT_PUBLIC_WIKI_URI=https://docs.useplunk.com + # Set to 'true' if using HTTPS in production (behind a reverse proxy/load balancer) # This affects how application URIs are auto-generated from domain names USE_HTTPS=false diff --git a/Dockerfile b/Dockerfile index f1ea211..d071a6d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -171,6 +171,8 @@ RUN --mount=type=cache,target=/app/.turbo,sharing=locked \ NEXT_PUBLIC_LANDING_URI=${LANDING_URI} \ NEXT_PUBLIC_WIKI_URI=${WIKI_URI} \ yarn turbo build --filter=wiki +# Generate sitemap for wiki +RUN NEXT_PUBLIC_WIKI_URI=${WIKI_URI} yarn workspace wiki sitemap # Step 4: Copy and build Web dashboard COPY apps/web ./apps/web @@ -184,6 +186,8 @@ RUN --mount=type=cache,target=/app/.turbo,sharing=locked \ NEXT_PUBLIC_LANDING_URI=${LANDING_URI} \ NEXT_PUBLIC_WIKI_URI=${WIKI_URI} \ yarn turbo build --filter=web +# Generate sitemap for web +RUN NEXT_PUBLIC_DASHBOARD_URI=${DASHBOARD_URI} yarn workspace web sitemap # Step 5: Copy and build Landing page COPY apps/landing ./apps/landing @@ -197,6 +201,8 @@ RUN --mount=type=cache,target=/app/.turbo,sharing=locked \ NEXT_PUBLIC_LANDING_URI=${LANDING_URI} \ NEXT_PUBLIC_WIKI_URI=${WIKI_URI} \ yarn turbo build --filter=landing +# Generate sitemap for landing +RUN NEXT_PUBLIC_LANDING_URI=${LANDING_URI} yarn workspace landing sitemap # Copy any remaining root files (if needed) COPY . . diff --git a/apps/landing/next-sitemap.config.js b/apps/landing/next-sitemap.config.js index ba90fa4..24bb943 100644 --- a/apps/landing/next-sitemap.config.js +++ b/apps/landing/next-sitemap.config.js @@ -1,5 +1,5 @@ /** @type {import('next-sitemap').IConfig} */ module.exports = { - siteUrl: process.env.NEXT_PUBLIC_LANDING_URI || 'https://www.swyp.be', + siteUrl: process.env.NEXT_PUBLIC_LANDING_URI || 'https://www.useplunk.com', generateRobotsTxt: true, }; diff --git a/apps/landing/src/components/CodeBlock.tsx b/apps/landing/src/components/CodeBlock.tsx index a2644ef..03f84be 100644 --- a/apps/landing/src/components/CodeBlock.tsx +++ b/apps/landing/src/components/CodeBlock.tsx @@ -1,31 +1,63 @@ import {motion} from 'framer-motion'; -import React from 'react'; +import React, {useState} from 'react'; +import {Check, Copy} from 'lucide-react'; interface CodeBlockProps { code: string; language?: string; title?: string; + showCopy?: boolean; } /** - * Reusable code block component with syntax highlighting styling + * Reusable code block component with syntax highlighting styling and copy functionality */ -export function CodeBlock({code, language = 'javascript', title}: CodeBlockProps) { +export function CodeBlock({code, language = 'javascript', title, showCopy = true}: CodeBlockProps) { + const [copied, setCopied] = useState(false); + + const handleCopy = async () => { + await navigator.clipboard.writeText(code); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + return ( - {title && ( -
- {title} + {(title || showCopy) && ( +
+ {title && {title}} + {!title && {language}} + {showCopy && ( + + )}
)} -
-        {code}
+      
+        {code}
       
); diff --git a/apps/landing/src/components/FAQSection.tsx b/apps/landing/src/components/FAQSection.tsx index cca2bcd..db03212 100644 --- a/apps/landing/src/components/FAQSection.tsx +++ b/apps/landing/src/components/FAQSection.tsx @@ -25,7 +25,7 @@ export function FAQSection({faqs, schemaId = 'faq-schema'}: FAQSectionProps) { __html: JSON.stringify({ '@context': 'https://schema.org', '@type': 'FAQPage', - 'mainEntity': faqs.map((faq) => ({ + 'mainEntity': faqs.map(faq => ({ '@type': 'Question', 'name': faq.question, 'acceptedAnswer': { diff --git a/apps/landing/src/components/Footer/Footer.tsx b/apps/landing/src/components/Footer/Footer.tsx index 89ca96f..5fb8603 100644 --- a/apps/landing/src/components/Footer/Footer.tsx +++ b/apps/landing/src/components/Footer/Footer.tsx @@ -67,7 +67,10 @@ export default function Footer() {
  • - + Made by humans
  • @@ -131,9 +134,7 @@ export default function Footer() {
    -

    - © {new Date().getFullYear()} Plunk. All rights reserved. -

    +

    © {new Date().getFullYear()} Plunk. All rights reserved.

    diff --git a/apps/landing/src/components/Navbar/Navbar.tsx b/apps/landing/src/components/Navbar/Navbar.tsx index cb5df03..41dcdab 100644 --- a/apps/landing/src/components/Navbar/Navbar.tsx +++ b/apps/landing/src/components/Navbar/Navbar.tsx @@ -25,19 +25,34 @@ export default function Navbar() {
    - + By humans - + Pricing + + Guides + + Docs @@ -47,32 +62,27 @@ export default function Navbar() { strokeWidth="2" d="M9.25 4.75H6.75C5.64543 4.75 4.75 5.64543 4.75 6.75V17.25C4.75 18.3546 5.64543 19.25 6.75 19.25H17.25C18.3546 19.25 19.25 18.3546 19.25 17.25V14.75" /> - - + +
    - + Sign in Get started @@ -146,6 +156,14 @@ export default function Navbar() { Pricing + setMobileOpen(false)} + className="block rounded-lg px-4 py-3 text-sm font-medium text-neutral-600 transition hover:bg-neutral-100 hover:text-neutral-900" + > + Guides + +

    Pricing Calculator

    -

    - Compare costs across platforms at different email volumes -

    +

    Compare costs across platforms at different email volumes

    {/* Volume Selector */}
    - {volumeOptions.map((option) => ( + {volumeOptions.map(option => (