Applications: show related knowledge-base articles on the app detail
Some checks failed
CI / backend (push) Successful in 1m39s
CI / naming (push) Successful in 1s
CI / frontend (push) Successful in 8s
CI / migrations-mysql (push) Failing after 8s

Classic ShopDB listed an application's related KB articles on its page; the flask
app detail didn't, even though KB articles carry an appid FK. get_application now
returns a knowledgebase list (KB rows linked by appid; lazy + plugin-guarded so
core stays decoupled), and ApplicationDetail.vue renders a Knowledge Base section
linking each article.

Verified: an app returns its linked KB (e.g. 77 articles) and the section renders.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-07-13 13:44:23 -04:00
parent 49d311b56e
commit 6d843e46e1
2 changed files with 31 additions and 0 deletions

View File

@@ -93,6 +93,23 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Related Knowledge Base -->
<div class="section-card" v-if="app.knowledgebase && app.knowledgebase.length">
<h3 class="section-title">Knowledge Base ({{ app.knowledgebase.length }})</h3>
<div class="kb-list">
<a
v-for="kb in app.knowledgebase"
:key="kb.linkid"
:href="kb.linkurl"
target="_blank"
class="kb-item"
>
<span class="kb-title">{{ kb.shortdescription }}</span>
<span class="kb-keywords" v-if="kb.keywords">{{ kb.keywords }}</span>
</a>
</div>
</div>
</div> </div>
<!-- Right Column --> <!-- Right Column -->

View File

@@ -40,6 +40,19 @@ def _installed_count(appid):
return ComputerInstalledApp.query.filter_by(appid=appid, isactive=True).count() return ComputerInstalledApp.query.filter_by(appid=appid, isactive=True).count()
def _related_kb(appid):
"""Knowledge Base articles linked to this app, [] when the KB plugin is
absent. Lazy + guarded so core stays decoupled from the plugin."""
try:
from plugins.knowledgebase.models import KnowledgeBase
except ImportError:
return []
rows = KnowledgeBase.query.filter_by(appid=appid, isactive=True).order_by(
KnowledgeBase.shortdescription).all()
return [{'linkid': k.linkid, 'shortdescription': k.shortdescription,
'linkurl': k.linkurl, 'keywords': k.keywords} for k in rows]
def _require_computer_models(): def _require_computer_models():
"""Resolve (Computer, ComputerInstalledApp) or a 503 response tuple. """Resolve (Computer, ComputerInstalledApp) or a 503 response tuple.
@@ -116,6 +129,7 @@ def get_application(app_id: int):
data = app.to_dict() data = app.to_dict()
data['versions'] = [v.to_dict() for v in app.versions.filter_by(isactive=True).order_by(AppVersion.version.desc()).all()] data['versions'] = [v.to_dict() for v in app.versions.filter_by(isactive=True).order_by(AppVersion.version.desc()).all()]
data['installedcount'] = _installed_count(app.appid) data['installedcount'] = _installed_count(app.appid)
data['knowledgebase'] = _related_kb(app.appid)
return success_response(data) return success_response(data)