Six bilingual patent-workflow checklists (UPC Statement of Claim, Defence, Confidentiality Application, Representative Registration; BPatG Nullity; EPO Opposition) with grouped items, rule references, and tips. Index page lists cards with regime filter and per-checklist progress; detail page persists check state in localStorage (patholo:checklist:<slug>), shows a live progress bar, supports reset and print, and submits feedback via Supabase checklisten_feedback.
82 lines
6.3 KiB
TypeScript
82 lines
6.3 KiB
TypeScript
import { h, Fragment } from "../jsx";
|
|
|
|
const ICON_HOME = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>';
|
|
const ICON_CALC = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="2" width="16" height="20" rx="2"/><line x1="8" y1="6" x2="16" y2="6"/><line x1="8" y1="14" x2="8" y2="14.01"/><line x1="12" y1="14" x2="12" y2="14.01"/><line x1="16" y1="14" x2="16" y2="14.01"/><line x1="8" y1="18" x2="16" y2="18"/></svg>';
|
|
const ICON_CLOCK = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>';
|
|
const ICON_DOWNLOAD = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>';
|
|
const ICON_LINK = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>';
|
|
const ICON_BOOK = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>';
|
|
const ICON_TABLE = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="3" x2="9" y2="21"/></svg>';
|
|
const ICON_CHECK = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>';
|
|
const ICON_GLOBE = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10A15.3 15.3 0 0 1 12 2z"/></svg>';
|
|
const ICON_LOGOUT = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>';
|
|
const ICON_PIN = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9 4v6l-2 4h10l-2-4V4"/><line x1="12" y1="16" x2="12" y2="21"/><line x1="8" y1="4" x2="16" y2="4"/></svg>';
|
|
const ICON_MENU = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="6" x2="20" y2="6"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="18" x2="20" y2="18"/></svg>';
|
|
|
|
interface SidebarProps {
|
|
currentPath: string;
|
|
}
|
|
|
|
function navItem(href: string, icon: string, i18nKey: string, label: string, currentPath: string): string {
|
|
const active = href === currentPath;
|
|
return (
|
|
<a href={href} className={`sidebar-item${active ? " active" : ""}`}>
|
|
<span className="sidebar-icon" dangerouslySetInnerHTML={{ __html: icon }} />
|
|
<span className="sidebar-label" data-i18n={i18nKey}>{label}</span>
|
|
</a>
|
|
);
|
|
}
|
|
|
|
export function Sidebar({ currentPath }: SidebarProps): string {
|
|
return (
|
|
<Fragment>
|
|
<aside className="sidebar">
|
|
<div className="sidebar-header">
|
|
<a href="/" className="sidebar-logo">
|
|
<span className="sidebar-icon"><span className="logo-mark">p</span></span>
|
|
<span className="sidebar-label logo-text">patHoLo</span>
|
|
</a>
|
|
<button className="sidebar-pin" type="button" aria-label="Pin sidebar">
|
|
<span dangerouslySetInnerHTML={{ __html: ICON_PIN }} />
|
|
</button>
|
|
</div>
|
|
|
|
<nav className="sidebar-nav">
|
|
{navItem("/", ICON_HOME, "nav.home", "Home", currentPath)}
|
|
{navItem("/tools/kostenrechner", ICON_CALC, "nav.kostenrechner", "Kostenrechner", currentPath)}
|
|
{navItem("/tools/fristenrechner", ICON_CLOCK, "nav.fristenrechner", "Fristenrechner", currentPath)}
|
|
{navItem("/tools/gebuehrentabellen", ICON_TABLE, "nav.gebuehrentabellen", "Gebührentabellen", currentPath)}
|
|
{navItem("/checklisten", ICON_CHECK, "nav.checklisten", "Checklisten", currentPath)}
|
|
{navItem("/glossar", ICON_BOOK, "nav.glossar", "Glossar", currentPath)}
|
|
{navItem("/downloads", ICON_DOWNLOAD, "nav.downloads", "Downloads", currentPath)}
|
|
{navItem("/links", ICON_LINK, "nav.links", "Links", currentPath)}
|
|
</nav>
|
|
|
|
<div className="sidebar-spacer" />
|
|
|
|
<div className="sidebar-bottom">
|
|
<div className="sidebar-item sidebar-lang-item">
|
|
<span className="sidebar-icon" dangerouslySetInnerHTML={{ __html: ICON_GLOBE }} />
|
|
<span className="sidebar-label">
|
|
<span className="sidebar-lang">
|
|
<button className="lang-btn lang-active" data-lang-toggle="de" type="button">DE</button>
|
|
<span className="lang-sep">/</span>
|
|
<button className="lang-btn" data-lang-toggle="en" type="button">EN</button>
|
|
</span>
|
|
</span>
|
|
</div>
|
|
<a href="/logout" className="sidebar-item sidebar-logout">
|
|
<span className="sidebar-icon" dangerouslySetInnerHTML={{ __html: ICON_LOGOUT }} />
|
|
<span className="sidebar-label" data-i18n="nav.logout">Abmelden</span>
|
|
</a>
|
|
</div>
|
|
</aside>
|
|
|
|
<button className="sidebar-hamburger" type="button" aria-label="Menu">
|
|
<span dangerouslySetInnerHTML={{ __html: ICON_MENU }} />
|
|
</button>
|
|
<div className="sidebar-overlay" />
|
|
</Fragment>
|
|
);
|
|
}
|