Function bodies 234 total
POST function · typescript · L5-L108 (104 LOC)app/api/stripe/portal/route.ts
export async function POST(request: NextRequest) {
// Check if Stripe is configured
if (!isStripeConfigured()) {
return NextResponse.json(
{ error: 'Stripe is not configured' },
{ status: 503 }
);
}
try {
// Get Supabase client
const supabase = createServerClient();
if (!supabase) {
return NextResponse.json(
{ error: 'Database not configured' },
{ status: 503 }
);
}
// Get authenticated user
const authHeader = request.headers.get('authorization');
let userId: string | null = null;
if (authHeader?.startsWith('Bearer ')) {
const token = authHeader.substring(7);
const { data: { user } } = await supabase.auth.getUser(token);
userId = user?.id || null;
}
// For development/testing, allow demo user
if (!userId && process.env.NODE_ENV === 'development') {
userId = 'demo-user-id';
}
if (!userId) {
return NextResponse.json(
{ error: 'UnauthorizedPOST function · typescript · L10-L50 (41 LOC)app/api/stripe/webhooks/route.ts
export async function POST(request: NextRequest) {
// Get raw body for signature verification
const body = await request.text();
const signature = request.headers.get('stripe-signature');
if (!signature) {
console.error('No stripe-signature header');
return NextResponse.json({ error: 'No signature' }, { status: 400 });
}
// Verify webhook signature
const event = constructWebhookEvent(body, signature);
if (!event) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 400 });
}
// Get Supabase client
const supabase = createServerClient();
if (!supabase) {
console.error('Supabase not configured');
return NextResponse.json({ error: 'Database not configured' }, { status: 503 });
}
try {
// Only handle checkout.session.completed
if (event.type === 'checkout.session.completed') {
await handleCheckoutCompleted(event.data.object as Stripe.Checkout.Session, supabase);
} else {
console.log(`Ignoring event thandleCheckoutCompleted function · typescript · L55-L96 (42 LOC)app/api/stripe/webhooks/route.ts
async function handleCheckoutCompleted(
session: Stripe.Checkout.Session,
supabase: NonNullable<ReturnType<typeof createServerClient>>
) {
// Get user ID from metadata
const userId = session.metadata?.userId;
if (!userId) {
console.error('No userId in checkout session metadata');
return;
}
// Get line items to find price ID
const lineItems = await stripe?.checkout.sessions.listLineItems(session.id);
const priceId = lineItems?.data[0]?.price?.id;
if (!priceId) {
console.error('No price ID found in checkout session');
return;
}
// Look up bundle from price ID
const bundleType = getBundleFromPriceId(priceId);
if (!bundleType) {
console.error('Unknown price ID:', priceId);
return;
}
// Get payment intent ID for idempotency
const paymentId =
typeof session.payment_intent === 'string'
? session.payment_intent
: session.payment_intent?.id || session.id;
// Add credits to user
const success = await addCredits(uslogBillingEvent function · typescript · L101-L124 (24 LOC)app/api/stripe/webhooks/route.ts
async function logBillingEvent(
event: Stripe.Event,
supabase: NonNullable<ReturnType<typeof createServerClient>>
) {
const eventData = event.data.object as {
metadata?: Record<string, string>;
amount_total?: number;
};
const userId = eventData.metadata?.userId || null;
const amountCents = eventData.amount_total || null;
await supabase.from('billing_events').insert({
user_id: userId,
event_type: event.type,
stripe_event_id: event.id,
amount_cents: amountCents,
currency: 'usd',
metadata: {
stripeEventType: event.type,
livemode: event.livemode,
},
});
}POST function · typescript · L5-L113 (109 LOC)app/api/sync/fix-duplicates/route.ts
export async function POST(request: NextRequest) {
try {
const supabase = createServerClient();
if (!supabase) {
return NextResponse.json({ error: 'Database not configured' }, { status: 503 });
}
const { company } = await request.json();
// Get applications
const { data: apps } = await supabase
.from('applications')
.select('id, company, role, status, created_at')
.ilike('company', `%${company}%`)
.order('created_at', { ascending: true });
if (!apps || apps.length < 2) {
return NextResponse.json({ error: 'Need at least 2 applications to fix duplicates' }, { status: 400 });
}
// Get all emails for these apps
const { data: allEmails } = await supabase
.from('application_emails')
.select('*')
.in('application_id', apps.map(a => a.id));
if (!allEmails) {
return NextResponse.json({ error: 'No emails found' }, { status: 404 });
}
// Group emails by gmail_message_id to find POST function · typescript · L17-L98 (82 LOC)app/api/sync/fix-emails/route.ts
export async function POST(request: NextRequest) {
try {
const supabase = createServerClient();
if (!supabase) {
return NextResponse.json({ error: 'Database not configured' }, { status: 503 });
}
const { company } = await request.json();
if (!company) {
return NextResponse.json({ error: 'Company name required' }, { status: 400 });
}
// Get all applications for this company
const { data: apps } = await supabase
.from('applications')
.select('id, company, role, status')
.ilike('company', `%${company}%`);
if (!apps || apps.length === 0) {
return NextResponse.json({ error: 'No applications found', company }, { status: 404 });
}
console.log(`Found ${apps.length} applications for "${company}":`, apps);
// Get all emails linked to these applications
const { data: emails } = await supabase
.from('application_emails')
.select('*')
.in('application_id', apps.map(a => a.id));
if POST function · typescript · L6-L76 (71 LOC)app/api/sync/reset/route.ts
export async function POST(request: NextRequest) {
try {
const supabase = createServerClient();
if (!supabase) {
return NextResponse.json({ error: 'Database not configured' }, { status: 503 });
}
const { company } = await request.json();
if (!company) {
return NextResponse.json({ error: 'Company name required' }, { status: 400 });
}
// Find emails linked to this company's applications
const { data: apps } = await supabase
.from('applications')
.select('id, company, role, status, source_email_id')
.ilike('company', `%${company}%`);
if (!apps || apps.length === 0) {
return NextResponse.json({ error: 'No applications found for company', company }, { status: 404 });
}
console.log(`Found ${apps.length} applications for "${company}":`, apps);
// Get email IDs to reset
const emailIds = apps
.filter(app => app.source_email_id)
.map(app => app.source_email_id);
// Also get emails froMethodology: Repobility · https://repobility.com/research/state-of-ai-code-2026/
normalizeCompanyName function · typescript · L13-L19 (7 LOC)app/api/sync/route.ts
function normalizeCompanyName(name: string): string {
return name
.toLowerCase()
.replace(/\s+(inc|llc|ltd|co|corp|corporation|ai|io|hq)\.?$/i, '')
.replace(/[^a-z0-9]/g, '')
.trim();
}companiesMatch function · typescript · L22-L33 (12 LOC)app/api/sync/route.ts
function companiesMatch(name1: string, name2: string): boolean {
const norm1 = normalizeCompanyName(name1);
const norm2 = normalizeCompanyName(name2);
// Exact match after normalization
if (norm1 === norm2) return true;
// One contains the other (handles "Cleo" vs "Cleo AI")
if (norm1.includes(norm2) || norm2.includes(norm1)) return true;
return false;
}normalizeRole function · typescript · L36-L42 (7 LOC)app/api/sync/route.ts
function normalizeRole(role: string): string {
return role
.toLowerCase()
.replace(/[^a-z0-9\s]/g, ' ')
.replace(/\s+/g, ' ')
.trim();
}rolesMatch function · typescript · L45-L64 (20 LOC)app/api/sync/route.ts
function rolesMatch(role1: string | null, role2: string | null): boolean {
if (!role1 || !role2) return false;
const norm1 = normalizeRole(role1);
const norm2 = normalizeRole(role2);
// Exact match after normalization
if (norm1 === norm2) return true;
// One contains the other (handles slight variations)
if (norm1.includes(norm2) || norm2.includes(norm1)) return true;
// Check if key words match (at least 2 significant words)
const words1 = norm1.split(' ').filter(w => w.length > 2);
const words2 = norm2.split(' ').filter(w => w.length > 2);
const commonWords = words1.filter(w => words2.includes(w));
if (commonWords.length >= 2) return true;
return false;
}GET function · typescript · L342-L364 (23 LOC)app/api/sync/route.ts
export async function GET() {
try {
const supabase = createServerClient();
if (!supabase) {
return NextResponse.json({ connected: false, error: 'Database not configured' });
}
const { data, error } = await supabase
.from('gmail_tokens')
.select('id, updated_at')
.single();
console.log('Gmail status check:', { hasData: !!data, error: error?.message });
return NextResponse.json({
connected: !!data,
lastSync: data?.updated_at || null,
});
} catch (err) {
console.error('Gmail status error:', err);
return NextResponse.json({ connected: false });
}
}DashboardLayout function · typescript · L3-L9 (7 LOC)app/(app)/layout.tsx
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return <AppLayout>{children}</AppLayout>;
}AppLayout function · typescript · L10-L28 (19 LOC)app/components/AppLayout.tsx
export function AppLayout({ children }: AppLayoutProps) {
const [, setIsCollapsed] = useState(false);
const handleCollapseChange = useCallback((collapsed: boolean) => {
setIsCollapsed(collapsed);
}, []);
return (
<div className="flex h-screen overflow-hidden bg-white dark:bg-slate-950">
{/* Side Navigation */}
<SideNav onCollapseChange={handleCollapseChange} />
{/* Main Content */}
<main className="flex-1 overflow-auto bg-slate-50 dark:bg-slate-950">
{children}
</main>
</div>
);
}ApplicationCard function · typescript · L27-L181 (155 LOC)app/components/ApplicationCard.tsx
export function ApplicationCard({ application, index, onClick }: ApplicationCardProps) {
const formatDate = (dateString: string) => {
const date = new Date(dateString);
const now = new Date();
const diffDays = Math.floor((now.getTime() - date.getTime()) / (1000 * 60 * 60 * 24));
if (diffDays === 0) return 'Today';
if (diffDays === 1) return 'Yesterday';
if (diffDays < 7) return `${diffDays}d ago`;
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
};
const statusColor = STATUS_COLORS[application.status];
const closeReasonStyle = application.closeReason ? CLOSE_REASON_STYLES[application.closeReason] : null;
return (
<Draggable draggableId={application.id} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
onClick={() => onClick(application)}
data-testid="application-card"
All rows above produced by Repobility · https://repobility.com
BuyCreditsModal function · typescript · L22-L167 (146 LOC)app/components/billing/BuyCreditsModal.tsx
export function BuyCreditsModal({ isOpen, onClose, currentCredits }: BuyCreditsModalProps) {
const [loadingBundle, setLoadingBundle] = useState<CreditBundle | null>(null);
const [error, setError] = useState<string | null>(null);
const handlePurchase = async (bundle: CreditBundle) => {
const priceId = STRIPE_PRICES[bundle];
if (!priceId) {
setError('Payment not configured. Please try again later.');
return;
}
setLoadingBundle(bundle);
setError(null);
try {
const response = await fetch('/api/stripe/create-checkout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ priceId }),
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'Failed to start checkout');
}
if (data.url) {
// Redirect to Stripe checkout
window.location.href = data.url;
} else {
thCreditSkeleton function · typescript · L38-L61 (24 LOC)app/components/billing/CreditBalance.tsx
function CreditSkeleton({ variant }: { variant: 'sidebar' | 'header' | 'compact' }) {
if (variant === 'compact') {
return <div className="h-4 w-6 bg-slate-200 dark:bg-slate-700 rounded animate-pulse" />;
}
if (variant === 'header') {
return (
<div className="flex items-center gap-1.5">
<div className="w-4 h-4 bg-slate-200 dark:bg-slate-700 rounded animate-pulse" />
<div className="h-4 w-6 bg-slate-200 dark:bg-slate-700 rounded animate-pulse" />
</div>
);
}
// sidebar variant
return (
<div className="flex items-center justify-between px-2.5 py-2 w-full">
<div className="flex items-center gap-2">
<div className="w-5 h-5 bg-slate-200 dark:bg-slate-700 rounded animate-pulse" />
<div className="h-4 w-24 bg-slate-200 dark:bg-slate-700 rounded animate-pulse" />
</div>
</div>
);
}CreditBalance function · typescript · L63-L219 (157 LOC)app/components/billing/CreditBalance.tsx
export function CreditBalance({
variant = 'sidebar',
showBuyButton = true,
onBuyClick
}: CreditBalanceProps) {
const [balance, setBalance] = useState<CreditBalanceData | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const fetchCredits = useCallback(async () => {
try {
const response = await fetch('/api/credits');
if (!response.ok) {
if (response.status === 401) {
setError('Not logged in');
return;
}
throw new Error('Failed to fetch credits');
}
const data = await response.json();
setBalance(data);
setError(null);
} catch (err) {
console.error('Error fetching credits:', err);
setError('Could not load credits');
} finally {
setIsLoading(false);
}
}, []);
// Fetch on mount
useEffect(() => {
fetchCredits();
}, [fetchCredits]);
// Re-fetch when window regains focus
useEffWarningIcon function · typescript · L11-L27 (17 LOC)app/components/billing/LowCreditsWarning.tsx
function WarningIcon({ className = '' }: { className?: string }) {
return (
<svg
className={className}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
);
}CloseIcon function · typescript · L29-L45 (17 LOC)app/components/billing/LowCreditsWarning.tsx
function CloseIcon({ className = '' }: { className?: string }) {
return (
<svg
className={className}
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
);
}getMessage function · typescript · L47-L55 (9 LOC)app/components/billing/LowCreditsWarning.tsx
function getMessage(credits: number): string {
if (credits === 0) {
return "You're out of credits! Buy more to generate CVs.";
}
if (credits === 1) {
return 'Only 1 credit left!';
}
return 'Running low on credits';
}LowCreditsWarning function · typescript · L57-L241 (185 LOC)app/components/billing/LowCreditsWarning.tsx
export function LowCreditsWarning({
credits,
onBuyClick,
variant = 'banner',
}: LowCreditsWarningProps) {
const [isDismissed, setIsDismissed] = useState(false);
// Only show when credits <= 3
if (credits > 3 || isDismissed) {
return null;
}
const message = getMessage(credits);
const isUrgent = credits === 0;
// Banner variant - full-width yellow/amber bar
if (variant === 'banner') {
return (
<div
className={`
w-full px-4 py-3
${isUrgent
? 'bg-red-50 dark:bg-red-900/30 border-b border-red-200 dark:border-red-800'
: 'bg-amber-50 dark:bg-amber-900/30 border-b border-amber-200 dark:border-amber-800'
}
`}
role="alert"
data-testid="low-credits-warning"
>
<div className="flex items-center justify-between max-w-7xl mx-auto">
<div className="flex items-center gap-3">
<WarningIcon
className={`w-5 h-5 shrink-0 ${
ChatMessage function · typescript · L13-L75 (63 LOC)app/components/ChatMessage.tsx
export function ChatMessage({ message, onFollowupSelect, isLatest = false }: ChatMessageProps) {
const isUser = message.role === 'user';
return (
<div className={`flex ${isUser ? 'justify-end' : 'justify-start'} mb-4`}>
<div
className={`
max-w-[85%]
${isUser
? 'bg-blue-600 text-white rounded-2xl rounded-br-md px-4 py-2.5'
: 'bg-slate-100 dark:bg-slate-800 text-slate-900 dark:text-slate-100 rounded-2xl rounded-bl-md px-4 py-3'
}
`}
>
{/* Message content */}
{isUser ? (
<div className="text-sm leading-relaxed whitespace-pre-wrap">
{message.content}
</div>
) : (
<div className="text-[13px] leading-relaxed [&>h1]:text-base [&>h1]:font-bold [&>h1]:mt-3 [&>h1]:mb-1.5 [&>h2]:text-sm [&>h2]:font-semibold [&>h2]:mt-3 [&>h2]:mb-1 [&>h3]:text-[13px] [&>h3]:font-semibold [&>h3]:mt-2 [&>h3]:mb-1 [&>p]:my-1.5 [&>ul]:my-1.5 [&>ul]:pl-4 [&>ul>li]:Repobility (the analyzer behind this table) · https://repobility.com
formatRelativeDate function · typescript · L11-L23 (13 LOC)app/components/CVCard.tsx
function formatRelativeDate(dateString: string): string {
const date = new Date(dateString);
const now = new Date();
const diffTime = now.getTime() - date.getTime();
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
if (diffDays === 0) return 'Today';
if (diffDays === 1) return 'Yesterday';
if (diffDays < 7) return `${diffDays} days ago`;
if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago`;
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
}CVCard function · typescript · L25-L101 (77 LOC)app/components/CVCard.tsx
export default function CVCard({ cv, onPreview, onDelete }: CVCardProps) {
const isTailored = cv.type === 'tailored';
return (
<div
onClick={() => onPreview(cv)}
className="group relative bg-white border border-gray-200 rounded-xl p-4 hover:border-gray-300 hover:shadow-md transition-all duration-200 cursor-pointer"
>
{/* Delete button */}
{isTailored && onDelete && (
<button
onClick={(e) => {
e.stopPropagation();
onDelete(cv);
}}
className="absolute top-3 right-3 p-1.5 text-gray-400 hover:text-red-500 hover:bg-red-50 rounded-lg opacity-0 group-hover:opacity-100 transition-all duration-200"
title="Delete CV"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
)}
{/* PDF Badge */}
CVPreviewModal function · typescript · L11-L116 (106 LOC)app/components/CVPreviewModal.tsx
export default function CVPreviewModal({ cv, onClose }: CVPreviewModalProps) {
// Handle escape key
const handleKeyDown = useCallback((e: KeyboardEvent) => {
if (e.key === 'Escape') {
onClose();
}
}, [onClose]);
useEffect(() => {
document.addEventListener('keydown', handleKeyDown);
document.body.style.overflow = 'hidden';
return () => {
document.removeEventListener('keydown', handleKeyDown);
document.body.style.overflow = 'unset';
};
}, [handleKeyDown]);
// Handle backdrop click
const handleBackdropClick = (e: React.MouseEvent) => {
if (e.target === e.currentTarget) {
onClose();
}
};
const handleDownload = () => {
if (cv.url) {
window.open(cv.url, '_blank');
}
};
return (
<div
className="fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4"
onClick={handleBackdropClick}
>
<div className="bg-white rounded-2xl shadow-2xl w-full max-w-4xEmptyState function · typescript · L11-L193 (183 LOC)app/components/EmptyState.tsx
export function EmptyState({ onConnectGmail, onAddApplication, gmailConnected }: EmptyStateProps) {
const [isVisible, setIsVisible] = useState(false);
const [hoveredButton, setHoveredButton] = useState<'gmail' | 'add' | null>(null);
useEffect(() => {
const timer = setTimeout(() => setIsVisible(true), 100);
return () => clearTimeout(timer);
}, []);
return (
<div
className={`flex flex-col items-center justify-center py-16 px-6 max-w-lg mx-auto transition-all duration-500 ${
isVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4'
}`}
>
{/* Animated Illustration */}
<div className="relative mb-8">
{/* Background circles with subtle animation */}
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-40 h-40 rounded-full bg-gradient-to-br from-blue-100 to-purple-100 dark:from-blue-900/20 dark:to-purple-900/20 animate-pulse" style={{ animationDuration: '3s' }} />
KanbanColumn function · typescript · L74-L169 (96 LOC)app/components/KanbanColumn.tsx
export function KanbanColumn({
id,
title,
applications,
onCardClick,
isMobile = false,
}: KanbanColumnProps) {
const config = COLUMN_CONFIG[id];
return (
<div
className={`
flex flex-col
${isMobile ? 'w-full h-full' : 'w-72 shrink-0'}
bg-slate-50/50 dark:bg-slate-900/50
rounded-xl
transition-colors duration-200
`}
>
{/* Column Header */}
{!isMobile && (
<div className="px-3 py-3 flex items-center justify-between">
<div className="flex items-center gap-2">
<span className={`w-2 h-2 rounded-full ${config.dotColor}`} />
<h2 className="font-medium text-slate-700 dark:text-slate-200 text-sm">
{title}
</h2>
</div>
<span
className={`
text-xs font-medium
px-2 py-0.5 rounded-full
${config.countBg} ${config.countText}
tabular-nums
`}
>
useRevealOnScroll function · typescript · L8-L31 (24 LOC)app/components/landing/LandingComponents.tsx
export function useRevealOnScroll(threshold = 0.1) {
const ref = useRef<HTMLDivElement>(null);
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsVisible(true);
observer.disconnect();
}
},
{ threshold }
);
if (ref.current) {
observer.observe(ref.current);
}
return () => observer.disconnect();
}, [threshold]);
return { ref, isVisible };
}LandingNav function · typescript · L41-L101 (61 LOC)app/components/landing/LandingComponents.tsx
export function LandingNav() {
const [scrolled, setScrolled] = useState(false);
useEffect(() => {
const handleScroll = () => setScrolled(window.scrollY > 20);
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
return (
<nav className={`
fixed top-0 left-0 right-0 z-50
px-6 transition-all duration-200
${scrolled
? 'py-3 bg-white/95 backdrop-blur-lg border-b border-neutral-200/50 shadow-sm'
: 'py-4 bg-white/80 backdrop-blur-lg border-b border-neutral-200/30'
}
`}>
<div className="max-w-7xl mx-auto flex items-center justify-between">
{/* Logo */}
<a href="/" className="flex items-center gap-1.5">
<svg className="w-6 h-6" viewBox="0 0 24 24" fill="none">
<path d="M3 18C3 10 7 4 12 4C17 4 21 10 21 18" stroke="url(#logo-gradient-nav)" strokeWidth="1.5" strokeLinecap="round"/>
<path d="M6 17C6 11 8.NavLink function · typescript · L103-L112 (10 LOC)app/components/landing/LandingComponents.tsx
function NavLink({ href, children }: NavLinkProps) {
return (
<a
href={href}
className="text-sm text-neutral-600 hover:text-neutral-900 transition-colors"
>
{children}
</a>
);
}Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
Hero function · typescript · L126-L242 (117 LOC)app/components/landing/LandingComponents.tsx
export function Hero({
badge,
headline,
subheadline,
primaryCTA,
secondaryCTA,
variant = 'light'
}: HeroProps) {
const isDark = variant === 'dark';
return (
<section className={`relative min-h-screen flex items-center overflow-hidden ${isDark ? 'bg-neutral-950' : ''}`}>
{/* Background */}
{isDark ? (
<div className="absolute inset-0">
<div className="absolute top-1/4 left-1/2 -translate-x-1/2 w-[800px] h-[600px] rounded-full bg-gradient-to-br from-primary-600/30 to-info-600/20 blur-3xl" />
</div>
) : (
<>
<div className="absolute inset-0 bg-gradient-to-br from-primary-50 via-white to-info-50" />
<div
className="absolute inset-0 opacity-80"
style={{
background: `
radial-gradient(at 20% 30%, rgba(139, 92, 246, 0.12) 0%, transparent 50%),
radial-gradient(at 80% 70%, rgba(59, 130, 246, 0.08) 0%, transparent 50%)
`
TrustSignal function · typescript · L244-L253 (10 LOC)app/components/landing/LandingComponents.tsx
function TrustSignal({ children }: { children: ReactNode }) {
return (
<div className="flex items-center gap-2 text-sm text-neutral-500">
<svg className="w-5 h-5 text-success-500" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
{children}
</div>
);
}Section function · typescript · L264-L272 (9 LOC)app/components/landing/LandingComponents.tsx
export function Section({ id, className = '', children }: SectionProps) {
return (
<section id={id} className={`py-16 md:py-24 lg:py-32 ${className}`}>
<div className="max-w-7xl mx-auto px-6">
{children}
</div>
</section>
);
}SectionHeader function · typescript · L284-L312 (29 LOC)app/components/landing/LandingComponents.tsx
export function SectionHeader({ overline, title, description, centered = true }: SectionHeaderProps) {
const { ref, isVisible } = useRevealOnScroll();
return (
<div
ref={ref}
className={`
mb-12 md:mb-16
${centered ? 'text-center max-w-3xl mx-auto' : 'max-w-2xl'}
transition-all duration-500
${isVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-5'}
`}
>
{overline && (
<span className="text-sm font-semibold text-primary-600 uppercase tracking-wider">
{overline}
</span>
)}
<h2 className="mt-2 text-3xl md:text-4xl font-bold tracking-tight text-neutral-900">
{title}
</h2>
{description && (
<p className="mt-4 text-lg text-neutral-600">
{description}
</p>
)}
</div>
);
}FeatureCard function · typescript · L324-L353 (30 LOC)app/components/landing/LandingComponents.tsx
export function FeatureCard({ icon, title, description, delay = 0 }: FeatureCardProps) {
const { ref, isVisible } = useRevealOnScroll();
return (
<div
ref={ref}
className={`
group p-6 md:p-8
bg-white border border-neutral-200 rounded-2xl
hover:shadow-lg hover:border-primary-200
transition-all duration-300
${isVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-5'}
`}
style={{ transitionDelay: `${delay}ms` }}
>
<div className="
w-12 h-12 mb-4
flex items-center justify-center
bg-primary-50 text-primary-600
rounded-xl
group-hover:bg-primary-100
transition-colors duration-300
">
{icon}
</div>
<h3 className="text-lg font-semibold text-neutral-900 mb-2">{title}</h3>
<p className="text-neutral-600 leading-relaxed">{description}</p>
</div>
);
}StatsGrid function · typescript · L363-L385 (23 LOC)app/components/landing/LandingComponents.tsx
export function StatsGrid({ stats }: { stats: StatProps[] }) {
const { ref, isVisible } = useRevealOnScroll();
return (
<div
ref={ref}
className={`
grid grid-cols-2 md:grid-cols-4 gap-8
transition-all duration-500
${isVisible ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-5'}
`}
>
{stats.map((stat, index) => (
<div key={index} className="text-center">
<p className="text-4xl md:text-5xl font-bold text-primary-600 font-mono tracking-tight">
{stat.value}
</p>
<p className="text-neutral-600 mt-1">{stat.label}</p>
</div>
))}
</div>
);
}TestimonialCard function · typescript · L399-L426 (28 LOC)app/components/landing/LandingComponents.tsx
export function TestimonialCard({ quote, author }: TestimonialProps) {
return (
<div className="p-8 bg-neutral-50 border border-neutral-100 rounded-2xl">
<p className="text-lg text-neutral-700 leading-relaxed italic mb-6">
"{quote}"
</p>
<div className="flex items-center gap-4">
{author.avatar ? (
<img
src={author.avatar}
alt={author.name}
className="w-12 h-12 rounded-full object-cover"
/>
) : (
<div className="w-12 h-12 rounded-full bg-primary-100 flex items-center justify-center">
<span className="text-primary-700 font-semibold">
{author.name.charAt(0)}
</span>
</div>
)}
<div>
<p className="font-semibold text-neutral-900">{author.name}</p>
<p className="text-sm text-neutral-500">{author.role}</p>
</div>
</div>
</div>
);
}CTASection function · typescript · L438-L456 (19 LOC)app/components/landing/LandingComponents.tsx
export function CTASection({ title, description, ctaText, ctaHref }: CTASectionProps) {
return (
<section className="py-16 md:py-24 bg-gradient-to-br from-primary-600 to-primary-700 text-white">
<div className="max-w-4xl mx-auto px-6 text-center">
<h2 className="text-3xl md:text-4xl font-bold mb-4">{title}</h2>
<p className="text-lg opacity-90 mb-8 max-w-2xl mx-auto">{description}</p>
<a
href={ctaHref}
className="inline-flex items-center justify-center gap-2 px-8 py-4 bg-white text-primary-700 font-semibold rounded-xl hover:bg-neutral-100 transition-colors shadow-lg"
>
{ctaText}
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M13 7l5 5m0 0l-5 5m5-5H6" />
</svg>
</a>
</div>
</section>
);
}Methodology: Repobility · https://repobility.com/research/state-of-ai-code-2026/
Badge function · typescript · L476-L482 (7 LOC)app/components/landing/LandingComponents.tsx
export function Badge({ variant = 'primary', children }: BadgeProps) {
return (
<span className={`px-2.5 py-1 text-xs font-medium rounded-full ${badgeStyles[variant]}`}>
{children}
</span>
);
}Button function · typescript · L511-L543 (33 LOC)app/components/landing/LandingComponents.tsx
export function Button({
variant = 'primary',
size = 'md',
children,
onClick,
href,
className = ''
}: ButtonProps) {
const baseClasses = `
inline-flex items-center justify-center gap-2
font-medium rounded-lg
transition-all duration-150
active:scale-[0.98]
focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2
${buttonVariants[variant]}
${buttonSizes[size]}
${className}
`;
if (href) {
return (
<a href={href} className={baseClasses}>
{children}
</a>
);
}
return (
<button onClick={onClick} className={baseClasses}>
{children}
</button>
);
}LandingFooter function · typescript · L548-L623 (76 LOC)app/components/landing/LandingComponents.tsx
export function LandingFooter() {
return (
<footer className="py-16 bg-neutral-50 border-t border-neutral-200">
<div className="max-w-7xl mx-auto px-6">
<div className="grid grid-cols-1 md:grid-cols-4 gap-8 mb-12">
{/* Brand */}
<div className="md:col-span-1">
<a href="/" className="flex items-center gap-1.5 mb-4">
<svg className="w-6 h-6" viewBox="0 0 24 24" fill="none">
<path d="M3 18C3 10 7 4 12 4C17 4 21 10 21 18" stroke="url(#logo-gradient-footer)" strokeWidth="1.5" strokeLinecap="round"/>
<path d="M6 17C6 11 8.5 6.5 12 6.5C15.5 6.5 18 11 18 17" stroke="url(#logo-gradient-footer)" strokeWidth="1.5" strokeLinecap="round"/>
<path d="M9 16C9 12 10.5 9 12 9C13.5 9 15 12 15 16" stroke="url(#logo-gradient-footer)" strokeWidth="1.5" strokeLinecap="round"/>
<defs>
<linearGradient id="logo-gradient-footer" x1="3" y1="4" x2="21" y2="18" gradientSkeletonCard function · typescript · L3-L12 (10 LOC)app/components/LoadingSkeleton.tsx
function SkeletonCard() {
return (
<div className="bg-white rounded-lg border border-gray-200 p-3 mb-2 animate-pulse">
<div className="h-5 bg-gray-200 rounded w-3/4 mb-2" />
<div className="h-4 bg-gray-100 rounded w-1/2 mb-1" />
<div className="h-3 bg-gray-100 rounded w-1/3" />
<div className="mt-2 h-3 bg-gray-100 rounded w-16" />
</div>
);
}SkeletonColumn function · typescript · L14-L30 (17 LOC)app/components/LoadingSkeleton.tsx
function SkeletonColumn() {
return (
<div className="flex flex-col w-72 shrink-0 bg-gray-50 rounded-lg border border-gray-200">
<div className="p-3 border-b border-gray-200">
<div className="flex items-center justify-between">
<div className="h-5 bg-gray-200 rounded w-24 animate-pulse" />
<div className="h-4 bg-gray-200 rounded w-6 animate-pulse" />
</div>
</div>
<div className="flex-1 p-2 min-h-[200px]">
<SkeletonCard />
<SkeletonCard />
<SkeletonCard />
</div>
</div>
);
}LoadingSkeleton function · typescript · L32-L53 (22 LOC)app/components/LoadingSkeleton.tsx
export function LoadingSkeleton({ count = 4 }: { count?: number }) {
return (
<div className="h-screen flex flex-col bg-gray-100">
<header className="bg-white border-b border-gray-200 px-6 py-4">
<div className="flex items-center justify-between">
<div>
<div className="h-6 bg-gray-200 rounded w-32 mb-2 animate-pulse" />
<div className="h-4 bg-gray-100 rounded w-48 animate-pulse" />
</div>
<div className="h-10 bg-blue-200 rounded-lg w-28 animate-pulse" />
</div>
</header>
<div className="flex-1 overflow-x-auto p-6">
<div className="flex gap-4 h-full">
{Array.from({ length: count }).map((_, i) => (
<SkeletonColumn key={i} />
))}
</div>
</div>
</div>
);
}LoadingSkeletonCards function · typescript · L55-L63 (9 LOC)app/components/LoadingSkeleton.tsx
export function LoadingSkeletonCards({ count = 3 }: { count?: number }) {
return (
<>
{Array.from({ length: count }).map((_, i) => (
<SkeletonCard key={i} />
))}
</>
);
}formatRelativeDate function · typescript · L11-L23 (13 LOC)app/components/MasterCVCard.tsx
function formatRelativeDate(dateString: string): string {
const date = new Date(dateString);
const now = new Date();
const diffTime = now.getTime() - date.getTime();
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
if (diffDays === 0) return 'Today';
if (diffDays === 1) return 'Yesterday';
if (diffDays < 7) return `${diffDays} days ago`;
if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago`;
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
}All rows above produced by Repobility · https://repobility.com
MasterCVCard function · typescript · L25-L86 (62 LOC)app/components/MasterCVCard.tsx
export default function MasterCVCard({ cv, onEdit, onAdd }: MasterCVCardProps) {
// Empty state - no master CV
if (!cv) {
return (
<div
onClick={onAdd}
className="relative bg-gradient-to-br from-amber-50 to-orange-50 border-2 border-dashed border-amber-200 rounded-xl p-4 hover:border-amber-300 hover:shadow-md transition-all duration-200 cursor-pointer min-h-[160px] flex flex-col items-center justify-center"
>
<div className="w-12 h-12 bg-amber-100 rounded-full flex items-center justify-center mb-3">
<svg className="w-6 h-6 text-amber-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
</svg>
</div>
<p className="text-sm font-medium text-amber-700 mb-1">Add Master CV</p>
<p className="text-xs text-amber-600/70 text-center">Your base CV for tailoring to jobs</p>
</div>
);
}
returNavItem function · typescript · L14-L82 (69 LOC)app/components/NavItem.tsx
export function NavItem({ icon, label, href, isActive, isCollapsed }: NavItemProps) {
return (
<Link
href={href}
className={`
relative group flex items-center
${isCollapsed ? 'justify-center' : 'justify-start'}
px-3 py-2.5
rounded-lg
transition-all duration-200 ease-out
${isActive
? 'bg-blue-600/10 text-blue-500'
: 'text-slate-400 hover:bg-slate-800 hover:text-slate-200'
}
`}
>
{/* Icon */}
<span className={`
flex-shrink-0 w-5 h-5
transition-transform duration-200
${isActive ? 'scale-110' : 'group-hover:scale-105'}
`}>
{icon}
</span>
{/* Label - hidden when collapsed */}
{!isCollapsed && (
<span className={`
ml-3 text-sm font-medium
transition-opacity duration-200
whitespace-nowrap
`}>
{label}
</span>
)}
{/* Active indicator */}
{isAcOnboardingCard function · typescript · L11-L29 (19 LOC)app/components/onboarding/OnboardingCard.tsx
export function OnboardingCard({ children, className = '' }: OnboardingCardProps) {
return (
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.3, ease: [0.4, 0, 0.2, 1] }}
className={`
bg-white
border border-gray-200
rounded-2xl
p-8
shadow-xl shadow-gray-200/50
${className}
`}
>
{children}
</motion.div>
);
}