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
27 changes: 26 additions & 1 deletion frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,32 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CronStream</title>

<!-- Primary meta tags for SEO & AI summaries -->
<title>CronStream - Programmable Payroll for Contractors & Teams</title>
<meta name="description" content="Milestone-verified payment streams for teams. Contractors earn as work ships with automatic verification from GitHub, Jira, Bitbucket, and Figma. No invoices. No delays. No disputes." />
<meta name="keywords" content="contractor payments, payroll, blockchain payment streams, GitHub integration, verification, milestone-based pay" />

<!-- Open Graph for social sharing -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://cronstream.xyz" />
<meta property="og:title" content="CronStream - Programmable Payroll for Contractors & Teams" />
<meta property="og:description" content="Milestone-verified payment streams for teams. Contractors earn as work ships with automatic verification from GitHub, Jira, Bitbucket, and Figma." />
<meta property="og:image" content="https://cronstream.xyz/og-image.png" />

<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:url" content="https://cronstream.xyz" />
<meta name="twitter:title" content="CronStream - Programmable Payroll for Contractors & Teams" />
<meta name="twitter:description" content="Milestone-verified payment streams. Contractors earn as work ships with automatic verification from GitHub, Jira, Bitbucket, and Figma." />
<meta name="twitter:image" content="https://cronstream.xyz/og-image.png" />

<!-- Additional SEO -->
<link rel="canonical" href="https://cronstream.xyz" />
<meta name="robots" content="index, follow" />
<meta name="language" content="English" />

<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet" />
Expand Down
52 changes: 52 additions & 0 deletions frontend/public/og-image.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 52 additions & 0 deletions frontend/src/hooks/useMetaTags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useEffect } from 'react';

export function useMetaTags({
title = 'CronStream - Programmable Payroll for Contractors & Teams',
description = 'Milestone-verified payment streams for teams. Contractors earn as work ships with automatic verification.',
url = 'https://cronstream.xyz',
image = 'https://cronstream.xyz/og-image.png',
type = 'website',
} = {}) {
useEffect(() => {
// Update title
document.title = title;

// Update or create meta tags
const updateMeta = (name, content, isProperty = false) => {
let element = document.querySelector(
isProperty ? `meta[property="${name}"]` : `meta[name="${name}"]`
);
if (!element) {
element = document.createElement('meta');
isProperty ? element.setAttribute('property', name) : element.setAttribute('name', name);
document.head.appendChild(element);
}
element.content = content;
};

// Standard meta
updateMeta('description', description);

// Open Graph
updateMeta('og:type', type, true);
updateMeta('og:url', url, true);
updateMeta('og:title', title, true);
updateMeta('og:description', description, true);
updateMeta('og:image', image, true);

// Twitter
updateMeta('twitter:url', url);
updateMeta('twitter:title', title);
updateMeta('twitter:description', description);
updateMeta('twitter:image', image);

// Canonical
let canonical = document.querySelector('link[rel="canonical"]');
if (!canonical) {
canonical = document.createElement('link');
canonical.rel = 'canonical';
document.head.appendChild(canonical);
}
canonical.href = url;
}, [title, description, url, image, type]);
}
15 changes: 15 additions & 0 deletions frontend/src/hooks/useSchemaMarkup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useEffect } from 'react';

export function useSchemaMarkup(schema) {
useEffect(() => {
if (!schema) return;

let script = document.querySelector('script[type="application/ld+json"]');
if (!script) {
script = document.createElement('script');
script.type = 'application/ld+json';
document.head.appendChild(script);
}
script.textContent = JSON.stringify(schema);
}, [schema]);
}
7 changes: 7 additions & 0 deletions frontend/src/pages/Faucet.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useState } from 'react';
import { useAccount, useWriteContract, useWaitForTransactionReceipt } from 'wagmi';
import { parseAbi } from 'viem';
import { useNavigate } from 'react-router-dom';
import { useMetaTags } from '../hooks/useMetaTags';

