Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 208 additions & 0 deletions app/contact/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import { PageContainer } from "@/components/layout/PageContainer";
import { QRCodeDisplay } from "@/components/contact/QRCodeDisplay";
import { JsonLd } from "@/components/seo/JsonLd";
import { Heading } from "@/components/ui/Heading";

import { SITE_URL } from "@/lib/constants";
import {
createBreadcrumbList,
createSchemaGraph,
} from "@/lib/seo";
import { paths, urls } from "@/lib/utils/urls";

export async function generateMetadata() {
return {
title: "Contact | Builder Vancouver",
description:
"Connect with Builder Vancouver. Follow us on X, Nostr, and Luma. Scan our QR code to share our website.",
keywords: ["contact", "builder vancouver", "bitcoin", "connect"],
};
}

/**
* Contact page with real-time QR code and social links
* Optimized for mobile sharing and showcasing work
*/
export default function ContactPage() {
// Use SITE_URL as fallback, QRCodeDisplay will update with actual URL on client
const fallbackUrl = `${SITE_URL}${paths.contact()}`;

const breadcrumbSchema = createBreadcrumbList([
{ name: "Home", url: urls.home() },
{ name: "Contact" },
]);

const structuredData = createSchemaGraph(breadcrumbSchema);

// Social links - update these with actual URLs
const socialLinks = [
{
name: "X (Twitter)",
href: "https://x.com/builder_van", // Update with actual URL
icon: (
<svg
className="w-6 h-6"
fill="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
</svg>
),
color: "hover:text-neutral-100",
},
{
name: "Nostr",
href: "https://nostr.com/builder_van", // Update with actual Nostr profile URL or npub
icon: (
<svg
className="w-6 h-6"
fill="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z" />
</svg>
),
color: "hover:text-orange-400",
},
{
name: "Luma",
href: "https://lu.ma/builder-vancouver", // Update with actual Luma URL
icon: (
<svg
className="w-6 h-6"
fill="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" />
</svg>
),
color: "hover:text-blue-400",
},
];

return (
<>
<JsonLd data={structuredData} />
<PageContainer className="min-h-screen">
{/* Hero Section */}
<div className="text-center mb-12">
<Heading level="h1" className="text-orange-400 mb-4">
Connect With Us
</Heading>
<p className="text-xl text-neutral-300 mb-2">
Join the Builder Vancouver community
</p>
<p className="text-lg text-neutral-400">
Scan the QR code or follow us on social media
</p>
</div>

{/* QR Code Section - Mobile Optimized */}
<div className="mb-12">
<div className="bg-gradient-to-br from-neutral-900 to-neutral-800 rounded-2xl p-8 border border-neutral-700 shadow-2xl">
<QRCodeDisplay url={fallbackUrl} />
</div>
</div>

{/* Social Links Section */}
<div className="mb-12">
<h2 className="text-2xl font-bold text-neutral-100 mb-6 text-center">
Follow Us
</h2>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
{socialLinks.map((social) => (
<a
key={social.name}
href={social.href}
target="_blank"
rel="noopener noreferrer"
className="group bg-neutral-900 border border-neutral-800 rounded-xl p-6 hover:border-orange-400 transition-all hover:shadow-lg hover:shadow-orange-400/20"
>
<div className="flex flex-col items-center text-center">
<div className={`text-neutral-400 ${social.color} transition-colors mb-4`}>
{social.icon}
</div>
<h3 className="text-lg font-semibold text-neutral-100 mb-2 group-hover:text-orange-400 transition-colors">
{social.name}
</h3>
<p className="text-sm text-neutral-400">
Connect on {social.name}
</p>
</div>
</a>
))}
</div>
</div>

{/* Contact Information */}
<div className="bg-gradient-to-br from-neutral-900 to-neutral-800 rounded-2xl p-8 border border-neutral-700">
<h2 className="text-2xl font-bold text-neutral-100 mb-6 text-center">
Get In Touch
</h2>
<div className="text-center space-y-4">
<p className="text-lg text-neutral-300">
Email us at{" "}
<a
href="mailto:bitcoinbuildervan@gmail.com"
className="font-semibold text-orange-400 underline hover:text-orange-300 transition-colors"
>
bitcoinbuildervan@gmail.com
</a>
</p>
<p className="text-sm text-neutral-400">
We typically respond within 48 hours
</p>
</div>
</div>

{/* Work Showcase Section */}
<div className="mt-12">
<h2 className="text-2xl font-bold text-neutral-100 mb-6 text-center">
What We Do
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="bg-neutral-900 border border-neutral-800 rounded-xl p-6 hover:border-orange-400 transition-all">
<h3 className="text-xl font-semibold text-orange-400 mb-3">
Bitcoin Education
</h3>
<p className="text-neutral-300">
Learn about Bitcoin, Lightning Network, and Layer 2 solutions
through our educational sessions and workshops.
</p>
</div>
<div className="bg-neutral-900 border border-neutral-800 rounded-xl p-6 hover:border-orange-400 transition-all">
<h3 className="text-xl font-semibold text-orange-400 mb-3">
Community Events
</h3>
<p className="text-neutral-300">
Join our regular meetups, presentations, and networking events
in Vancouver.
</p>
</div>
<div className="bg-neutral-900 border border-neutral-800 rounded-xl p-6 hover:border-orange-400 transition-all">
<h3 className="text-xl font-semibold text-orange-400 mb-3">
Open Source
</h3>
<p className="text-neutral-300">
Contribute to Bitcoin-related projects and collaborate with
builders in the ecosystem.
</p>
</div>
<div className="bg-neutral-900 border border-neutral-800 rounded-xl p-6 hover:border-orange-400 transition-all">
<h3 className="text-xl font-semibold text-orange-400 mb-3">
Resources
</h3>
<p className="text-neutral-300">
Access presentations, slides, recaps, and educational materials
from our community.
</p>
</div>
</div>
</div>
</PageContainer>
</>
);
}
86 changes: 86 additions & 0 deletions components/contact/QRCodeDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"use client";

