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:
Marius Tarau
2026-02-17 12:50:25 +02:00
commit 4c46e8bcdd
189 changed files with 33780 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
'use client';
import { useFeatureFlag } from './use-feature-flag';
interface FeatureGateProps {
flag: string;
children: React.ReactNode;
fallback?: React.ReactNode;
}
export function FeatureGate({ flag, children, fallback = null }: FeatureGateProps) {
const enabled = useFeatureFlag(flag);
return enabled ? <>{children}</> : <>{fallback}</>;
}
+76
View File
@@ -0,0 +1,76 @@
'use client';
import { createContext, useContext, useMemo, useState, useCallback } from 'react';
import type { FeatureFlag } from './types';
import { resolveAllFlags, loadRuntimeOverrides, saveRuntimeOverride, clearRuntimeOverrides } from './flag-service';
interface FeatureFlagContextValue {
flags: Record<string, boolean>;
isEnabled: (key: string) => boolean;
setOverride: (key: string, value: boolean) => void;
clearOverrides: () => void;
}
const FeatureFlagContext = createContext<FeatureFlagContextValue | null>(null);
interface FeatureFlagProviderProps {
flagDefinitions: FeatureFlag[];
children: React.ReactNode;
}
export function FeatureFlagProvider({ flagDefinitions, children }: FeatureFlagProviderProps) {
const [runtimeOverrides, setRuntimeOverrides] = useState<Record<string, boolean>>(() => {
if (typeof window === 'undefined') return {};
return loadRuntimeOverrides();
});
const envOverrides = useMemo(() => {
const env: Record<string, string> = {};
if (typeof window !== 'undefined') {
// Collect NEXT_PUBLIC_FLAG_* env vars injected at build time
for (const [key, value] of Object.entries(process.env)) {
if (key.startsWith('NEXT_PUBLIC_FLAG_') && value) {
env[key] = value;
}
}
}
return env;
}, []);
const flags = useMemo(
() => resolveAllFlags(flagDefinitions, envOverrides, runtimeOverrides),
[flagDefinitions, envOverrides, runtimeOverrides]
);
const isEnabled = useCallback((key: string) => flags[key] ?? false, [flags]);
const setOverride = useCallback(
(key: string, value: boolean) => {
saveRuntimeOverride(key, value);
setRuntimeOverrides((prev) => ({ ...prev, [key]: value }));
},
[]
);
const handleClearOverrides = useCallback(() => {
clearRuntimeOverrides();
setRuntimeOverrides({});
}, []);
const value = useMemo(
() => ({ flags, isEnabled, setOverride, clearOverrides: handleClearOverrides }),
[flags, isEnabled, setOverride, handleClearOverrides]
);
return (
<FeatureFlagContext.Provider value={value}>
{children}
</FeatureFlagContext.Provider>
);
}
export function useFeatureFlags(): FeatureFlagContextValue {
const ctx = useContext(FeatureFlagContext);
if (!ctx) throw new Error('useFeatureFlags must be used within FeatureFlagProvider');
return ctx;
}
+74
View File
@@ -0,0 +1,74 @@
import type { FeatureFlag } from './types';
const RUNTIME_OVERRIDE_KEY = 'architools:flag-overrides';
export function resolveFlag(
flag: FeatureFlag,
envOverrides: Record<string, string>,
runtimeOverrides: Record<string, boolean>
): boolean {
// 1. Check environment variable override
const envKey = `NEXT_PUBLIC_FLAG_${flag.key.toUpperCase().replace(/\./g, '_')}`;
const envVal = envOverrides[envKey];
if (envVal !== undefined) {
return envVal === 'true';
}
// 2. Check runtime override
if (flag.overridable && runtimeOverrides[flag.key] !== undefined) {
return runtimeOverrides[flag.key]!;
}
// 3. Fall back to default
return flag.enabled;
}
export function resolveAllFlags(
flags: FeatureFlag[],
envOverrides: Record<string, string>,
runtimeOverrides: Record<string, boolean>
): Record<string, boolean> {
const resolved: Record<string, boolean> = {};
// First pass: resolve without dependency checks
for (const flag of flags) {
resolved[flag.key] = resolveFlag(flag, envOverrides, runtimeOverrides);
}
// Second pass: enforce dependencies
for (const flag of flags) {
if (resolved[flag.key] && flag.dependencies) {
for (const dep of flag.dependencies) {
if (!resolved[dep]) {
resolved[flag.key] = false;
break;
}
}
}
}
return resolved;
}
export function loadRuntimeOverrides(): Record<string, boolean> {
if (typeof window === 'undefined') return {};
try {
const stored = window.localStorage.getItem(RUNTIME_OVERRIDE_KEY);
if (stored) return JSON.parse(stored);
} catch {
// Ignore parse errors
}
return {};
}
export function saveRuntimeOverride(key: string, value: boolean): void {
if (typeof window === 'undefined') return;
const overrides = loadRuntimeOverrides();
overrides[key] = value;
window.localStorage.setItem(RUNTIME_OVERRIDE_KEY, JSON.stringify(overrides));
}
export function clearRuntimeOverrides(): void {
if (typeof window === 'undefined') return;
window.localStorage.removeItem(RUNTIME_OVERRIDE_KEY);
}
+4
View File
@@ -0,0 +1,4 @@
export type { FeatureFlag, FlagCategory } from './types';
export { FeatureFlagProvider, useFeatureFlags } from './flag-provider';
export { useFeatureFlag } from './use-feature-flag';
export { FeatureGate } from './feature-gate';
+11
View File
@@ -0,0 +1,11 @@
export type FlagCategory = 'module' | 'experimental' | 'system';
export interface FeatureFlag {
key: string;
enabled: boolean;
label: string;
description: string;
category: FlagCategory;
dependencies?: string[];
overridable: boolean;
}
@@ -0,0 +1,8 @@
'use client';
import { useFeatureFlags } from './flag-provider';
export function useFeatureFlag(key: string): boolean {
const { isEnabled } = useFeatureFlags();
return isEnabled(key);
}