const CRM_ADDRESS = '0x2Ca6e6FbAA8D0Bc27a64Ca079aFa6bf5cc8C7ad1';
const CRM_ABI = parseAbi(['function faucet() external']);
Expand Down Expand Up @@ -63,6 +64,12 @@ export default function Faucet() {
const navigate = useNavigate();
const [claimed, setClaimed] = useState(false);

useMetaTags({
title: 'Faucet - CronStream Testnet',
description: 'Get free test tokens on Arbitrum Sepolia and Robinhood Chain for CronStream development and testing.',
url: 'https://cronstream.xyz/faucet',
});

const { writeContract, data: txHash, isPending, error } = useWriteContract();
const { isLoading: confirming, isSuccess } = useWaitForTransactionReceipt({ hash: txHash });

Expand Down
32 changes: 32 additions & 0 deletions frontend/src/pages/Landing.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { useAccount } from 'wagmi';
import { useConnectModal } from '@rainbow-me/rainbowkit';
import { useEffect } from 'react';
import { useProfile } from '../hooks/useProfile';
import { useMetaTags } from '../hooks/useMetaTags';
import { useSchemaMarkup } from '../hooks/useSchemaMarkup';
import StreamBackground from '../components/StreamBackground';
import FlowDiagram from '../components/FlowDiagram';

Expand Down Expand Up @@ -30,6 +32,36 @@ export default function Landing() {
const { openConnectModal } = useConnectModal();
const { profileComplete } = useProfile(address);

// SEO metadata
useMetaTags({
title: 'CronStream - Programmable Payroll for Contractors & Teams',
description: 'Milestone-verified payment streams for teams. Contractors earn as work ships with automatic verification from GitHub, Jira, Bitbucket, and Figma. No invoices. No delays. No disputes.',
url: 'https://cronstream.xyz',
});

// Structured data for Google AI summaries
useSchemaMarkup({
'@context': 'https://schema.org',
'@type': 'SoftwareApplication',
name: 'CronStream',
description: 'Programmable payroll platform for milestone-verified contractor payments',
applicationCategory: 'FinanceApplication',
offers: {
'@type': 'Offer',
price: '0.5',
priceCurrency: 'USD',
description: '0.5% protocol fee'
},
featureList: [
'Milestone-Gated Streams',
'Autonomous Agent Verification',
'ERC-20 Token Support',
'Per-Second Precision',
'Full Budget Recovery',
'EIP-712 Cryptographic Proof'
],
});

useEffect(() => {
if (isConnected) {
navigate(profileComplete ? '/app/dashboard' : '/app/setup', { replace: true });
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/pages/Privacy.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useNavigate } from 'react-router-dom';
import { useMetaTags } from '../hooks/useMetaTags';

const LAST_UPDATED = 'May 2026';

Expand Down Expand Up @@ -35,6 +36,13 @@ const SECTIONS = [

export default function Privacy() {
const navigate = useNavigate();

useMetaTags({
title: 'Privacy Policy - CronStream',
description: 'Learn how CronStream protects your data and privacy. We collect only what is necessary to operate the protocol.',
url: 'https://cronstream.xyz/privacy',
});

return (
<div className="min-h-screen bg-dark text-white">
{/* Nav */}
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/pages/PublicProfile.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useParams, useNavigate } from 'react-router-dom';
import { useAccount } from 'wagmi';
import { ConnectButton } from '@rainbow-me/rainbowkit';
import { useCreateStream } from '../context/CreateStreamContext';
import { useMetaTags } from '../hooks/useMetaTags';
import CreateStreamModal from '../components/CreateStreamModal';
import Watermark from '../components/Watermark';

Expand All @@ -26,6 +27,17 @@ export default function PublicProfile() {
const [status, setStatus] = useState('idle');
const [contractor, setContractor] = useState(null);

// Update metadata when contractor data loads
useMetaTags({
title: contractor?.name
? `${contractor.name} - CronStream Profile`
: 'CronStream - Contractor Profile',
description: contractor?.name
? `View ${contractor.name}'s payment streams and work verification on CronStream`
: 'View contractor profile on CronStream',
url: `https://cronstream.xyz/p/${username}`,
});

// Only fetch once wallet is connected - never expose contractor info to unauthenticated visitors
useEffect(() => {
if (!isConnected || !username) return;
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/pages/Terms.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useNavigate } from 'react-router-dom';
import { useMetaTags } from '../hooks/useMetaTags';

const LAST_UPDATED = 'May 2026';

Expand Down Expand Up @@ -47,6 +48,13 @@ const SECTIONS = [

export default function Terms() {
const navigate = useNavigate();

useMetaTags({
title: 'Terms of Service - CronStream',
description: 'CronStream Terms of Service and legal agreement for using the platform.',
url: 'https://cronstream.xyz/terms',
});

return (
<div className="min-h-screen bg-dark text-white">
{/* Nav */}
Expand Down
Loading