import { useEffect, useState } from "react";
import { QRCodeSVG } from "qrcode.react";

interface QRCodeDisplayProps {
url: string;
}

/**
* Client component for QR code display with mobile detection
* Uses the current page URL dynamically for real-time QR code generation
*/
export function QRCodeDisplay({ url }: QRCodeDisplayProps) {
const [currentUrl, setCurrentUrl] = useState(url);
const [isMobile, setIsMobile] = useState(false);

useEffect(() => {
// Use the actual current page URL
setCurrentUrl(window.location.href);

const checkMobile = () => {
setIsMobile(window.innerWidth < 768);
};
checkMobile();
window.addEventListener("resize", checkMobile);
return () => window.removeEventListener("resize", checkMobile);
}, []);

const handleShare = async () => {
if (navigator.share) {
try {
await navigator.share({
title: "Builder Vancouver",
text: "Check out Builder Vancouver - Bitcoin Meetups & Education",
url: currentUrl,
});
} catch (err) {
// User cancelled or error occurred
console.log("Share cancelled");
}
}
};

return (
<div className="flex flex-col items-center">
<h2 className="text-2xl font-bold text-neutral-100 mb-6">
Share This Page
</h2>

{/* QR Code Container */}
<div className="bg-white p-6 rounded-xl shadow-lg mb-6">
<QRCodeSVG
value={currentUrl}
size={isMobile ? 200 : 256}
level="H"
includeMargin={true}
fgColor="#000000"
bgColor="#ffffff"
/>
</div>

{/* URL Display */}
<div className="w-full max-w-md">
<p className="text-sm text-neutral-400 mb-2 text-center">
Current URL:
</p>
<div className="bg-neutral-950 rounded-lg p-3 border border-neutral-700">
<p className="text-xs text-neutral-300 break-all text-center font-mono">
{currentUrl}
</p>
</div>
</div>

{/* Share Button for Mobile */}
{isMobile && typeof navigator !== "undefined" && "share" in navigator && (
<button
onClick={handleShare}
className="mt-6 px-6 py-3 bg-orange-400 text-neutral-950 font-semibold rounded-lg hover:bg-orange-300 transition-colors shadow-lg"
>
Share Page
</button>
)}
</div>
);
}
31 changes: 18 additions & 13 deletions components/layout/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,54 +43,51 @@ export function Footer() {
{ href: paths.cities.list(), label: "Cities" },
{ href: paths.sponsors.list(), label: "Sponsors" },
{ href: paths.members.list(), label: "Members" },
{ href: paths.contact(), label: "Contact" },
],
};

// Social media links - update these with actual URLs
const socialLinks = [
{
name: "Twitter",
href: "#",
name: "X (Twitter)",
href: "https://x.com/builder_van", // Update with actual URL
icon: (
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path d="M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84" />
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
</svg>
),
},
{
name: "GitHub",
href: "#",
name: "Nostr",
href: "https://nostr.com/builder_van", // Update with actual Nostr profile URL
icon: (
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
fillRule="evenodd"
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
clipRule="evenodd"
/>
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" />
</svg>
),
},
{
name: "Nostr",
href: "#",
name: "Luma",
href: "https://lu.ma/builder-vancouver", // Update with actual Luma URL
icon: (
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" />
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" />
</svg>
),
},
Expand Down Expand Up @@ -214,6 +211,14 @@ export function Footer() {
</a>
))}
</div>
<div className="space-y-3 mb-4">
<Link
href={paths.contact()}
className="text-sm text-neutral-400 hover:text-orange-400 transition-colors block"
>
Contact Us
</Link>
</div>
<div className="text-xs text-neutral-500 space-y-2">
<p>Builder Vancouver</p>
<p>Bitcoin Meetups & Education</p>
Expand Down
2 changes: 2 additions & 0 deletions components/layout/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export function Navbar() {

const navItems: NavItem[] = [
{ href: "/", label: "Home" },
{ href: "/contact", label: "Contact" },
{
label: "About",
children: [
Expand Down Expand Up @@ -116,6 +117,7 @@ export function Navbar() {
{ href: "/sponsors", label: "Sponsors" },
{ href: "/members", label: "Members" },
{ href: "/get-involved", label: "Get Involved" },
{ href: "/contact", label: "Contact" },
],
},
];
Expand Down
Loading