Add copy button to claim cards with full attribution
Each claim card now includes a copy button that copies all 5 claims plus proper attribution to the clipboard in a citation-ready format. Features: - Copy button with icon (Copy → Check on success) - Copies all 5 numbered claims - Includes full attribution footer: - Source title - Author: Kamil Banc, AI Adopters Club - URL to claim page - Original article URL - CC BY 4.0 license - Visual feedback: "Copied!" confirmation for 2 seconds - Lightweight: Uses native Clipboard API, no dependencies Documentation updates: - Added "Current System Architecture" section to WORKFLOW.md - Documented homepage structure (unified header, wide containers) - Documented claims library features (5-column grid, copy button) - Listed all strict build requirements (12-18 tokens, etc.) - Included copy format template for reference This ensures: ✓ Easy claim sharing with proper attribution ✓ Compliance with CC BY 4.0 license requirements ✓ All future claims follow current system setup ✓ Consistent citation format across all uses 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
98b1e6482b
commit
8e87da3958
49
WORKFLOW.md
49
WORKFLOW.md
|
|
@ -14,6 +14,55 @@ This document outlines the complete, step-by-step process for creating new claim
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Current System Architecture (2025-11-09)
|
||||||
|
|
||||||
|
### Homepage Structure
|
||||||
|
- **Unified Header:** Photo + name/title flows horizontally with bio content on desktop, stacks vertically on mobile
|
||||||
|
- **Container Width:** `max-w-[1920px]` with responsive padding (`px-6 md:px-8 lg:px-12`)
|
||||||
|
- **Claims Library:** Full interactive library with search, topic filters, and 5-column responsive grid
|
||||||
|
- **No separate Bio section:** All consolidated into Header component
|
||||||
|
|
||||||
|
### Claims Library Features
|
||||||
|
- **Responsive Grid:** 1 → 2 → 3 → 4 → 5 columns as screen width increases
|
||||||
|
- **Card Structure (per article):**
|
||||||
|
- Topic tags
|
||||||
|
- Article title (linked to detail page)
|
||||||
|
- Featured claim (single-sentence italic context)
|
||||||
|
- All 5 atomic claims (bulleted list, 12-18 tokens each)
|
||||||
|
- Publication date
|
||||||
|
- "View Claims →" link
|
||||||
|
- **Copy button:** Copies all 5 claims + full attribution to clipboard
|
||||||
|
- **Copy Format:**
|
||||||
|
```
|
||||||
|
[Article Title]
|
||||||
|
|
||||||
|
1. [Claim 1]
|
||||||
|
|
||||||
|
2. [Claim 2]
|
||||||
|
|
||||||
|
3. [Claim 3]
|
||||||
|
|
||||||
|
4. [Claim 4]
|
||||||
|
|
||||||
|
5. [Claim 5]
|
||||||
|
|
||||||
|
---
|
||||||
|
Source: [Article Title]
|
||||||
|
Author: Kamil Banc, AI Adopters Club
|
||||||
|
URL: https://kbanc.com/claims-library/[slug]
|
||||||
|
Original Article: [originalUrl]
|
||||||
|
License: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Strict Requirements (Build Blockers)
|
||||||
|
1. **Token Length:** Every claim MUST be 12-18 tokens (validation enforced, build fails otherwise)
|
||||||
|
2. **Claim Count:** Exactly 5 claims per article
|
||||||
|
3. **Featured Claim:** Required single-sentence summary for card display
|
||||||
|
4. **Date Format:** YYYY-MM-DD
|
||||||
|
5. **URL Format:** Must start with `https://aiadopters.club/p/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Step 1: Select Source Article
|
## Step 1: Select Source Article
|
||||||
|
|
||||||
### 1.1 Identify Article
|
### 1.1 Identify Article
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Search } from "lucide-react";
|
import { Search, Copy, Check } from "lucide-react";
|
||||||
import { useState, useMemo } from "react";
|
import { useState, useMemo } from "react";
|
||||||
import { ALL_CLAIMS_DATA, getTotalClaimsCount } from "@/data/claims";
|
import { ALL_CLAIMS_DATA, getTotalClaimsCount } from "@/data/claims";
|
||||||
|
|
||||||
const ClaimsLibrary = () => {
|
const ClaimsLibrary = () => {
|
||||||
const [selectedTopic, setSelectedTopic] = useState<string>("all");
|
const [selectedTopic, setSelectedTopic] = useState<string>("all");
|
||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
|
const [copiedSlug, setCopiedSlug] = useState<string | null>(null);
|
||||||
|
|
||||||
// Automatically populated from centralized claims data
|
// Automatically populated from centralized claims data
|
||||||
const articles = ALL_CLAIMS_DATA;
|
const articles = ALL_CLAIMS_DATA;
|
||||||
|
|
@ -48,6 +49,27 @@ const ClaimsLibrary = () => {
|
||||||
{ id: "measurement", label: "ROI & Measurement", color: "bg-purple-100 text-purple-800" },
|
{ id: "measurement", label: "ROI & Measurement", color: "bg-purple-100 text-purple-800" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const copyClaimsToClipboard = async (article: typeof ALL_CLAIMS_DATA[0]) => {
|
||||||
|
const formattedText = `${article.title}
|
||||||
|
|
||||||
|
${article.claims.map((claim, idx) => `${idx + 1}. ${claim}`).join('\n\n')}
|
||||||
|
|
||||||
|
---
|
||||||
|
Source: ${article.title}
|
||||||
|
Author: Kamil Banc, AI Adopters Club
|
||||||
|
URL: https://kbanc.com/claims-library/${article.slug}
|
||||||
|
Original Article: ${article.originalUrl}
|
||||||
|
License: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(formattedText);
|
||||||
|
setCopiedSlug(article.slug);
|
||||||
|
setTimeout(() => setCopiedSlug(null), 2000);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to copy:', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="py-10 md:py-12 border-b border-border">
|
<section className="py-10 md:py-12 border-b border-border">
|
||||||
<div className="container max-w-[1920px] mx-auto px-6 md:px-8 lg:px-12">
|
<div className="container max-w-[1920px] mx-auto px-6 md:px-8 lg:px-12">
|
||||||
|
|
@ -143,20 +165,39 @@ const ClaimsLibrary = () => {
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className="flex items-center justify-between pt-3 border-t border-border">
|
<div className="pt-3 border-t border-border">
|
||||||
<span className="text-xs text-muted-foreground">
|
<div className="flex items-center justify-between mb-2">
|
||||||
{new Date(article.date).toLocaleDateString('en-US', {
|
<span className="text-xs text-muted-foreground">
|
||||||
month: 'short',
|
{new Date(article.date).toLocaleDateString('en-US', {
|
||||||
day: 'numeric',
|
month: 'short',
|
||||||
year: 'numeric'
|
day: 'numeric',
|
||||||
})}
|
year: 'numeric'
|
||||||
</span>
|
})}
|
||||||
<Link
|
</span>
|
||||||
href={`/claims-library/${article.slug}`}
|
<Link
|
||||||
className="text-sm font-semibold text-accent hover:text-accent/80 transition-colors"
|
href={`/claims-library/${article.slug}`}
|
||||||
|
className="text-sm font-semibold text-accent hover:text-accent/80 transition-colors"
|
||||||
|
>
|
||||||
|
View Claims →
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => copyClaimsToClipboard(article)}
|
||||||
|
className="w-full flex items-center justify-center gap-2 py-2 px-3 text-xs font-medium text-muted-foreground hover:text-foreground bg-muted hover:bg-muted/80 rounded transition-colors"
|
||||||
|
aria-label="Copy all claims to clipboard"
|
||||||
>
|
>
|
||||||
View Claims →
|
{copiedSlug === article.slug ? (
|
||||||
</Link>
|
<>
|
||||||
|
<Check className="h-3 w-3" />
|
||||||
|
Copied!
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Copy className="h-3 w-3" />
|
||||||
|
Copy All Claims
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue