Initial commit: ArchiTools modular dashboard platform
Complete Next.js 16 application with 13 fully implemented modules: Email Signature, Word XML Generator, Registratura, Dashboard, Tag Manager, IT Inventory, Address Book, Password Vault, Mini Utilities, Prompt Generator, Digital Signatures, Word Templates, and AI Chat. Includes core platform systems (module registry, feature flags, storage abstraction, i18n, theming, auth stub, tagging), 16 technical documentation files, Docker deployment config, and legacy HTML tool reference. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import { FeatureGate } from '@/core/feature-flags';
|
||||
import { useI18n } from '@/core/i18n';
|
||||
import { AddressBookModule } from '@/modules/address-book';
|
||||
|
||||
export default function AddressBookPage() {
|
||||
const { t } = useI18n();
|
||||
|
||||
return (
|
||||
<FeatureGate flag="module.address-book" fallback={<ModuleDisabled />}>
|
||||
<div className="mx-auto max-w-6xl space-y-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight">{t('address-book.title')}</h1>
|
||||
<p className="text-muted-foreground">{t('address-book.description')}</p>
|
||||
</div>
|
||||
<AddressBookModule />
|
||||
</div>
|
||||
</FeatureGate>
|
||||
);
|
||||
}
|
||||
|
||||
function ModuleDisabled() {
|
||||
return (
|
||||
<div className="flex min-h-[40vh] items-center justify-center">
|
||||
<p className="text-muted-foreground">Modul dezactivat</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import { FeatureGate } from '@/core/feature-flags';
|
||||
import { useI18n } from '@/core/i18n';
|
||||
import { AiChatModule } from '@/modules/ai-chat';
|
||||
|
||||
export default function AiChatPage() {
|
||||
const { t } = useI18n();
|
||||
|
||||
return (
|
||||
<FeatureGate flag="module.ai-chat" fallback={<ModuleDisabled />}>
|
||||
<div className="mx-auto max-w-5xl space-y-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight">{t('ai-chat.title')}</h1>
|
||||
<p className="text-muted-foreground">{t('ai-chat.description')}</p>
|
||||
</div>
|
||||
<AiChatModule />
|
||||
</div>
|
||||
</FeatureGate>
|
||||
);
|
||||
}
|
||||
|
||||
function ModuleDisabled() {
|
||||
return (
|
||||
<div className="flex min-h-[40vh] items-center justify-center">
|
||||
<p className="text-muted-foreground">Modul dezactivat</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import { FeatureGate } from '@/core/feature-flags';
|
||||
import { useI18n } from '@/core/i18n';
|
||||
import { DigitalSignaturesModule } from '@/modules/digital-signatures';
|
||||
|
||||
export default function DigitalSignaturesPage() {
|
||||
const { t } = useI18n();
|
||||
|
||||
return (
|
||||
<FeatureGate flag="module.digital-signatures" fallback={<ModuleDisabled />}>
|
||||
<div className="mx-auto max-w-6xl space-y-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight">{t('digital-signatures.title')}</h1>
|
||||
<p className="text-muted-foreground">{t('digital-signatures.description')}</p>
|
||||
</div>
|
||||
<DigitalSignaturesModule />
|
||||
</div>
|
||||
</FeatureGate>
|
||||
);
|
||||
}
|
||||
|
||||
function ModuleDisabled() {
|
||||
return (
|
||||
<div className="flex min-h-[40vh] items-center justify-center">
|
||||
<p className="text-muted-foreground">Modul dezactivat</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import { FeatureGate } from '@/core/feature-flags';
|
||||
import { useI18n } from '@/core/i18n';
|
||||
import { EmailSignatureModule } from '@/modules/email-signature';
|
||||
|
||||
export default function EmailSignaturePage() {
|
||||
const { t } = useI18n();
|
||||
|
||||
return (
|
||||
<FeatureGate flag="module.email-signature" fallback={<ModuleDisabled />}>
|
||||
<div className="mx-auto max-w-6xl space-y-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight">{t('email-signature.title')}</h1>
|
||||
<p className="text-muted-foreground">{t('email-signature.description')}</p>
|
||||
</div>
|
||||
<EmailSignatureModule />
|
||||
</div>
|
||||
</FeatureGate>
|
||||
);
|
||||
}
|
||||
|
||||
function ModuleDisabled() {
|
||||
return (
|
||||
<div className="flex min-h-[40vh] items-center justify-center">
|
||||
<p className="text-muted-foreground">Modul dezactivat</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import { FeatureGate } from '@/core/feature-flags';
|
||||
import { useI18n } from '@/core/i18n';
|
||||
import { ItInventoryModule } from '@/modules/it-inventory';
|
||||
|
||||
export default function ItInventoryPage() {
|
||||
const { t } = useI18n();
|
||||
|
||||
return (
|
||||
<FeatureGate flag="module.it-inventory" fallback={<ModuleDisabled />}>
|
||||
<div className="mx-auto max-w-6xl space-y-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight">{t('it-inventory.title')}</h1>
|
||||
<p className="text-muted-foreground">{t('it-inventory.description')}</p>
|
||||
</div>
|
||||
<ItInventoryModule />
|
||||
</div>
|
||||
</FeatureGate>
|
||||
);
|
||||
}
|
||||
|
||||
function ModuleDisabled() {
|
||||
return (
|
||||
<div className="flex min-h-[40vh] items-center justify-center">
|
||||
<p className="text-muted-foreground">Modul dezactivat</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import { FeatureGate } from '@/core/feature-flags';
|
||||
import { useI18n } from '@/core/i18n';
|
||||
import { MiniUtilitiesModule } from '@/modules/mini-utilities';
|
||||
|
||||
export default function MiniUtilitiesPage() {
|
||||
const { t } = useI18n();
|
||||
|
||||
return (
|
||||
<FeatureGate flag="module.mini-utilities" fallback={<ModuleDisabled />}>
|
||||
<div className="mx-auto max-w-5xl space-y-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight">{t('mini-utilities.title')}</h1>
|
||||
<p className="text-muted-foreground">{t('mini-utilities.description')}</p>
|
||||
</div>
|
||||
<MiniUtilitiesModule />
|
||||
</div>
|
||||
</FeatureGate>
|
||||
);
|
||||
}
|
||||
|
||||
function ModuleDisabled() {
|
||||
return (
|
||||
<div className="flex min-h-[40vh] items-center justify-center">
|
||||
<p className="text-muted-foreground">Modul dezactivat</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import { FeatureGate } from '@/core/feature-flags';
|
||||
import { useI18n } from '@/core/i18n';
|
||||
import { PasswordVaultModule } from '@/modules/password-vault';
|
||||
|
||||
export default function PasswordVaultPage() {
|
||||
const { t } = useI18n();
|
||||
|
||||
return (
|
||||
<FeatureGate flag="module.password-vault" fallback={<ModuleDisabled />}>
|
||||
<div className="mx-auto max-w-5xl space-y-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight">{t('password-vault.title')}</h1>
|
||||
<p className="text-muted-foreground">{t('password-vault.description')}</p>
|
||||
</div>
|
||||
<PasswordVaultModule />
|
||||
</div>
|
||||
</FeatureGate>
|
||||
);
|
||||
}
|
||||
|
||||
function ModuleDisabled() {
|
||||
return (
|
||||
<div className="flex min-h-[40vh] items-center justify-center">
|
||||
<p className="text-muted-foreground">Modul dezactivat</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import { FeatureGate } from '@/core/feature-flags';
|
||||
import { useI18n } from '@/core/i18n';
|
||||
import { PromptGeneratorModule } from '@/modules/prompt-generator';
|
||||
|
||||
export default function PromptGeneratorPage() {
|
||||
const { t } = useI18n();
|
||||
|
||||
return (
|
||||
<FeatureGate flag="module.prompt-generator" fallback={<ModuleDisabled />}>
|
||||
<div className="mx-auto max-w-6xl space-y-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight">{t('prompt-generator.title')}</h1>
|
||||
<p className="text-muted-foreground">{t('prompt-generator.description')}</p>
|
||||
</div>
|
||||
<PromptGeneratorModule />
|
||||
</div>
|
||||
</FeatureGate>
|
||||
);
|
||||
}
|
||||
|
||||
function ModuleDisabled() {
|
||||
return (
|
||||
<div className="flex min-h-[40vh] items-center justify-center">
|
||||
<p className="text-muted-foreground">Modul dezactivat</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import { FeatureGate } from '@/core/feature-flags';
|
||||
import { useI18n } from '@/core/i18n';
|
||||
import { RegistraturaModule } from '@/modules/registratura';
|
||||
|
||||
export default function RegistraturaPage() {
|
||||
const { t } = useI18n();
|
||||
|
||||
return (
|
||||
<FeatureGate flag="module.registratura" fallback={<ModuleDisabled />}>
|
||||
<div className="mx-auto max-w-6xl space-y-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight">{t('registratura.title')}</h1>
|
||||
<p className="text-muted-foreground">{t('registratura.description')}</p>
|
||||
</div>
|
||||
<RegistraturaModule />
|
||||
</div>
|
||||
</FeatureGate>
|
||||
);
|
||||
}
|
||||
|
||||
function ModuleDisabled() {
|
||||
return (
|
||||
<div className="flex min-h-[40vh] items-center justify-center">
|
||||
<p className="text-muted-foreground">Modul dezactivat</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import { FeatureGate } from '@/core/feature-flags';
|
||||
import { useI18n } from '@/core/i18n';
|
||||
import { TagManagerModule } from '@/modules/tag-manager';
|
||||
|
||||
export default function TagManagerPage() {
|
||||
const { t } = useI18n();
|
||||
|
||||
return (
|
||||
<FeatureGate flag="module.tag-manager" fallback={<ModuleDisabled />}>
|
||||
<div className="mx-auto max-w-5xl space-y-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight">{t('tag-manager.title')}</h1>
|
||||
<p className="text-muted-foreground">{t('tag-manager.description')}</p>
|
||||
</div>
|
||||
<TagManagerModule />
|
||||
</div>
|
||||
</FeatureGate>
|
||||
);
|
||||
}
|
||||
|
||||
function ModuleDisabled() {
|
||||
return (
|
||||
<div className="flex min-h-[40vh] items-center justify-center">
|
||||
<p className="text-muted-foreground">Modul dezactivat</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import { FeatureGate } from '@/core/feature-flags';
|
||||
import { useI18n } from '@/core/i18n';
|
||||
import { WordTemplatesModule } from '@/modules/word-templates';
|
||||
|
||||
export default function WordTemplatesPage() {
|
||||
const { t } = useI18n();
|
||||
|
||||
return (
|
||||
<FeatureGate flag="module.word-templates" fallback={<ModuleDisabled />}>
|
||||
<div className="mx-auto max-w-5xl space-y-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight">{t('word-templates.title')}</h1>
|
||||
<p className="text-muted-foreground">{t('word-templates.description')}</p>
|
||||
</div>
|
||||
<WordTemplatesModule />
|
||||
</div>
|
||||
</FeatureGate>
|
||||
);
|
||||
}
|
||||
|
||||
function ModuleDisabled() {
|
||||
return (
|
||||
<div className="flex min-h-[40vh] items-center justify-center">
|
||||
<p className="text-muted-foreground">Modul dezactivat</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
'use client';
|
||||
|
||||
import { FeatureGate } from '@/core/feature-flags';
|
||||
import { useI18n } from '@/core/i18n';
|
||||
import { WordXmlModule } from '@/modules/word-xml';
|
||||
|
||||
export default function WordXmlPage() {
|
||||
const { t } = useI18n();
|
||||
|
||||
return (
|
||||
<FeatureGate flag="module.word-xml" fallback={<ModuleDisabled />}>
|
||||
<div className="mx-auto max-w-5xl space-y-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold tracking-tight">{t('word-xml.title')}</h1>
|
||||
<p className="text-muted-foreground">{t('word-xml.description')}</p>
|
||||
</div>
|
||||
<WordXmlModule />
|
||||
</div>
|
||||
</FeatureGate>
|
||||
);
|
||||
}
|
||||
|
||||
function ModuleDisabled() {
|
||||
return (
|
||||
<div className="flex min-h-[40vh] items-center justify-center">
|
||||
<p className="text-muted-foreground">Modul dezactivat</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
@@ -0,0 +1,126 @@
|
||||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
@import "shadcn/tailwind.css";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-ring: var(--ring);
|
||||
--color-input: var(--input);
|
||||
--color-border: var(--border);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-card: var(--card);
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
--radius-2xl: calc(var(--radius) + 8px);
|
||||
--radius-3xl: calc(var(--radius) + 12px);
|
||||
--radius-4xl: calc(var(--radius) + 16px);
|
||||
}
|
||||
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.205 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
--muted: oklch(0.97 0 0);
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
--accent: oklch(0.97 0 0);
|
||||
--accent-foreground: oklch(0.205 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.922 0 0);
|
||||
--input: oklch(0.922 0 0);
|
||||
--ring: oklch(0.708 0 0);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.97 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.205 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.922 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.556 0 0);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import type { Metadata } from 'next';
|
||||
import { Inter } from 'next/font/google';
|
||||
import './globals.css';
|
||||
|
||||
import { Providers } from './providers';
|
||||
import { AppShell } from '@/shared/components/layout/app-shell';
|
||||
|
||||
const inter = Inter({ subsets: ['latin', 'latin-ext'] });
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'ArchiTools',
|
||||
description: 'Platformă internă de instrumente pentru birou',
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="ro" suppressHydrationWarning>
|
||||
<body className={inter.className}>
|
||||
<Providers>
|
||||
<AppShell>{children}</AppShell>
|
||||
</Providers>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div className="flex min-h-[60vh] flex-col items-center justify-center text-center">
|
||||
<h1 className="text-6xl font-bold text-muted-foreground">404</h1>
|
||||
<p className="mt-4 text-lg text-muted-foreground">
|
||||
Pagina nu a fost găsită
|
||||
</p>
|
||||
<Link
|
||||
href="/"
|
||||
className="mt-6 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90"
|
||||
>
|
||||
Înapoi la panou
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import * as Icons from 'lucide-react';
|
||||
import { getAllModules } from '@/core/module-registry';
|
||||
import { useFeatureFlag } from '@/core/feature-flags';
|
||||
import { useI18n } from '@/core/i18n';
|
||||
import { EXTERNAL_TOOLS } from '@/config/external-tools';
|
||||
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '@/shared/components/ui/card';
|
||||
import { Badge } from '@/shared/components/ui/badge';
|
||||
|
||||
function DynamicIcon({ name, className }: { name: string; className?: string }) {
|
||||
const pascalName = name.replace(/(^|-)([a-z])/g, (_, _p, c: string) => c.toUpperCase());
|
||||
const IconComponent = (Icons as unknown as Record<string, React.ComponentType<{ className?: string }>>)[pascalName];
|
||||
if (!IconComponent) return <Icons.Circle className={className} />;
|
||||
return <IconComponent className={className} />;
|
||||
}
|
||||
|
||||
function ModuleCard({ module }: { module: { id: string; name: string; description: string; icon: string; route: string; featureFlag: string } }) {
|
||||
const enabled = useFeatureFlag(module.featureFlag);
|
||||
if (!enabled) return null;
|
||||
|
||||
return (
|
||||
<Link href={module.route}>
|
||||
<Card className="h-full transition-colors hover:border-primary/50 hover:bg-accent/30">
|
||||
<CardHeader className="flex flex-row items-center gap-4 space-y-0">
|
||||
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-md bg-primary/10">
|
||||
<DynamicIcon name={module.icon} className="h-5 w-5 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<CardTitle className="text-base">{module.name}</CardTitle>
|
||||
<CardDescription className="text-sm">{module.description}</CardDescription>
|
||||
</div>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
const CATEGORY_LABELS: Record<string, string> = {
|
||||
dev: 'Dezvoltare',
|
||||
tools: 'Instrumente',
|
||||
monitoring: 'Monitorizare',
|
||||
security: 'Securitate',
|
||||
};
|
||||
|
||||
export default function DashboardPage() {
|
||||
const { t } = useI18n();
|
||||
const modules = getAllModules();
|
||||
|
||||
const toolCategories = Object.keys(CATEGORY_LABELS).filter(
|
||||
(cat) => EXTERNAL_TOOLS.some((tool) => tool.category === cat)
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-6xl space-y-8">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight">{t('dashboard.welcome')}</h1>
|
||||
<p className="mt-1 text-muted-foreground">{t('dashboard.subtitle')}</p>
|
||||
</div>
|
||||
|
||||
{/* Quick stats */}
|
||||
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<p className="text-xs text-muted-foreground">Module active</p>
|
||||
<p className="text-2xl font-bold">{modules.length}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<p className="text-xs text-muted-foreground">Companii</p>
|
||||
<p className="text-2xl font-bold">3</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<p className="text-xs text-muted-foreground">Instrumente externe</p>
|
||||
<p className="text-2xl font-bold">{EXTERNAL_TOOLS.length}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<p className="text-xs text-muted-foreground">Stocare</p>
|
||||
<p className="text-2xl font-bold">localStorage</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Modules grid */}
|
||||
<div>
|
||||
<h2 className="mb-4 text-lg font-semibold">{t('dashboard.modules')}</h2>
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{modules.map((m) => (
|
||||
<ModuleCard key={m.id} module={m} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* External tools */}
|
||||
<div>
|
||||
<h2 className="mb-4 text-lg font-semibold">Instrumente externe</h2>
|
||||
<div className="space-y-4">
|
||||
{toolCategories.map((cat) => (
|
||||
<div key={cat}>
|
||||
<Badge variant="outline" className="mb-2">{CATEGORY_LABELS[cat]}</Badge>
|
||||
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{EXTERNAL_TOOLS.filter((tool) => tool.category === cat).map((tool) => {
|
||||
const cardContent = (
|
||||
<Card key={tool.id} className="transition-colors hover:bg-accent/30">
|
||||
<CardHeader className="flex flex-row items-center gap-3 space-y-0 p-4">
|
||||
<DynamicIcon name={tool.icon} className="h-4 w-4 text-muted-foreground" />
|
||||
<div>
|
||||
<p className="text-sm font-medium">{tool.name}</p>
|
||||
<p className="text-xs text-muted-foreground">{tool.description}</p>
|
||||
</div>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
);
|
||||
if (!tool.url) return cardContent;
|
||||
return (
|
||||
<a key={tool.id} href={tool.url} target="_blank" rel="noopener noreferrer">
|
||||
{cardContent}
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
'use client';
|
||||
|
||||
import { ThemeProvider } from '@/core/theme';
|
||||
import { I18nProvider } from '@/core/i18n';
|
||||
import { StorageProvider } from '@/core/storage';
|
||||
import { FeatureFlagProvider } from '@/core/feature-flags';
|
||||
import { AuthProvider } from '@/core/auth';
|
||||
import { DEFAULT_FLAGS } from '@/config/flags';
|
||||
|
||||
// Ensure module registry is populated
|
||||
import '@/config/modules';
|
||||
|
||||
interface ProvidersProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function Providers({ children }: ProvidersProps) {
|
||||
return (
|
||||
<ThemeProvider>
|
||||
<I18nProvider>
|
||||
<StorageProvider>
|
||||
<FeatureFlagProvider flagDefinitions={DEFAULT_FLAGS}>
|
||||
<AuthProvider>
|
||||
{children}
|
||||
</AuthProvider>
|
||||
</FeatureFlagProvider>
|
||||
</StorageProvider>
|
||||
</I18nProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user