396 lines
13 KiB
Vue

<script setup>
definePageMeta({
title: "Plugin Manager",
middleware: ["auth"],
requiresAuth: true,
});
// Quick stats for dashboard
const pluginStats = ref({
installed: 3,
active: 3,
available: 4,
updates: 2
});
// Quick actions
const quickActions = ref([
{
title: "Browse Store",
description: "Discover new plugins and applications",
icon: "mdi:store",
color: "blue",
path: "/devtool/plugin-manager/store"
},
{
title: "My Plugins",
description: "Manage installed plugins",
icon: "mdi:puzzle",
color: "green",
path: "/devtool/plugin-manager/installed"
},
{
title: "Upload Plugin",
description: "Install custom plugin packages",
icon: "mdi:cloud-upload",
color: "purple",
path: "/devtool/plugin-manager/upload"
},
{
title: "Plugin Settings",
description: "Configure plugin system settings",
icon: "mdi:cog",
color: "gray",
path: "/devtool/plugin-manager/settings"
}
]);
// Recently installed plugins
const recentPlugins = ref([
{
id: 1,
name: "notification-management",
displayName: "Notification Management System",
version: "1.2.0",
description: "Complete notification system with templates, scheduling, and analytics",
status: "active",
author: "CORRAD Team",
installDate: "2024-01-15",
icon: "mdi:bell-ring",
color: "orange",
authentik: {
applicationSlug: "notification-management",
requiredScopes: ["notifications:read", "notifications:write", "notifications:admin"],
groups: ["notification-users", "notification-admins"]
},
menuItems: 6,
apiEndpoints: 2,
migrations: 3
},
{
id: 2,
name: "report-management",
displayName: "Report Management System",
version: "1.0.0",
description: "Advanced reporting system integrated with Metabase for analytics and dashboards",
status: "active",
author: "CORRAD Team",
installDate: "2024-01-12",
icon: "mdi:chart-line",
color: "blue",
authentik: {
applicationSlug: "report-management",
requiredScopes: ["reports:read", "reports:write", "reports:admin"],
groups: ["report-users", "report-admins"]
},
menuItems: 5,
apiEndpoints: 3,
migrations: 2
},
{
id: 3,
name: "rbac",
displayName: "RBAC System",
version: "1.0.0",
description: "Role-Based Access Control system integrated with Authentik",
status: "active",
author: "CORRAD Team",
installDate: "2024-01-10",
icon: "mdi:shield-account",
color: "green",
authentik: {
applicationSlug: "rbac-system",
requiredScopes: ["rbac:read", "rbac:write", "rbac:admin"],
groups: ["rbac-users", "rbac-admins"]
},
menuItems: 4,
apiEndpoints: 4,
migrations: 1
}
]);
// Plugin updates available
const availableUpdates = ref([
{
id: 4,
name: "audit-trail",
displayName: "Audit Trail System",
currentVersion: "1.0.0",
newVersion: "1.1.0",
updateSize: "2.1 MB",
icon: "mdi:file-document-alert",
color: "purple"
},
{
id: 5,
name: "queue-management",
displayName: "Queue Management System",
currentVersion: "1.0.0",
newVersion: "1.0.1",
updateSize: "800 KB",
icon: "mdi:format-list-numbered",
color: "indigo"
}
]);
const getColorClasses = (color, type = 'bg') => {
const colorMap = {
bg: {
blue: 'bg-blue-100 text-blue-600',
green: 'bg-green-100 text-green-600',
purple: 'bg-purple-100 text-purple-600',
orange: 'bg-orange-100 text-orange-600',
indigo: 'bg-indigo-100 text-indigo-600',
red: 'bg-red-100 text-red-600',
gray: 'bg-gray-100 text-gray-600'
},
hover: {
blue: 'hover:bg-blue-200',
green: 'hover:bg-green-200',
purple: 'hover:bg-purple-200',
orange: 'hover:bg-orange-200',
indigo: 'hover:bg-indigo-200',
red: 'hover:bg-red-200',
gray: 'hover:bg-gray-200'
}
};
return colorMap[type][color] || colorMap[type]['gray'];
};
const navigateToAction = (path) => {
navigateTo(path);
};
const getStatusBadgeVariant = (status) => {
return status === 'active' ? 'success' : 'secondary';
};
</script>
<template>
<div>
<LayoutsBreadcrumb />
<!-- Welcome Header -->
<rs-card class="mb-6">
<template #body>
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold text-gray-900 mb-2">Plugin Manager</h1>
<p class="text-gray-600">
Manage your application ecosystem. Install, configure, and organize plugins to extend functionality.
</p>
</div>
<div class="hidden md:block">
<Icon name="mdi:puzzle" size="64" class="text-primary/20" />
</div>
</div>
</template>
</rs-card>
<!-- Stats Overview -->
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
<rs-card class="text-center">
<template #body>
<div class="p-4">
<div class="flex items-center justify-center mb-2">
<div class="bg-blue-100 text-blue-600 p-3 rounded-full">
<Icon name="mdi:puzzle" size="24" />
</div>
</div>
<h3 class="text-2xl font-bold text-gray-900">{{ pluginStats.installed }}</h3>
<p class="text-sm text-gray-600">Installed</p>
</div>
</template>
</rs-card>
<rs-card class="text-center">
<template #body>
<div class="p-4">
<div class="flex items-center justify-center mb-2">
<div class="bg-green-100 text-green-600 p-3 rounded-full">
<Icon name="mdi:check-circle" size="24" />
</div>
</div>
<h3 class="text-2xl font-bold text-gray-900">{{ pluginStats.active }}</h3>
<p class="text-sm text-gray-600">Active</p>
</div>
</template>
</rs-card>
<rs-card class="text-center">
<template #body>
<div class="p-4">
<div class="flex items-center justify-center mb-2">
<div class="bg-purple-100 text-purple-600 p-3 rounded-full">
<Icon name="mdi:store" size="24" />
</div>
</div>
<h3 class="text-2xl font-bold text-gray-900">{{ pluginStats.available }}</h3>
<p class="text-sm text-gray-600">Available</p>
</div>
</template>
</rs-card>
<rs-card class="text-center">
<template #body>
<div class="p-4">
<div class="flex items-center justify-center mb-2">
<div class="bg-orange-100 text-orange-600 p-3 rounded-full">
<Icon name="mdi:update" size="24" />
</div>
</div>
<h3 class="text-2xl font-bold text-gray-900">{{ pluginStats.updates }}</h3>
<p class="text-sm text-gray-600">Updates</p>
</div>
</template>
</rs-card>
</div>
<!-- Quick Actions -->
<rs-card class="mb-6">
<template #header>
<div class="flex items-center">
<Icon name="mdi:lightning-bolt" class="mr-2" />
Quick Actions
</div>
</template>
<template #body>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<div
v-for="action in quickActions"
:key="action.title"
class="group p-6 rounded-lg border-2 border-gray-200 hover:border-primary cursor-pointer transition-all duration-200 hover:shadow-md"
@click="navigateToAction(action.path)"
>
<div class="text-center">
<div
:class="[getColorClasses(action.color), getColorClasses(action.color, 'hover')]"
class="w-16 h-16 mx-auto rounded-full flex items-center justify-center mb-4 transition-colors"
>
<Icon :name="action.icon" size="32" />
</div>
<h3 class="font-semibold text-lg text-gray-900 mb-2 group-hover:text-primary transition-colors">
{{ action.title }}
</h3>
<p class="text-gray-600 text-sm">{{ action.description }}</p>
</div>
</div>
</div>
</template>
</rs-card>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- Recently Installed -->
<rs-card>
<template #header>
<div class="flex items-center justify-between w-full">
<div class="flex items-center">
<Icon name="mdi:history" class="mr-2" />
Recently Installed
</div>
<NuxtLink
to="/devtool/plugin-manager/installed"
class="text-primary hover:text-primary/80 text-sm font-medium"
>
View All
</NuxtLink>
</div>
</template>
<template #body>
<div class="space-y-4">
<div
v-for="plugin in recentPlugins"
:key="plugin.id"
class="flex items-center p-4 rounded-lg border border-gray-200 hover:shadow-sm transition-shadow"
>
<div
:class="getColorClasses(plugin.color)"
class="w-12 h-12 rounded-lg flex items-center justify-center mr-4"
>
<Icon :name="plugin.icon" size="24" />
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center justify-between">
<h4 class="font-medium text-gray-900 truncate">{{ plugin.displayName }}</h4>
<rs-badge :variant="getStatusBadgeVariant(plugin.status)" size="sm">
{{ plugin.status }}
</rs-badge>
</div>
<p class="text-sm text-gray-600 mt-1">v{{ plugin.version }} {{ plugin.author }}</p>
<div class="flex items-center space-x-3 mt-2 text-xs text-gray-500">
<span class="flex items-center">
<Icon name="mdi:menu" size="12" class="mr-1" />
{{ plugin.menuItems }} menus
</span>
<span class="flex items-center">
<Icon name="mdi:api" size="12" class="mr-1" />
{{ plugin.apiEndpoints }} APIs
</span>
<span class="flex items-center">
<Icon name="mdi:database" size="12" class="mr-1" />
{{ plugin.migrations }} migrations
</span>
</div>
<p class="text-xs text-gray-500 mt-1">Installed {{ plugin.installDate }}</p>
</div>
</div>
<div v-if="recentPlugins.length === 0" class="text-center py-8">
<Icon name="mdi:puzzle-outline" size="48" class="mx-auto text-gray-400 mb-2" />
<p class="text-gray-500">No plugins installed yet</p>
</div>
</div>
</template>
</rs-card>
<!-- Available Updates -->
<rs-card>
<template #header>
<div class="flex items-center justify-between w-full">
<div class="flex items-center">
<Icon name="mdi:update" class="mr-2" />
Available Updates
<rs-badge v-if="availableUpdates.length > 0" variant="warning" size="sm" class="ml-2">
{{ availableUpdates.length }}
</rs-badge>
</div>
<rs-button size="sm" variant="outline" v-if="availableUpdates.length > 0">
Update All
</rs-button>
</div>
</template>
<template #body>
<div class="space-y-4">
<div
v-for="update in availableUpdates"
:key="update.id"
class="flex items-center p-4 rounded-lg border border-gray-200 hover:shadow-sm transition-shadow"
>
<div
:class="getColorClasses(update.color)"
class="w-12 h-12 rounded-lg flex items-center justify-center mr-4"
>
<Icon :name="update.icon" size="24" />
</div>
<div class="flex-1 min-w-0">
<h4 class="font-medium text-gray-900">{{ update.displayName }}</h4>
<p class="text-sm text-gray-600 mt-1">
{{ update.currentVersion }} {{ update.newVersion }}
</p>
<p class="text-xs text-gray-500 mt-1">{{ update.updateSize }}</p>
</div>
<rs-button size="sm" variant="primary">
Update
</rs-button>
</div>
<div v-if="availableUpdates.length === 0" class="text-center py-8">
<Icon name="mdi:check-circle" size="48" class="mx-auto text-green-400 mb-2" />
<p class="text-gray-500">All plugins are up to date</p>
</div>
</div>
</template>
</rs-card>
</div>
</div>
</template>