85 lines
3.2 KiB
TypeScript
85 lines
3.2 KiB
TypeScript
import { Link } from 'react-router-dom';
|
|||
|
|
import { Calculator, BarChart3, Clock, FileText } from 'lucide-react';
|
||
|
|
import type { ComponentType } from 'react';
|
||
|
|
import { PublicLayout, PublicHero } from '@/components/public/PublicLayout';
|
||
|
|
import { useToolsOnline, type ToolName } from '@/hooks/useToolUsage';
|
||
|
|
|
||
|
|
interface Tool {
|
||
|
|
slug: ToolName;
|
||
|
|
title: string;
|
||
|
|
description: string;
|
||
|
|
icon: ComponentType<{ className?: string }>;
|
||
|
|
}
|
||
|
|
|
||
|
|
const TOOLS: Tool[] = [
|
||
|
|
{
|
||
|
|
slug: 'hourly-rate-calculator',
|
||
|
|
title: 'Hourly Rate Calculator',
|
||
|
|
description:
|
||
|
|
'Work backwards from your target take-home to find the hourly rate you actually need to charge.',
|
||
|
|
icon: Calculator,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
slug: 'case-profitability',
|
||
|
|
title: 'Case Profitability Analyzer',
|
||
|
|
description: 'Plug in hours, rates, and case expenses to see whether a matter is making you money.',
|
||
|
|
icon: BarChart3,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
slug: 'billable-hours-tracker',
|
||
|
|
title: 'Billable Hours Tracker',
|
||
|
|
description: 'A no-signup timer with manual entries and CSV export. Saved locally to your browser.',
|
||
|
|
icon: Clock,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
slug: 'document-templates',
|
||
|
|
title: 'Document Templates',
|
||
|
|
description: 'Fill in a few fields, download a clean text version of common law-firm documents.',
|
||
|
|
icon: FileText,
|
||
|
|
},
|
||
|
|
];
|
||
|
|
|
||
|
|
export default function ToolsIndexPage() {
|
||
|
|
const online = useToolsOnline();
|
||
|
|
|
||
|
|
return (
|
||
|
|
<PublicLayout>
|
||
|
|
<PublicHero
|
||
|
|
eyebrow="Free Legal Tools"
|
||
|
|
title="Practical calculators for your practice"
|
||
|
|
description="No signup, no email. Use them as much as you like. If you want everything in one place, that's what eLegal Software is for."
|
||
|
|
/>
|
||
|
|
|
||
|
|
<section className="container py-16">
|
||
|
|
<div className="grid gap-5 md:grid-cols-2">
|
||
|
|
{TOOLS.map((tool) => {
|
||
|
|
const count = online.data?.online[tool.slug] ?? 0;
|
||
|
|
return (
|
||
|
|
<Link
|
||
|
|
key={tool.slug}
|
||
|
|
to={`/tools/${tool.slug}`}
|
||
|
|
className="group rounded-2xl border border-ink-100 bg-white p-6 hover:border-brand-200 hover:shadow-lg hover:shadow-brand-500/5 transition flex flex-col"
|
||
|
|
>
|
||
|
|
<div className="flex items-start justify-between">
|
||
|
|
<div className="grid h-12 w-12 place-items-center rounded-xl bg-brand-50 text-brand-600 group-hover:bg-brand-500 group-hover:text-white transition">
|
||
|
|
<tool.icon className="h-5 w-5" />
|
||
|
|
</div>
|
||
|
|
<span className="inline-flex items-center gap-1.5 rounded-full bg-emerald-50 text-emerald-700 px-2 py-0.5 text-[11px] font-semibold">
|
||
|
|
<span className="h-1.5 w-1.5 rounded-full bg-emerald-500 animate-pulse" />
|
||
|
|
{count} online
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
<h3 className="mt-5 text-lg font-semibold text-ink-900">{tool.title}</h3>
|
||
|
|
<p className="mt-2 text-sm leading-relaxed text-ink-600">{tool.description}</p>
|
||
|
|
<span className="mt-4 text-sm font-semibold text-brand-600 group-hover:text-brand-700">
|
||
|
|
Open tool →
|
||
|
|
</span>
|
||
|
|
</Link>
|
||
|
|
);
|
||
|
|
})}
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
</PublicLayout>
|
||
|
|
);
|
||
|
|
}
|