diff --git a/apps/web/src/app/(portal)/marketplace/components/MarketplaceCard.tsx b/apps/web/src/app/(portal)/marketplace/components/MarketplaceCard.tsx new file mode 100644 index 0000000..5805844 --- /dev/null +++ b/apps/web/src/app/(portal)/marketplace/components/MarketplaceCard.tsx @@ -0,0 +1,141 @@ +'use client'; + +import { useTranslations } from 'next-intl'; + +/** + * Props for the MarketplaceCard component. + */ +export interface MarketplaceCardProps { + id: string; + name: string; + slug: string; + description: Record; + icon?: string; + category: string; + isActive: boolean; + isToggling: boolean; + onToggle: (id: string, currentlyActive: boolean) => void; +} + +/** + * Default icon mapping for known module icons. + * Renders inline SVG to avoid external icon library dependency. + */ +function ModuleIcon({ icon }: { icon?: string }) { + if (icon === 'Globe') { + return ( + + + + + + ); + } + + // Default puzzle-piece icon for generic modules + return ( + + + + ); +} + +/** + * Marketplace card showing a module with activation controls. + * + * Displays module name, localized description, category badge, + * status badge, and an activate/deactivate button. Following the + * ModuleCard pattern from Phase 3 with marketplace-specific extensions. + */ +export function MarketplaceCard({ + id, + name, + slug, + description, + icon, + category, + isActive, + isToggling, + onToggle, +}: MarketplaceCardProps) { + const t = useTranslations('marketplace'); + const locale = (typeof window !== 'undefined' && document.documentElement.lang) || 'de'; + const localizedDescription = description[locale] || description.en || description.de || ''; + + return ( +
+ {/* Header: icon + name + badges */} +
+
+ +
+
+

{name}

+
+ + {category} + + + {isActive ? t('statusActive') : t('statusAvailable')} + +
+
+
+ + {/* Description */} +

+ {localizedDescription} +

+ + {/* Footer: action button */} +
+ +
+
+ ); +} diff --git a/apps/web/src/messages/de.json b/apps/web/src/messages/de.json index 3b02f8a..0519e1e 100644 --- a/apps/web/src/messages/de.json +++ b/apps/web/src/messages/de.json @@ -81,6 +81,8 @@ "collapse": "Einklappen", "expand": "Ausklappen", "userPlaceholder": "Benutzer", + "search": "Module suchen...", + "noResults": "Keine Module gefunden", "categories": { "label": "Kategorien", "empty": "Keine Module" @@ -171,5 +173,34 @@ "noResults": "Gib einen Domain-Namen ein um die Verfuegbarkeit zu pruefen", "checking": "Pruefe...", "error": "Fehler bei der Pruefung" + }, + "marketplace": { + "title": "Marktplatz", + "subtitle": "Entdecken und aktivieren Sie Module fuer Ihren Mandanten", + "activate": "Modul aktivieren", + "deactivate": "Deaktivieren", + "searchPlaceholder": "Module suchen...", + "statusAll": "Alle", + "statusActive": "Aktiviert", + "statusAvailable": "Verfuegbar", + "categoryAll": "Alle", + "emptyTitle": "Noch keine Module verfuegbar", + "emptyBody": "Module werden vom Systemadministrator bereitgestellt. Schauen Sie spaeter noch einmal vorbei.", + "filteredEmptyTitle": "Keine Ergebnisse", + "filteredEmptyBody": "Versuchen Sie einen anderen Suchbegriff oder aendern Sie die Filter.", + "errorLoad": "Module konnten nicht geladen werden. Bitte laden Sie die Seite neu.", + "toastActivated": "Modul erfolgreich aktiviert", + "toastDeactivated": "Modul erfolgreich deaktiviert", + "toastActivateError": "Aktivierung fehlgeschlagen. Bitte versuchen Sie es erneut.", + "toastDeactivateError": "Deaktivierung fehlgeschlagen. Bitte versuchen Sie es erneut.", + "deactivateDialogTitle": "Modul deaktivieren", + "deactivateDialogBody": "Moechten Sie {moduleName} fuer diesen Mandanten deaktivieren? Das Modul wird aus der Seitenleiste entfernt.", + "deactivateDialogConfirm": "Deaktivieren", + "deactivateDialogCancel": "Abbrechen", + "tenantContextLabel": "Mandanten-Kontext", + "backToMarketplace": "Zurueck zum Marktplatz", + "detailVersion": "Version {version}", + "detailStatusActive": "Aktiviert fuer diesen Mandanten", + "detailStatusInactive": "Nicht aktiviert" } } diff --git a/apps/web/src/messages/en.json b/apps/web/src/messages/en.json index 13aa7da..5bf67ee 100644 --- a/apps/web/src/messages/en.json +++ b/apps/web/src/messages/en.json @@ -81,6 +81,8 @@ "collapse": "Collapse", "expand": "Expand", "userPlaceholder": "User", + "search": "Search modules...", + "noResults": "No modules found", "categories": { "label": "Categories", "empty": "No modules" @@ -171,5 +173,34 @@ "noResults": "Enter a domain name to check availability", "checking": "Checking...", "error": "Error checking domain" + }, + "marketplace": { + "title": "Marketplace", + "subtitle": "Discover and activate modules for your tenant", + "activate": "Activate Module", + "deactivate": "Deactivate", + "searchPlaceholder": "Search modules...", + "statusAll": "All", + "statusActive": "Activated", + "statusAvailable": "Available", + "categoryAll": "All", + "emptyTitle": "No modules available yet", + "emptyBody": "Modules are provided by the system administrator. Check back later.", + "filteredEmptyTitle": "No results", + "filteredEmptyBody": "Try a different search term or adjust your filters.", + "errorLoad": "Could not load modules. Please reload the page.", + "toastActivated": "Module activated successfully", + "toastDeactivated": "Module deactivated successfully", + "toastActivateError": "Activation failed. Please try again.", + "toastDeactivateError": "Deactivation failed. Please try again.", + "deactivateDialogTitle": "Deactivate Module", + "deactivateDialogBody": "Do you want to deactivate {moduleName} for this tenant? The module will be removed from the sidebar.", + "deactivateDialogConfirm": "Deactivate", + "deactivateDialogCancel": "Cancel", + "tenantContextLabel": "Tenant Context", + "backToMarketplace": "Back to Marketplace", + "detailVersion": "Version {version}", + "detailStatusActive": "Activated for this tenant", + "detailStatusInactive": "Not activated" } }