Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
3cc1c98
refactor: improve architecture with custom hooks and better state man…
claude Jan 7, 2026
fab88f6
chore: update package-lock.json
claude Jan 7, 2026
e4986d9
ci: add GitHub Pages deployment with PR previews
claude Jan 7, 2026
1d3496c
feat: make Issued date editable via calendar picker
claude Jan 7, 2026
e68435a
feat: add JWT templates management
claude Jan 7, 2026
1676861
feat: add 'Save as template' with expiry safety warning
claude Jan 7, 2026
f02fb01
feat: add verbose AK validation and rename JWT to activation key
claude Jan 7, 2026
3faa06c
feat: improve 'Save as template' with overwrite and expiry options
claude Jan 7, 2026
fba480c
feat: add 'Load in Editor' button to template cards
claude Jan 7, 2026
5592f94
refactor: redesign Templates page with improved UX and visual hierarchy
claude Jan 7, 2026
ef14241
refactor: extract generic hooks for localStorage and CRUD state
claude Jan 7, 2026
60d9daf
fix: wait for templates to load before opening in editor
claude Jan 7, 2026
6280470
feat: redesign editor UX with template switcher and better key selection
claude Jan 7, 2026
155d59c
fix: move validation indicator beside template selector and add label
claude Jan 7, 2026
711a1e3
style: freshen up UI with professional accent colors
claude Jan 7, 2026
e5cae4c
chore: remove GitHub Pages workflows (using Cloudflare Pages)
claude Jan 7, 2026
1060d9b
fix: add Cloudflare Pages redirects for SPA routing
claude Jan 7, 2026
92741cb
feat: add footer with Beshu, ReadonlyREST, and Anaphora links
claude Jan 7, 2026
f3b3b4e
fix: address PR review feedback
claude Jan 7, 2026
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
3 changes: 3 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions public/_redirects
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* /index.html 200
146 changes: 100 additions & 46 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,88 +1,142 @@
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import './App.css';
import { Key, ShieldCheck } from 'lucide-react';
import { ThemeProvider } from './hooks/use-theme'
import { Key, ShieldCheck, FileText, ExternalLink } from 'lucide-react';
import { ThemeProvider } from './hooks/use-theme';
import { ToastProvider } from './hooks/use-toast';
import { ThemeToggle } from './components/ui/theme-toggle';

// Import page components
import Keys from './components/pages/Keys';
import Templates from './components/pages/Templates';
import ActivationKeyEditor from './components/pages/ActivationKeyEditor';

