Implement plugin dynamic settings routes architecture

Plugins can now register their settings pages dynamically via get_settings_routes()
hook, eliminating hardcoded imports that break lean builds.

Backend:
- Add get_settings_routes() hook to BasePlugin (shopdb/plugins/base.py)
- Implement in ComputersPlugin for Operating Systems management
- Add /api/plugins/settings-routes endpoint to expose plugin routes

Frontend:
- ModelCatalogHub fetches plugin routes and dynamically adds them via router.addRoute()
- Tabs render dynamically from pluginTabs ref
- Components lazy-load when clicked

Benefits:
- Lean builds work without manual route exclusions
- Plugins self-contain their UI extensions
- No core code changes needed when adding plugin settings pages
- Follows plugin architecture best practices

Fixes TODO from commit a03fd3de4f

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Proudlock
2026-09-11 16:49:10 -04:00
parent 4943e182c3
commit fe09d2745e
5 changed files with 95 additions and 15 deletions

View File

@@ -27,14 +27,16 @@
>
Models
</router-link>
<!-- TODO: Operating Systems tab should be added by computers plugin -->
<!-- <router-link
:to="{ name: 'model-catalog-os' }"
<!-- Plugin-contributed tabs loaded dynamically -->
<router-link
v-for="tab in pluginTabs"
:key="tab.name"
:to="{ name: tab.name }"
class="tab"
:class="{ active: $route.name === 'model-catalog-os' }"
:class="{ active: $route.name === tab.name }"
>
Operating Systems
</router-link> -->
{{ tab.tab }}
</router-link>
</div>
<div class="tab-content">
@@ -44,13 +46,37 @@
</template>
<script setup>
import { onMounted } from 'vue'
import { ref, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import axios from 'axios'
const route = useRoute()
const router = useRouter()
const pluginTabs = ref([])
onMounted(async () => {
// Fetch plugin-contributed settings routes
try {
const response = await axios.get('/api/plugins/settings-routes')
if (response.data && response.data.data) {
// Filter for model-catalog hub routes
pluginTabs.value = response.data.data.routes.filter(r => r.hub === 'model-catalog')
// Dynamically register routes for plugin tabs
pluginTabs.value.forEach(tab => {
if (!router.hasRoute(tab.name)) {
router.addRoute('model-catalog', {
path: tab.path,
name: tab.name,
component: () => import(`../../${tab.component}`)
})
}
})
}
} catch (error) {
console.error('Failed to load plugin settings routes:', error)
}
onMounted(() => {
// Redirect to vendors tab if no child route
if (route.name === 'model-catalog') {
router.replace({ name: 'model-catalog-vendors' })