178 lines
6.5 KiB
TypeScript
178 lines
6.5 KiB
TypeScript
/**
|
|
* Store Page
|
|
*
|
|
* Feature Store where users can self-activate features in the root mandate.
|
|
* Uses the Shared Instance Pattern -- each feature has one shared instance,
|
|
* and users get their own FeatureAccess + user-role upon activation.
|
|
*/
|
|
|
|
import React from 'react';
|
|
import { FaCogs, FaComments, FaHeadset, FaProjectDiagram } from 'react-icons/fa';
|
|
import { useLanguage } from '../providers/language/LanguageContext';
|
|
import { useStore } from '../hooks/useStore';
|
|
import type { StoreFeature } from '../api/storeApi';
|
|
import styles from './Store.module.css';
|
|
|
|
const FEATURE_ICONS: Record<string, React.ReactNode> = {
|
|
automation: <FaCogs />,
|
|
automation2: <FaProjectDiagram />,
|
|
teamsbot: <FaHeadset />,
|
|
workspace: <FaComments />,
|
|
commcoach: <FaComments />,
|
|
};
|
|
|
|
const FEATURE_DESCRIPTIONS: Record<string, Record<string, string>> = {
|
|
automation: {
|
|
de: 'Erstelle und verwalte Automatisierungen, um wiederkehrende Aufgaben effizient zu erledigen.',
|
|
en: 'Create and manage automations to handle recurring tasks efficiently.',
|
|
fr: 'Creer et gerer des automatisations pour traiter efficacement les taches recurrentes.',
|
|
},
|
|
automation2: {
|
|
de: 'n8n-style Flow-Automatisierung mit grafischem Editor, RAG und Tools.',
|
|
en: 'n8n-style flow automation with visual editor, RAG and tools.',
|
|
fr: 'Automatisation de flux style n8n avec editeur visuel, RAG et outils.',
|
|
},
|
|
teamsbot: {
|
|
de: 'Integriere einen AI-Bot in deine Microsoft Teams Meetings und Channels.',
|
|
en: 'Integrate an AI bot into your Microsoft Teams meetings and channels.',
|
|
fr: 'Integrez un bot IA dans vos reunions et canaux Microsoft Teams.',
|
|
},
|
|
workspace: {
|
|
de: 'Nutze den gemeinsamen AI Workspace: Chats, Tools und Kontext pro Instanz.',
|
|
en: 'Use the shared AI workspace: chats, tools, and context per instance.',
|
|
fr: 'Utilisez l\'espace de travail IA partage: chats, outils et contexte par instance.',
|
|
},
|
|
commcoach: {
|
|
de: 'CommCoach: Kommunikation trainieren mit KI-gestütztem Coaching und Feedback.',
|
|
en: 'CommCoach: practice communication with AI-assisted coaching and feedback.',
|
|
fr: 'CommCoach: entrainer la communication avec un coaching assiste par IA.',
|
|
},
|
|
};
|
|
|
|
function _getLabel(labels: Record<string, string>, lang: string): string {
|
|
return labels[lang] || labels['en'] || labels['de'] || Object.values(labels)[0] || '';
|
|
}
|
|
|
|
function _getDescription(featureCode: string, lang: string): string {
|
|
const desc = FEATURE_DESCRIPTIONS[featureCode];
|
|
if (!desc) return '';
|
|
return desc[lang] || desc['en'] || desc['de'] || '';
|
|
}
|
|
|
|
interface FeatureCardProps {
|
|
feature: StoreFeature;
|
|
language: string;
|
|
actionLoading: string | null;
|
|
onActivate: (code: string) => void;
|
|
onDeactivate: (code: string) => void;
|
|
}
|
|
|
|
const FeatureCard: React.FC<FeatureCardProps> = ({
|
|
feature,
|
|
language,
|
|
actionLoading,
|
|
onActivate,
|
|
onDeactivate,
|
|
}) => {
|
|
const isProcessing = actionLoading === feature.featureCode;
|
|
const icon = FEATURE_ICONS[feature.featureCode];
|
|
|
|
return (
|
|
<div className={`${styles.card} ${feature.isActive ? styles.cardActive : ''}`}>
|
|
<div className={styles.cardHeader}>
|
|
{icon && <span className={styles.cardIcon}>{icon}</span>}
|
|
<h3 className={styles.cardTitle}>
|
|
{_getLabel(feature.label, language)}
|
|
</h3>
|
|
</div>
|
|
|
|
<div className={styles.cardBody}>
|
|
<p className={styles.cardDescription}>
|
|
{_getDescription(feature.featureCode, language)}
|
|
</p>
|
|
</div>
|
|
|
|
<div>
|
|
<span className={`${styles.statusBadge} ${feature.isActive ? styles.statusActive : styles.statusInactive}`}>
|
|
<span className={styles.statusDot} />
|
|
{feature.isActive
|
|
? (language === 'de' ? 'Aktiv' : language === 'fr' ? 'Actif' : 'Active')
|
|
: (language === 'de' ? 'Verfuegbar' : language === 'fr' ? 'Disponible' : 'Available')}
|
|
</span>
|
|
</div>
|
|
|
|
<div className={styles.cardActions}>
|
|
{feature.isActive ? (
|
|
<button
|
|
className={styles.deactivateButton}
|
|
onClick={() => onDeactivate(feature.featureCode)}
|
|
disabled={isProcessing}
|
|
>
|
|
{isProcessing
|
|
? (language === 'de' ? 'Wird deaktiviert...' : 'Deactivating...')
|
|
: (language === 'de' ? 'Deaktivieren' : language === 'fr' ? 'Desactiver' : 'Deactivate')}
|
|
</button>
|
|
) : (
|
|
<button
|
|
className={styles.activateButton}
|
|
onClick={() => onActivate(feature.featureCode)}
|
|
disabled={isProcessing || !feature.canActivate}
|
|
>
|
|
{isProcessing
|
|
? (language === 'de' ? 'Wird aktiviert...' : 'Activating...')
|
|
: (language === 'de' ? 'Aktivieren' : language === 'fr' ? 'Activer' : 'Activate')}
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const StorePage: React.FC = () => {
|
|
const { currentLanguage } = useLanguage();
|
|
const { features, loading, actionLoading, error, activate, deactivate } = useStore();
|
|
|
|
return (
|
|
<div className={styles.store}>
|
|
<div className={styles.header}>
|
|
<h1>{currentLanguage === 'de' ? 'Feature Store' : currentLanguage === 'fr' ? 'Feature Store' : 'Feature Store'}</h1>
|
|
<p className={styles.subtitle}>
|
|
{currentLanguage === 'de'
|
|
? 'Aktiviere Features fuer dein Konto. Deine Daten sind isoliert und nur fuer dich sichtbar.'
|
|
: currentLanguage === 'fr'
|
|
? 'Activez des fonctionnalites pour votre compte. Vos donnees sont isolees et visibles uniquement par vous.'
|
|
: 'Activate features for your account. Your data is isolated and only visible to you.'}
|
|
</p>
|
|
</div>
|
|
|
|
{error && <div className={styles.error}>{error}</div>}
|
|
|
|
{loading ? (
|
|
<div className={styles.loading}>
|
|
{currentLanguage === 'de' ? 'Lade Features...' : 'Loading features...'}
|
|
</div>
|
|
) : features.length === 0 ? (
|
|
<div className={styles.empty}>
|
|
{currentLanguage === 'de'
|
|
? 'Keine Features im Store verfuegbar.'
|
|
: 'No features available in the store.'}
|
|
</div>
|
|
) : (
|
|
<div className={styles.grid}>
|
|
{features.map((feature) => (
|
|
<FeatureCard
|
|
key={feature.featureCode}
|
|
feature={feature}
|
|
language={currentLanguage}
|
|
actionLoading={actionLoading}
|
|
onActivate={activate}
|
|
onDeactivate={deactivate}
|
|
/>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default StorePage;
|