function App() {
const menuItems = [
{ title: "Activation Key Editor", icon: ShieldCheck, path: "/" },
{ title: "Editor", icon: ShieldCheck, path: "/" },
{ title: "Templates", icon: FileText, path: "/templates" },
{ title: "Keys", icon: Key, path: "/keys" },
];

return (
<ThemeProvider defaultTheme="system" storageKey="ak-tools-theme">
<ToastProvider>
<Router>
<div className="min-h-screen">
<nav className="border-b">
<div className="min-h-screen page-pattern flex flex-col">
<nav className="border-b bg-card/80 backdrop-blur-sm sticky top-0 z-50">
<div className="container mx-auto px-4">
<div className="flex items-center justify-between h-20">
<div className="flex items-center gap-4">
<svg
viewBox="0 0 24 24"
className="h-10 w-10 text-primary"
fill="none"
stroke="currentColor"
>
{/* Main tuna can body - outline */}
<path
d="M3 7c0-1.657 4.03-3 9-3s9 1.343 9 3v10c0 1.657-4.03 3-9 3s-9-1.343-9-3V7z"
strokeWidth="1.5"
/>

{/* Top rim */}
<path
d="M12 4c4.97 0 9 1.343 9 3 0 1.657-4.03 3-9 3s-9-1.343-9-3c0-1.657 4.03-3 9-3z"
strokeWidth="1.5"
/>

{/* Decorative rings */}
<path
d="M3.5 11c4.5 2 12.5 2 17 0M3.5 15c4.5 2 12.5 2 17 0"
strokeWidth="1"
/>

{/* Pull tab */}
<path
d="M14 5l2-2 1 1"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg>
<span className="text-2xl font-bold text-primary">AK TOOLS</span>
</div>

<div className="flex items-center space-x-6">
<div className="flex items-center justify-between h-16">
<Link to="/" className="flex items-center gap-3 group">
<div className="relative">
<div className="absolute inset-0 bg-primary/20 rounded-lg blur-md group-hover:bg-primary/30 transition-colors" />
<svg
viewBox="0 0 24 24"
className="h-9 w-9 text-primary relative"
fill="none"
stroke="currentColor"
>
<path
d="M3 7c0-1.657 4.03-3 9-3s9 1.343 9 3v10c0 1.657-4.03 3-9 3s-9-1.343-9-3V7z"
strokeWidth="1.5"
/>
<path
d="M12 4c4.97 0 9 1.343 9 3 0 1.657-4.03 3-9 3s-9-1.343-9-3c0-1.657 4.03-3 9-3z"
strokeWidth="1.5"
/>
<path
d="M3.5 11c4.5 2 12.5 2 17 0M3.5 15c4.5 2 12.5 2 17 0"
strokeWidth="1"
/>
<path
d="M14 5l2-2 1 1"
strokeWidth="1.5"
strokeLinecap="round"
/>
</svg>
</div>
<span className="text-xl font-bold tracking-tight">
<span className="text-primary">AK</span>
<span className="text-muted-foreground font-medium ml-1">Tools</span>
</span>
</Link>

<div className="flex items-center space-x-1">
{menuItems.map((item) => (
<Link
key={item.title}
to={item.path}
className="flex items-center space-x-2 px-4 py-2 rounded-md text-base font-medium hover:bg-accent hover:text-accent-foreground transition-colors"
className="nav-link flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-accent/50 transition-colors"
>
<item.icon className="h-5 w-5" />
<item.icon className="h-4 w-4" />
<span>{item.title}</span>
</Link>
))}
<div className="ml-4">
<div className="ml-2 pl-2 border-l">
<ThemeToggle />
</div>
</div>
</div>
</div>
</nav>

<main className="container mx-auto px-4 py-6">
<main className="container mx-auto px-4 py-8 flex-1">
<Routes>
<Route path="/" element={<ActivationKeyEditor />} />
<Route path="/templates" element={<Templates />} />
<Route path="/keys" element={<Keys />} />
</Routes>
</main>

<footer className="border-t bg-card/60 backdrop-blur-sm mt-auto">
<div className="container mx-auto px-4 py-6">
<div className="flex flex-col md:flex-row items-center justify-between gap-4">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<span>© {new Date().getFullYear()}</span>
<a
href="https://beshu.tech/"
target="_blank"
rel="noopener noreferrer"
className="font-semibold text-foreground hover:text-primary transition-colors"
>
Beshu Tech
</a>
<span>— All rights reserved</span>
</div>

<div className="flex items-center gap-6">
<a
href="https://readonlyrest.com/"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1.5 text-sm text-muted-foreground hover:text-primary transition-colors group"
>
<span>ReadonlyREST</span>
<ExternalLink className="h-3 w-3 opacity-0 group-hover:opacity-100 transition-opacity" />
</a>
<a
href="https://anaphora.it/"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1.5 text-sm text-muted-foreground hover:text-primary transition-colors group"
>
<span>Anaphora</span>
<ExternalLink className="h-3 w-3 opacity-0 group-hover:opacity-100 transition-opacity" />
</a>
<a
href="https://beshu.tech/"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1.5 text-sm text-muted-foreground hover:text-primary transition-colors group"
>
<span>Beshu</span>
<ExternalLink className="h-3 w-3 opacity-0 group-hover:opacity-100 transition-opacity" />
</a>
</div>
</div>
</div>
</footer>
</div>
</Router>
</ToastProvider>
</ThemeProvider>
);
}
Expand Down
34 changes: 28 additions & 6 deletions src/components/activationkey/ActivationKeyMetadata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ interface ActivationKeyMetadataDisplayProps {
metadata: ActivationKeyMetadata;
expiryDate?: Date;
onExpiryChange?: (date: Date | undefined) => void;
issuedDate?: Date;
onIssuedChange?: (date: Date | undefined) => void;
}

export const ActivationKeyMetadataDisplay: React.FC<ActivationKeyMetadataDisplayProps> = ({
metadata,
export const ActivationKeyMetadataDisplay: React.FC<ActivationKeyMetadataDisplayProps> = ({
metadata,
expiryDate,
onExpiryChange
onExpiryChange,
issuedDate,
onIssuedChange,
}) => {
return (
<div className="grid grid-cols-3 gap-4 p-4 bg-muted rounded-lg">
Expand All @@ -34,9 +38,27 @@ export const ActivationKeyMetadataDisplay: React.FC<ActivationKeyMetadataDisplay
<KeyRound className="h-5 w-5 text-muted-foreground" />
<div className="text-center">
<div className="text-sm font-medium">Issued</div>
<div className="text-sm text-muted-foreground">
{formatRelativeTime(metadata.issuedAt)}
</div>
{onIssuedChange ? (
<Popover>
<PopoverTrigger asChild>
<Button variant="link" className="h-auto p-0 text-sm text-muted-foreground hover:no-underline">
{issuedDate ? format(issuedDate, 'd MMM yyyy') : formatRelativeTime(metadata.issuedAt)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="center">
<Calendar
mode="single"
selected={issuedDate}
onSelect={onIssuedChange}
initialFocus
/>
</PopoverContent>
</Popover>
) : (
<div className="text-sm text-muted-foreground">
{formatRelativeTime(metadata.issuedAt)}
</div>
)}
</div>
</div>

Expand Down
Loading