Skip to content
Merged
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
7 changes: 6 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ServiceWorkerRegister } from "@/components/sw-register";
import { Header } from "@/components/header";
import { Footer } from "@/components/footer";
import { Footer as PfsFooter } from "@profullstack/footer/react";
import { PitFooter } from "@/components/pit-footer";
import { getSiteUrl } from "@/lib/site-url";
import { priceUsdPerMonth } from "@/lib/plans";
import { getBrand } from "@/lib/brand-server";
Expand Down Expand Up @@ -183,9 +184,12 @@ export default async function RootLayout({
<PfsFooter
site="https://smshub.dev/"
links={[
{ label: "Pricing", href: "/#pricing" },
{ label: "Rent a number", href: "/phonenumbers" },
{ label: "Docs", href: "/docs" },
{ label: "API", href: "/docs/api" },
{ label: "Privacy", href: "/privacy" },
{ label: "Terms", href: "/terms" },
{ label: "SMS", href: "/sms" },
{ label: "GitHub", href: "https://github.com/profullstack/smshub" },
]}
/>
Expand All @@ -195,6 +199,7 @@ export default async function RootLayout({
<ServiceWorkerRegister />
</ToastProvider>
</BrandProvider>
<PitFooter />
</body>
</html>
);
Expand Down
69 changes: 6 additions & 63 deletions src/components/footer.tsx
Original file line number Diff line number Diff line change
@@ -1,75 +1,18 @@
"use client";

import Link from "next/link";
import { PHONENUMBERS_BOT_URL } from "@/components/sister-sim-link";
import { usePathname } from "next/navigation";
import { Logo } from "@/components/logo";

const APP_ROUTES = ["/inbox", "/settings", "/campaigns", "/contacts", "/analytics", "/org"];

/**
* One footer row: links, copyright and the Profullstack ring, from
* @profullstack/footer (an async server component rendered in app/layout.tsx
* and handed down as `bottom`). Hidden on app routes.
*/
export function Footer({ bottom }: { bottom?: React.ReactNode }) {
const pathname = usePathname();

// Hide footer on app routes
if (APP_ROUTES.some((r) => pathname.startsWith(r))) return null;

return (
<footer className="border-t border-gray-800 bg-gray-950 mt-auto">
<div className="max-w-6xl mx-auto px-4 sm:px-6 py-12">
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
{/* Brand */}
<div className="col-span-2 md:col-span-1">
<Logo
imageClassName="h-14 w-auto"
className="flex items-center gap-3 mb-4"
/>
<p className="text-sm text-gray-400">
Multi-platform SMS messaging for developers and businesses.
</p>
</div>

{/* Product */}
<div>
<h3 className="text-sm font-semibold text-gray-200 mb-3">Product</h3>
<ul className="space-y-2">
<li><Link href="/#features" className="text-sm text-gray-400 hover:text-white transition-colors">Features</Link></li>
<li><Link href="/#pricing" className="text-sm text-gray-400 hover:text-white transition-colors">Pricing</Link></li>
<li><Link href="/phonenumbers" className="text-sm text-gray-400 hover:text-white transition-colors">Rent a number</Link></li>
<li><Link href="/#api" className="text-sm text-gray-400 hover:text-white transition-colors">API</Link></li>
</ul>
</div>

{/* Platforms */}
<div>
<h3 className="text-sm font-semibold text-gray-200 mb-3">Platforms</h3>
<ul className="space-y-2">
<li><span className="text-sm text-gray-400">🌐 Web App</span></li>
<li><span className="text-sm text-gray-400">📱 iOS & Android</span></li>
<li><span className="text-sm text-gray-400">🖥 Desktop</span></li>
<li><span className="text-sm text-gray-400">📲 PWA</span></li>
</ul>
</div>

{/* Providers */}
<div>
<h3 className="text-sm font-semibold text-gray-200 mb-3">Providers</h3>
<ul className="space-y-2">
<li><Link href="/docs/twilio" className="text-sm text-blue-400 hover:text-blue-300 transition-colors">Twilio setup</Link></li>
<li><Link href="/docs/telnyx" className="text-sm text-blue-400 hover:text-blue-300 transition-colors">Telnyx setup</Link></li>
<li><Link href="/docs/api" className="text-sm text-blue-400 hover:text-blue-300 transition-colors">API, CLI &amp; MCP</Link></li>
<li><Link href="/phonenumbers" className="text-sm text-blue-400 hover:text-blue-300 transition-colors">SMSHub rented numbers</Link></li>
<li>
<a href={PHONENUMBERS_BOT_URL} target="_blank" rel="noopener" className="text-sm text-gray-400 hover:text-white transition-colors">
phonenumbers.bot: real SIMs, coming soon
</a>
</li>
</ul>
</div>
</div>

{/* Copyright, links and the Profullstack webring: @profullstack/footer, server-rendered in app/layout.tsx */}
{bottom && <div className="mt-8">{bottom}</div>}
</div>
</footer>
);
return <footer className="border-t border-gray-800 bg-gray-950 mt-auto text-gray-400">{bottom}</footer>;
}
21 changes: 21 additions & 0 deletions src/components/pit-footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* The pit.world network footer, fetched on the server and rendered as the last
* thing in <body> on smshub.dev. Cached for an hour; any failure (timeout,
* non-200, odd body) renders nothing so the page never breaks.
*/
const PIT_FOOTER_URL = "https://pit.world/snippets/footer.html?site=smshub.dev";

export async function PitFooter() {
try {
const res = await fetch(PIT_FOOTER_URL, {
next: { revalidate: 3600 },
signal: AbortSignal.timeout(3000),
});
if (!res.ok) return null;
const html = await res.text();
if (!html.includes("data-pit-footer")) return null;
return <div dangerouslySetInnerHTML={{ __html: html }} />;
} catch {
return null;
}
}
Loading