Adding license generation functionality

This commit is contained in:
Muluhabt
2026-07-29 16:53:50 +03:00
parent 26cc1f4fd2
commit 5161e278ce
102 changed files with 5995 additions and 21326 deletions

View File

@@ -1,5 +1,6 @@
import {
AppShell,
Badge,
NavLink,
ScrollArea,
Stack,
@@ -20,11 +21,30 @@ export interface NavItem {
label: string;
icon: Icon;
to?: string;
/** Not yet connected to real data — surfaced as a "Soon" badge. */
soon?: boolean;
}
/** A labelled group of nav items. */
export interface NavSection {
/** i18n key or literal heading. Omit for an ungrouped leading block. */
label?: string;
items: NavItem[];
}
/** Both shapes are accepted so callers can migrate to sections gradually. */
export type NavEntries = NavItem[] | NavSection[];
function toSections(entries: NavEntries): NavSection[] {
if (entries.length === 0) return [];
const isSectioned = (entries as NavSection[])[0]?.items !== undefined;
return isSectioned
? (entries as NavSection[])
: [{ items: entries as NavItem[] }];
}
interface AppSidebarProps {
navItems: NavItem[];
navItems: NavEntries;
collapsed: boolean;
activePath: string;
onToggleCollapse: () => void;
@@ -96,14 +116,43 @@ export function AppSidebar({
{/* Navigation items */}
<AppShell.Section grow component={ScrollArea} p="md">
<Stack gap={4}>
{navItems.map((item) => {
<Stack gap={2}>
{toSections(navItems).map((section, sectionIndex) => (
<Stack gap={2} key={section.label ?? `section-${sectionIndex}`}>
{/* Headings are noise when only icons are visible. */}
{section.label && !collapsed && (
<Text
size="xs"
fw={700}
c="dimmed"
mt={sectionIndex === 0 ? 0 : rem(14)}
pl={rem(12)}
style={{ textTransform: 'uppercase', letterSpacing: '0.06em' }}
>
{t(section.label)}
</Text>
)}
{section.label && collapsed && sectionIndex > 0 && (
<div
style={{
height: 1,
margin: `${rem(8)} ${rem(6)}`,
backgroundColor: 'var(--mantine-color-gray-2)',
}}
/>
)}
{section.items.map((item) => {
const ItemIcon = item.icon;
const active = activeNavItem(item);
if (collapsed) {
return (
<Tooltip key={item.label} label={t(item.label)} position="right" withArrow>
<Tooltip
key={item.label}
label={item.soon ? `${t(item.label)}${t('nav.soon', 'Soon')}` : t(item.label)}
position="right"
withArrow
>
<UnstyledButton
onClick={() => onNavigate(item)}
style={{
@@ -113,6 +162,7 @@ export function AppSidebar({
width: '100%',
height: rem(40),
borderRadius: rem(10),
opacity: item.soon ? 0.55 : 1,
color: active ? 'var(--mantine-color-blue-6)' : undefined,
backgroundColor: active ? 'var(--mantine-color-blue-light)' : undefined,
}}
@@ -129,12 +179,25 @@ export function AppSidebar({
active={active}
label={t(item.label)}
leftSection={<ItemIcon size={19} stroke={1.6} />}
// Tells a reviewer at a glance which screens are wired up.
rightSection={
item.soon ? (
<Badge size="xs" variant="light" color="gray" radius="sm">
{t('nav.soon', 'Soon')}
</Badge>
) : undefined
}
onClick={() => onNavigate(item)}
variant="light"
styles={{ root: { borderRadius: rem(10) }, label: { fontWeight: 500 } }}
styles={{
root: { borderRadius: rem(10), opacity: item.soon ? 0.7 : 1 },
label: { fontWeight: 500 },
}}
/>
);
})}
})}
</Stack>
))}
</Stack>
</AppShell.Section>