Files
Gnuboard7/templates/_bundled/sirsoft-admin_basic/layouts/admin_dashboard.json
T
HeuJung 072b3f50d9 feat(core,admin-template): 자산 URL 방식 감지 지점 보강 + 테스트 격리 결함 수정
계획서 §검증 후반부를 다시 대조해 미구현 3건을 확인하고 모두 구현했다. 앞선 커밋에서
"미구현 1건" 으로 보고한 것은 §12 불변식 표만 파일로 대조하고 §검증 섹션의 표 두 개는
확인하지 않은 결과였다.

- 설치 2단계 요구사항 화면에 자산 URL 방식 항목 추가. 판정은 서버가 아니라 브라우저가
 한다 — 서버에서 자기 APP_URL 로 요청하면 loopback 이 vhost·프록시 체인을 우회해
 실제 방문자와 다른 답을 낸다. 2단계와 3단계가 같은 판정 함수를 쓰도록 추출했다
- 대시보드가 스스로 프로브를 던져 **저장된** 방식과 대조하고 어긋나면 안내한다.
 런타임 값이 아니라 저장값을 보는 이유는, 자가 복구가 이미 전환해 놓았을 때가 바로
 알려야 할 상황이기 때문이다 — 봇은 자바스크립트를 실행하지 않아 자가 복구가 닿지 않는다.
 알리기만 하고 저장하지 않으므로 클라이언트가 서버 설정을 뒤집지 않는다
- 레이아웃 렌더링 테스트를 두 모드로 추가. 핵심 단언은 두 모드의 DOM 이 완전히 동일하다는
 것이다 — 모드가 화면 구조로 새어나가면 확장자 없는 환경에서만 깨지고 그 환경은 개발 중
 거의 재현되지 않는다

함께 고친 테스트 격리 결함 4건 (이번 기능과 무관하나 같은 세션에서 발견):

- 인스톨러 공유 클래스 로드가 오토로드를 막고 있어, 앱 루트를 임시 디렉토리로 바꾸는
 테스트에서 없는 파일을 읽으려다 죽었다 (8건 fatal)
- 테스트용 번역 스텁이 실제 구현과의 정의 경쟁에서 이기면 주입된 번역을 무시했다.
 클래스 단독 실행은 통과하고 스위트 실행만 실패하던 원인
- 모듈 활성 목록은 "디스크 로드분 ∩ DB 활성분" 인데 부팅 시 테이블이 비어 디스크 스캔이
 일어나지 않아, 파일 캐시 잔재가 있을 때만 통과하던 순서 의존 테스트
- 라우트 프로바이더 가드 테스트가 Git 미추적 파일(.env) 존재에 의존해 신규 클론·CI 에서
 실패했다. 임시 앱 루트로 격리

또한 일부 테스트가 실제 개발 환경 파일을 삭제·재생성하고 자신의 정리 단계에서 되돌리는데,
그 테스트가 죽으면 되돌아오지 않아 복구 불가능한 유실이 발생한다. 실제로 이번 세션에서
겪었으므로 테스트 부트스트랩에 프로세스 수준 복원 안전망을 추가했다.

검증: 백엔드 344건 / 템플릿 2799건 / 레이아웃 7건 / 브라우저 9건 green, audit 위반 0.
2026-07-23 11:39:57 +09:00

1645 lines
100 KiB
JSON

{
"version": "1.0.0",
"layout_name": "admin_dashboard",
"permissions": [
"core.dashboard.read"
],
"extends": "_admin_base",
"meta": {
"title": "$t:admin.dashboard.title",
"description": "$t:admin.dashboard.description",
"auth_required": true
},
"init_actions": [
{
"handler": "checkAssetUrlModeDrift"
}
],
"data_sources": [
{
"id": "dashboard_stats",
"label_key": "$t:editor.data_source.dashboard_stats",
"type": "api",
"endpoint": "/api/admin/dashboard/stats",
"method": "GET",
"auto_fetch": true,
"auth_required": true,
"loading_strategy": "progressive",
"fallback": {
"data": {
"total_users": {
"count": 0,
"change_display": "-"
},
"installed_modules": {
"total": 0,
"active": 0
},
"active_plugins": {
"total": 0,
"active": 0
},
"installed_templates": {
"total": 0,
"active": 0
},
"language_packs": {
"total": 0,
"active": 0
},
"system_status": {
"label": "-"
}
}
}
},
{
"id": "dashboard_activities",
"label_key": "$t:editor.data_source.dashboard_activities",
"type": "api",
"endpoint": "/api/admin/dashboard/activities",
"method": "GET",
"auto_fetch": true,
"auth_required": true,
"loading_strategy": "progressive",
"fallback": {
"data": []
}
},
{
"id": "dashboard_modules",
"label_key": "$t:editor.data_source.dashboard_modules",
"type": "api",
"endpoint": "/api/admin/modules/installed",
"method": "GET",
"auto_fetch": true,
"auth_required": true,
"loading_strategy": "progressive",
"params": {
"per_page": "5"
},
"fallback": {
"data": {
"data": [],
"current_page": 1,
"last_page": 1,
"per_page": 5,
"total": 0
}
}
},
{
"id": "dashboard_plugins",
"label_key": "$t:editor.data_source.dashboard_plugins",
"type": "api",
"endpoint": "/api/admin/plugins/installed",
"method": "GET",
"auto_fetch": true,
"auth_required": true,
"loading_strategy": "progressive",
"params": {
"per_page": "5"
},
"fallback": {
"data": {
"data": [],
"current_page": 1,
"last_page": 1,
"per_page": 5,
"total": 0
}
}
},
{
"id": "dashboard_templates",
"label_key": "$t:editor.data_source.dashboard_templates",
"type": "api",
"endpoint": "/api/admin/templates",
"method": "GET",
"auto_fetch": true,
"auth_required": true,
"loading_strategy": "progressive",
"params": {
"per_page": "5"
},
"fallback": {
"data": {
"data": [],
"current_page": 1,
"last_page": 1,
"per_page": 5,
"total": 0
}
}
},
{
"id": "dashboard_alerts",
"label_key": "$t:editor.data_source.dashboard_alerts",
"type": "api",
"endpoint": "/api/admin/dashboard/alerts",
"method": "GET",
"auto_fetch": true,
"auth_required": true,
"loading_strategy": "background",
"fallback": {
"data": []
}
},
{
"id": "dashboard_recent_notifications",
"label_key": "$t:editor.data_source.dashboard_recent_notifications",
"type": "api",
"endpoint": "/api/admin/dashboard/recent-notifications",
"method": "GET",
"auto_fetch": true,
"auth_required": true,
"loading_strategy": "progressive",
"fallback": {
"data": []
}
},
{
"id": "dashboard_stats_ws",
"label_key": "$t:editor.data_source.dashboard_stats_ws",
"type": "websocket",
"channel": "core.admin.dashboard",
"event": "dashboard.stats.updated",
"channel_type": "private",
"target_source": "dashboard_stats"
}
],
"slots": {
"content": [
{
"id": "dashboard_content",
"type": "basic",
"name": "Div",
"props": {
"className": "admin-page-content-responsive-fluid"
},
"children": [
{
"id": "page_header",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-between mb-6"
},
"children": [
{
"id": "page_title",
"type": "basic",
"name": "H1",
"props": {
"className": "text-primary text-xl sm:text-2xl font-semibold"
},
"text": "$t:admin.dashboard.title"
},
{
"id": "refresh_button",
"type": "basic",
"name": "Button",
"props": {
"className": "btn-icon",
"aria-label": "$t:admin.dashboard.refresh"
},
"actions": [
{
"type": "click",
"handler": "sequence",
"params": {
"actions": [
{
"handler": "refetchDataSource",
"params": {
"dataSourceId": "dashboard_stats"
}
},
{
"handler": "refetchDataSource",
"params": {
"dataSourceId": "dashboard_activities"
}
},
{
"handler": "refetchDataSource",
"params": {
"dataSourceId": "dashboard_modules"
}
},
{
"handler": "refetchDataSource",
"params": {
"dataSourceId": "dashboard_plugins"
}
},
{
"handler": "refetchDataSource",
"params": {
"dataSourceId": "dashboard_templates"
}
},
{
"handler": "refetchDataSource",
"params": {
"dataSourceId": "dashboard_alerts"
}
},
{
"handler": "refetchDataSource",
"params": {
"dataSourceId": "dashboard_recent_notifications"
}
},
{
"handler": "toast",
"params": {
"type": "success",
"message": "$t:admin.dashboard.refreshed"
}
}
]
}
}
],
"children": [
{
"id": "refresh_icon",
"type": "basic",
"name": "Icon",
"props": {
"name": "refresh",
"size": "md"
}
}
]
}
]
},
{
"id": "asset_url_mode_drift_alert",
"type": "basic",
"name": "Div",
"if": "{{_global.assetUrlModeDrift}}",
"props": {
"className": "flex items-start gap-3 p-4 mb-6 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800"
},
"children": [
{
"id": "asset_url_mode_drift_icon",
"type": "basic",
"name": "Icon",
"props": {
"name": "triangle-exclamation",
"className": "text-base text-amber-600 dark:text-amber-400 mt-0.5"
}
},
{
"id": "asset_url_mode_drift_body",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-1 min-w-0"
},
"children": [
{
"id": "asset_url_mode_drift_title",
"type": "basic",
"name": "Div",
"props": {
"className": "text-sm font-semibold text-amber-900 dark:text-amber-200"
},
"text": "$t:admin.dashboard.asset_url_mode_drift_title"
},
{
"id": "asset_url_mode_drift_desc",
"type": "basic",
"name": "P",
"props": {
"className": "mt-1 text-sm text-amber-800 dark:text-amber-300"
},
"text": "$t:admin.dashboard.asset_url_mode_drift_description"
},
{
"id": "asset_url_mode_drift_link",
"type": "basic",
"name": "Button",
"props": {
"type": "button",
"className": "mt-2 inline-flex items-center gap-1.5 text-sm font-medium text-amber-900 dark:text-amber-200 underline hover:no-underline cursor-pointer"
},
"text": "$t:admin.dashboard.asset_url_mode_drift_action",
"actions": [
{
"type": "click",
"handler": "navigate",
"params": {
"path": "/admin/settings",
"query": {
"tab": "general"
}
}
}
]
}
]
}
]
},
{
"id": "quick_menu",
"type": "basic",
"name": "Div",
"props": {
"className": "flex flex-wrap gap-2 md:gap-3 mb-4 md:mb-6 lg:mb-8"
},
"children": [
{
"id": "qm_users",
"type": "basic",
"name": "A",
"props": {
"href": "/admin/users",
"className": "flex-center flex-col justify-center gap-1 w-16 h-16 md:w-20 md:h-20 text-[10px] md:text-xs font-medium rounded-xl bg-slate-100 dark:bg-slate-700 text-slate-600 dark:text-slate-300 hover:bg-slate-700 hover:text-white dark:hover:bg-slate-200 dark:hover:text-slate-800 hover:-translate-y-0.5 focus:outline-none focus:ring-2 focus:ring-slate-400 dark:focus:ring-slate-500 transition-all duration-200"
},
"children": [
{
"type": "basic",
"name": "Icon",
"props": {
"name": "users",
"className": "text-lg md:text-xl"
}
},
{
"type": "basic",
"name": "Span",
"props": {
"className": "text-center leading-tight"
},
"text": "$t:admin.dashboard.quick_menu.users"
}
]
},
{
"id": "qm_settings",
"type": "basic",
"name": "A",
"props": {
"href": "/admin/settings",
"className": "flex-center flex-col justify-center gap-1 w-16 h-16 md:w-20 md:h-20 text-[10px] md:text-xs font-medium rounded-xl bg-slate-100 dark:bg-slate-700 text-slate-600 dark:text-slate-300 hover:bg-slate-700 hover:text-white dark:hover:bg-slate-200 dark:hover:text-slate-800 hover:-translate-y-0.5 focus:outline-none focus:ring-2 focus:ring-slate-400 dark:focus:ring-slate-500 transition-all duration-200"
},
"children": [
{
"type": "basic",
"name": "Icon",
"props": {
"name": "cog",
"className": "text-lg md:text-xl"
}
},
{
"type": "basic",
"name": "Span",
"props": {
"className": "text-center leading-tight"
},
"text": "$t:admin.dashboard.quick_menu.settings"
}
]
},
{
"id": "qm_modules",
"type": "basic",
"name": "A",
"props": {
"href": "/admin/modules",
"className": "flex-center flex-col justify-center gap-1 w-16 h-16 md:w-20 md:h-20 text-[10px] md:text-xs font-medium rounded-xl bg-slate-100 dark:bg-slate-700 text-slate-600 dark:text-slate-300 hover:bg-slate-700 hover:text-white dark:hover:bg-slate-200 dark:hover:text-slate-800 hover:-translate-y-0.5 focus:outline-none focus:ring-2 focus:ring-slate-400 dark:focus:ring-slate-500 transition-all duration-200"
},
"children": [
{
"type": "basic",
"name": "Icon",
"props": {
"name": "puzzle-piece",
"className": "text-lg md:text-xl"
}
},
{
"type": "basic",
"name": "Span",
"props": {
"className": "text-center leading-tight"
},
"text": "$t:admin.dashboard.quick_menu.modules"
}
]
},
{
"type": "extension_point",
"name": "admin_dashboard_quick_menu",
"default": [],
"props": {
"className": "contents"
}
}
]
},
{
"type": "extension_point",
"name": "admin_dashboard_commerce",
"default": []
},
{
"type": "extension_point",
"name": "admin_dashboard_community",
"default": []
},
{
"id": "secondary_stats_badges",
"type": "basic",
"name": "Div",
"blur_until_loaded": {
"enabled": true,
"data_sources": "dashboard_stats"
},
"props": {
"className": "flex-center flex-wrap gap-2 md:gap-3 mb-2"
},
"children": [
{
"id": "modules_badge",
"permissions": [
"core.modules.read"
],
"type": "basic",
"name": "Div",
"props": {
"className": "inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full bg-white dark:bg-slate-700 text-sm font-medium text-slate-700 dark:text-slate-300 cursor-pointer hover:bg-slate-50 dark:hover:bg-slate-600 transition-colors"
},
"children": [
{
"id": "modules_badge_icon",
"type": "basic",
"name": "Icon",
"props": {
"name": "cube",
"size": "sm"
}
},
{
"id": "modules_badge_label",
"type": "basic",
"name": "Span",
"text": "$t:admin.dashboard.stats.installed_modules"
},
{
"id": "modules_badge_value",
"type": "basic",
"name": "Span",
"props": {
"className": "font-bold"
},
"text": "{{dashboard_stats?.data?.installed_modules?.active ?? 0}}/{{dashboard_stats?.data?.installed_modules?.total ?? 0}}"
}
],
"actions": [
{
"type": "click",
"handler": "navigate",
"params": {
"path": "/admin/modules"
}
}
]
},
{
"id": "plugins_badge",
"permissions": [
"core.plugins.read"
],
"type": "basic",
"name": "Div",
"props": {
"className": "inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full bg-white dark:bg-slate-700 text-sm font-medium text-slate-700 dark:text-slate-300 cursor-pointer hover:bg-slate-50 dark:hover:bg-slate-600 transition-colors"
},
"children": [
{
"id": "plugins_badge_icon",
"type": "basic",
"name": "Icon",
"props": {
"name": "puzzle-piece",
"size": "sm"
}
},
{
"id": "plugins_badge_label",
"type": "basic",
"name": "Span",
"text": "$t:admin.dashboard.stats.active_plugins"
},
{
"id": "plugins_badge_value",
"type": "basic",
"name": "Span",
"props": {
"className": "font-bold"
},
"text": "{{dashboard_stats?.data?.active_plugins?.active ?? 0}}/{{dashboard_stats?.data?.active_plugins?.total ?? 0}}"
}
],
"actions": [
{
"type": "click",
"handler": "navigate",
"params": {
"path": "/admin/plugins"
}
}
]
},
{
"id": "templates_badge",
"permissions": [
"core.templates.read"
],
"type": "basic",
"name": "Div",
"props": {
"className": "inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full bg-white dark:bg-slate-700 text-sm font-medium text-slate-700 dark:text-slate-300 cursor-pointer hover:bg-slate-50 dark:hover:bg-slate-600 transition-colors"
},
"children": [
{
"id": "templates_badge_icon",
"type": "basic",
"name": "Icon",
"props": {
"name": "palette",
"size": "sm"
}
},
{
"id": "templates_badge_label",
"type": "basic",
"name": "Span",
"text": "$t:admin.dashboard.stats.active_templates"
},
{
"id": "templates_badge_value",
"type": "basic",
"name": "Span",
"props": {
"className": "font-bold"
},
"text": "{{dashboard_stats?.data?.installed_templates?.active ?? 0}}/{{dashboard_stats?.data?.installed_templates?.total ?? 0}}"
}
],
"actions": [
{
"type": "click",
"handler": "navigate",
"params": {
"path": "/admin/templates/admin"
}
}
]
},
{
"id": "language_packs_badge",
"permissions": [
"core.language_packs.read"
],
"type": "basic",
"name": "Div",
"props": {
"className": "inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full bg-white dark:bg-slate-700 text-sm font-medium text-slate-700 dark:text-slate-300 cursor-pointer hover:bg-slate-50 dark:hover:bg-slate-600 transition-colors"
},
"children": [
{
"id": "language_packs_badge_icon",
"type": "basic",
"name": "Icon",
"props": {
"name": "language",
"size": "sm"
}
},
{
"id": "language_packs_badge_label",
"type": "basic",
"name": "Span",
"text": "$t:admin.dashboard.stats.active_language_packs"
},
{
"id": "language_packs_badge_value",
"type": "basic",
"name": "Span",
"props": {
"className": "font-bold"
},
"text": "{{dashboard_stats?.data?.language_packs?.active ?? 0}}/{{dashboard_stats?.data?.language_packs?.total ?? 0}}"
}
],
"actions": [
{
"type": "click",
"handler": "navigate",
"params": {
"path": "/admin/settings?tab=language_packs"
}
}
]
}
]
},
{
"id": "middle_section_grid",
"type": "basic",
"name": "Div",
"props": {
"className": "grid grid-cols-1 lg:grid-cols-2 gap-4 sm:gap-6 mb-6"
},
"children": [
{
"id": "recent_notifications_card",
"permissions": [
"core.notification-logs.read"
],
"type": "basic",
"name": "Div",
"blur_until_loaded": {
"enabled": true,
"data_sources": "dashboard_recent_notifications"
},
"props": {
"className": "bg-slate-100 dark:bg-slate-800 rounded-xl md:rounded-2xl p-4 md:p-6"
},
"children": [
{
"id": "recent_notifications_header",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-start justify-between mb-4"
},
"children": [
{
"type": "basic",
"name": "Div",
"children": [
{
"id": "recent_notifications_title",
"type": "basic",
"name": "H2",
"props": {
"className": "section-heading"
},
"text": "$t:admin.dashboard.recent_notifications.title"
},
{
"id": "recent_notifications_subtitle",
"type": "basic",
"name": "P",
"props": {
"className": "text-label-subtle"
},
"text": "$t:admin.dashboard.recent_notifications.subtitle"
}
]
},
{
"id": "recent_notifications_more_btn",
"type": "basic",
"name": "Button",
"props": {
"type": "button",
"className": "text-tertiary hover:text-gray-700 dark:hover:text-gray-200 transition-colors cursor-pointer"
},
"text": "$t:admin.dashboard.extension_status.more",
"actions": [
{
"type": "click",
"handler": "navigate",
"params": {
"path": "/admin/notification-logs"
}
}
]
}
]
},
{
"id": "recent_notifications_empty",
"type": "basic",
"name": "P",
"if": "{{(dashboard_recent_notifications?.data ?? []).length === 0}}",
"props": {
"className": "text-sm text-slate-500 dark:text-slate-400 text-center py-6"
},
"text": "$t:admin.dashboard.recent_notifications.empty"
},
{
"id": "recent_notifications_list",
"type": "basic",
"name": "Div",
"if": "{{(dashboard_recent_notifications?.data ?? []).length > 0}}",
"props": {
"className": "mb-4"
},
"children": [
{
"id": "recent_notification_item_{{$idx}}",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-start gap-3"
},
"iteration": {
"source": "dashboard_recent_notifications?.data",
"item_var": "notification",
"index_var": "$idx"
},
"children": [
{
"id": "recent_notification_dot_{{$idx}}",
"type": "basic",
"name": "Div",
"props": {
"className": "{{notification?.status === 'failed' ? 'w-2 h-2 mt-2 rounded-full bg-rose-500 dark:bg-rose-400 flex-shrink-0' : (notification?.status === 'skipped' ? 'w-2 h-2 mt-2 rounded-full bg-slate-400 dark:bg-slate-300 flex-shrink-0' : 'w-2 h-2 mt-2 rounded-full bg-emerald-500 dark:bg-emerald-400 flex-shrink-0')}}"
}
},
{
"id": "recent_notification_content_{{$idx}}",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-1 min-w-0"
},
"children": [
{
"id": "recent_notification_subject_{{$idx}}",
"type": "basic",
"name": "P",
"props": {
"className": "text-heading truncate"
},
"text": "{{notification?.subject ?? ''}}"
},
{
"id": "recent_notification_recipient_{{$idx}}",
"type": "basic",
"name": "P",
"props": {
"className": "text-label-subtle truncate"
},
"text": "{{notification?.recipient ?? ''}}"
}
]
},
{
"id": "recent_notification_time_{{$idx}}",
"type": "basic",
"name": "Span",
"props": {
"className": "text-hint whitespace-nowrap"
},
"text": "{{notification?.time ?? ''}}"
}
]
}
]
}
]
},
{
"id": "recent_activity_card",
"permissions": [
"core.dashboard.activities"
],
"type": "basic",
"name": "Div",
"blur_until_loaded": {
"enabled": true,
"data_sources": "dashboard_activities"
},
"props": {
"className": "bg-slate-100 dark:bg-slate-800 rounded-xl md:rounded-2xl p-4 md:p-6"
},
"children": [
{
"id": "recent_activity_header",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-start justify-between mb-4"
},
"children": [
{
"type": "basic",
"name": "Div",
"children": [
{
"id": "recent_activity_title",
"type": "basic",
"name": "H2",
"props": {
"className": "section-heading"
},
"text": "$t:admin.dashboard.recent_activity.title"
},
{
"id": "recent_activity_subtitle",
"type": "basic",
"name": "P",
"props": {
"className": "text-label-subtle"
},
"text": "$t:admin.dashboard.recent_activity.subtitle"
}
]
},
{
"id": "activity_more_btn",
"type": "basic",
"name": "Button",
"props": {
"type": "button",
"className": "text-tertiary hover:text-gray-700 dark:hover:text-gray-200 transition-colors cursor-pointer"
},
"text": "$t:admin.dashboard.extension_status.more",
"actions": [
{
"type": "click",
"handler": "navigate",
"params": {
"path": "/admin/activity-logs"
}
}
]
}
]
},
{
"id": "activity_list",
"type": "basic",
"name": "Div",
"props": {},
"children": [
{
"id": "activity_item_{{$idx}}",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-start gap-3"
},
"iteration": {
"source": "dashboard_activities?.data",
"item_var": "activity",
"index_var": "$idx"
},
"children": [
{
"id": "activity_dot_{{$idx}}",
"type": "basic",
"name": "Div",
"props": {
"className": "w-2 h-2 mt-2 rounded-full bg-blue-500 dark:bg-blue-400 flex-shrink-0"
}
},
{
"id": "activity_content_{{$idx}}",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-1 min-w-0"
},
"children": [
{
"id": "activity_title_{{$idx}}",
"type": "basic",
"name": "P",
"props": {
"className": "text-heading"
},
"text": "{{activity.title}}"
},
{
"id": "activity_desc_{{$idx}}",
"type": "basic",
"name": "P",
"props": {
"className": "text-label-subtle"
},
"text": "{{activity.description}}"
}
]
},
{
"id": "activity_time_{{$idx}}",
"type": "basic",
"name": "Span",
"props": {
"className": "text-hint whitespace-nowrap"
},
"text": "{{activity.time}}"
}
]
}
]
}
]
}
]
},
{
"id": "extension_status_grid",
"type": "basic",
"name": "Div",
"props": {
"className": "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-6"
},
"children": [
{
"id": "module_status_card",
"permissions": [
"core.modules.read"
],
"type": "basic",
"name": "Div",
"blur_until_loaded": {
"enabled": true,
"data_sources": "dashboard_modules"
},
"props": {
"className": "bg-slate-100 dark:bg-slate-800 rounded-xl md:rounded-2xl p-4 md:p-6"
},
"children": [
{
"id": "module_status_header",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-between mb-4"
},
"children": [
{
"id": "module_header_left",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-center gap-2"
},
"children": [
{
"id": "module_header_text",
"type": "basic",
"name": "Div",
"children": [
{
"id": "module_status_title",
"type": "basic",
"name": "H2",
"props": {
"className": "section-heading"
},
"text": "$t:admin.dashboard.module_status.title"
},
{
"id": "module_status_subtitle",
"type": "basic",
"name": "P",
"props": {
"className": "text-label-subtle"
},
"text": "$t:admin.dashboard.module_status.subtitle"
}
]
}
]
},
{
"id": "module_more_btn",
"type": "basic",
"name": "Button",
"props": {
"type": "button",
"className": "text-tertiary hover:text-gray-700 dark:hover:text-gray-200 transition-colors cursor-pointer"
},
"text": "$t:admin.dashboard.extension_status.more",
"actions": [
{
"type": "click",
"handler": "navigate",
"params": {
"path": "/admin/modules"
}
}
]
}
]
},
{
"id": "module_list",
"type": "basic",
"name": "Div",
"props": {},
"children": [
{
"id": "module_item_{{$idx}}",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-between py-2"
},
"iteration": {
"source": "dashboard_modules?.data?.data",
"item_var": "module",
"index_var": "$idx"
},
"children": [
{
"id": "module_info_{{$idx}}",
"type": "basic",
"name": "Div",
"children": [
{
"id": "module_name_{{$idx}}",
"type": "basic",
"name": "P",
"props": {
"className": "text-heading"
},
"text": "{{module.name}}"
},
{
"id": "module_version_{{$idx}}",
"type": "basic",
"name": "P",
"props": {
"className": "text-tertiary"
},
"text": "v{{module.version}}"
}
]
},
{
"id": "module_badge_{{$idx}}",
"type": "basic",
"name": "Span",
"props": {
"className": "{{module.status === 'active' ? 'badge badge-green' : 'badge badge-gray'}}"
},
"text": "{{module.status === 'active' ? '$t:admin.dashboard.module_status.active' : '$t:admin.dashboard.module_status.inactive'}}"
}
]
}
]
}
]
},
{
"id": "plugin_status_card",
"permissions": [
"core.plugins.read"
],
"type": "basic",
"name": "Div",
"blur_until_loaded": {
"enabled": true,
"data_sources": "dashboard_plugins"
},
"props": {
"className": "bg-slate-100 dark:bg-slate-800 rounded-xl md:rounded-2xl p-4 md:p-6"
},
"children": [
{
"id": "plugin_status_header",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-between mb-4"
},
"children": [
{
"id": "plugin_header_left",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-center gap-2"
},
"children": [
{
"id": "plugin_header_text",
"type": "basic",
"name": "Div",
"children": [
{
"id": "plugin_status_title",
"type": "basic",
"name": "H2",
"props": {
"className": "section-heading"
},
"text": "$t:admin.dashboard.plugin_status.title"
},
{
"id": "plugin_status_subtitle",
"type": "basic",
"name": "P",
"props": {
"className": "text-label-subtle"
},
"text": "$t:admin.dashboard.plugin_status.subtitle"
}
]
}
]
},
{
"id": "plugin_more_btn",
"type": "basic",
"name": "Button",
"props": {
"type": "button",
"className": "text-tertiary hover:text-gray-700 dark:hover:text-gray-200 transition-colors cursor-pointer"
},
"text": "$t:admin.dashboard.extension_status.more",
"actions": [
{
"type": "click",
"handler": "navigate",
"params": {
"path": "/admin/plugins"
}
}
]
}
]
},
{
"id": "plugin_list",
"type": "basic",
"name": "Div",
"props": {},
"children": [
{
"id": "plugin_item_{{$idx}}",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-between py-2"
},
"iteration": {
"source": "dashboard_plugins?.data?.data",
"item_var": "plugin",
"index_var": "$idx"
},
"children": [
{
"id": "plugin_info_{{$idx}}",
"type": "basic",
"name": "Div",
"children": [
{
"id": "plugin_name_{{$idx}}",
"type": "basic",
"name": "P",
"props": {
"className": "text-heading"
},
"text": "{{plugin.name}}"
},
{
"id": "plugin_version_{{$idx}}",
"type": "basic",
"name": "P",
"props": {
"className": "text-tertiary"
},
"text": "v{{plugin.version}}"
}
]
},
{
"id": "plugin_badge_{{$idx}}",
"type": "basic",
"name": "Span",
"props": {
"className": "{{plugin.status === 'active' ? 'badge badge-green' : 'badge badge-gray'}}"
},
"text": "{{plugin.status === 'active' ? '$t:admin.dashboard.plugin_status.active' : '$t:admin.dashboard.plugin_status.inactive'}}"
}
]
}
]
}
]
},
{
"id": "template_status_card",
"permissions": [
"core.templates.read"
],
"type": "basic",
"name": "Div",
"blur_until_loaded": {
"enabled": true,
"data_sources": "dashboard_templates"
},
"props": {
"className": "bg-slate-100 dark:bg-slate-800 rounded-xl md:rounded-2xl p-4 md:p-6"
},
"children": [
{
"id": "template_status_header",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-between mb-4"
},
"children": [
{
"id": "template_header_left",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-center gap-2"
},
"children": [
{
"id": "template_header_text",
"type": "basic",
"name": "Div",
"children": [
{
"id": "template_status_title",
"type": "basic",
"name": "H2",
"props": {
"className": "section-heading"
},
"text": "$t:admin.dashboard.template_status.title"
},
{
"id": "template_status_subtitle",
"type": "basic",
"name": "P",
"props": {
"className": "text-label-subtle"
},
"text": "$t:admin.dashboard.template_status.subtitle"
}
]
}
]
},
{
"id": "template_more_btn",
"type": "basic",
"name": "Button",
"props": {
"type": "button",
"className": "text-tertiary hover:text-gray-700 dark:hover:text-gray-200 transition-colors cursor-pointer"
},
"text": "$t:admin.dashboard.extension_status.more",
"actions": [
{
"type": "click",
"handler": "navigate",
"params": {
"path": "/admin/templates/admin"
}
}
]
}
]
},
{
"id": "template_list",
"type": "basic",
"name": "Div",
"props": {},
"children": [
{
"id": "template_item_{{$idx}}",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-between py-2"
},
"iteration": {
"source": "dashboard_templates?.data?.data",
"item_var": "template",
"index_var": "$idx"
},
"children": [
{
"id": "template_info_{{$idx}}",
"type": "basic",
"name": "Div",
"children": [
{
"id": "template_name_{{$idx}}",
"type": "basic",
"name": "P",
"props": {
"className": "text-heading"
},
"text": "{{template.name}}"
},
{
"id": "template_version_{{$idx}}",
"type": "basic",
"name": "P",
"props": {
"className": "text-tertiary"
},
"text": "v{{template.version}}"
}
]
},
{
"id": "template_badge_{{$idx}}",
"type": "basic",
"name": "Span",
"props": {
"className": "{{template.status === 'active' ? 'badge badge-green' : 'badge badge-gray'}}"
},
"text": "{{template.status === 'active' ? '$t:admin.dashboard.template_status.active' : '$t:admin.dashboard.template_status.inactive'}}"
}
]
}
]
}
]
}
]
},
{
"id": "bottom_section_grid",
"type": "basic",
"name": "Div",
"props": {
"className": "mt-4 sm:mt-6"
},
"children": [
{
"id": "system_alerts_card",
"if": "{{(dashboard_alerts?.data?.data ? dashboard_alerts.data.data.length : (dashboard_alerts?.data?.length ?? 0)) > 0}}",
"permissions": [
"core.dashboard.alerts"
],
"type": "basic",
"name": "Div",
"blur_until_loaded": {
"enabled": true,
"data_sources": "dashboard_alerts"
},
"props": {
"className": "bg-slate-100 dark:bg-slate-800 rounded-xl md:rounded-2xl p-4 md:p-6"
},
"children": [
{
"id": "system_alerts_header",
"type": "basic",
"name": "Div",
"props": {
"className": "mb-4"
},
"children": [
{
"id": "system_alerts_title",
"type": "basic",
"name": "H2",
"props": {
"className": "section-heading"
},
"text": "$t:admin.dashboard.system_alerts.title"
},
{
"id": "system_alerts_subtitle",
"type": "basic",
"name": "P",
"props": {
"className": "text-label-subtle"
},
"text": "$t:admin.dashboard.system_alerts.subtitle"
}
]
},
{
"id": "alerts_list",
"type": "basic",
"name": "Div",
"props": {},
"children": [
{
"id": "alert_item_{{$idx}}",
"type": "basic",
"name": "Div",
"props": {
"className": "{{alert.subtype === 'incompatible_core' ? 'flex items-start gap-3 p-3 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800' : (alert.subtype === 'recovery_available' ? 'flex items-start gap-3 p-3 rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800' : 'flex items-start gap-3 p-3 rounded-lg bg-gray-50 dark:bg-gray-700')}}"
},
"iteration": {
"source": "{{dashboard_alerts?.data?.data ? dashboard_alerts.data.data : (dashboard_alerts?.data ?? [])}}",
"item_var": "alert",
"index_var": "$idx"
},
"children": [
{
"id": "alert_icon_{{$idx}}",
"type": "basic",
"name": "Icon",
"props": {
"name": "{{alert.icon ?? 'info-circle'}}",
"className": "{{alert.subtype === 'incompatible_core' ? 'text-amber-600 dark:text-amber-400 mt-0.5 shrink-0' : (alert.subtype === 'recovery_available' ? 'text-blue-600 dark:text-blue-400 mt-0.5 shrink-0' : 'text-gray-500 dark:text-gray-400 mt-0.5 shrink-0')}}"
}
},
{
"id": "alert_content_{{$idx}}",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-1 min-w-0"
},
"children": [
{
"id": "alert_title_{{$idx}}",
"type": "basic",
"name": "P",
"props": {
"className": "text-heading"
},
"text": "{{alert.title ?? ''}}"
},
{
"id": "alert_message_{{$idx}}",
"type": "basic",
"name": "P",
"if": "{{alert.message}}",
"props": {
"className": "text-secondary text-xs mt-0.5"
},
"text": "{{alert.message ?? ''}}"
},
{
"id": "alert_time_{{$idx}}",
"type": "basic",
"name": "P",
"if": "{{alert.time}}",
"props": {
"className": "form-hint"
},
"text": "{{alert.time ?? ''}}"
},
{
"id": "alert_actions_{{$idx}}",
"type": "basic",
"name": "Div",
"props": {
"className": "flex-center gap-2 mt-2"
},
"children": [
{
"id": "alert_recover_button_{{$idx}}",
"type": "basic",
"name": "Button",
"if": "{{alert.subtype === 'recovery_available'}}",
"props": {
"type": "button",
"className": "inline-flex items-center gap-1 px-3 py-1 text-xs bg-blue-600 dark:bg-blue-500 hover:bg-blue-700 dark:hover:bg-blue-600 text-white rounded"
},
"actions": [
{
"type": "click",
"handler": "apiCall",
"auth_required": true,
"target": "{{alert.recover_endpoint}}",
"params": {
"method": "POST"
},
"onSuccess": [
{
"handler": "toast",
"params": {
"type": "success",
"message": "$t:extensions.alerts.recovered_success"
}
},
{
"handler": "refetchDataSource",
"params": {
"dataSourceId": "dashboard_alerts"
}
}
],
"onError": [
{
"handler": "toast",
"params": {
"type": "error",
"message": "{{error.message}}"
}
}
]
}
],
"children": [
{
"type": "basic",
"name": "Icon",
"props": {
"name": "check-circle",
"size": "xs"
}
},
{
"type": "basic",
"name": "Span",
"text": "$t:extensions.alerts.recover_action"
}
]
},
{
"id": "alert_dismiss_button_{{$idx}}",
"type": "basic",
"name": "Button",
"if": "{{alert.extension_type && alert.identifier}}",
"props": {
"type": "button",
"className": "inline-flex items-center gap-1 px-3 py-1 text-xs text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600 rounded",
"aria-label": "$t:extensions.alerts.dismiss_action"
},
"actions": [
{
"type": "click",
"handler": "apiCall",
"auth_required": true,
"target": "/api/admin/extensions/{{alert.extension_type}}/{{alert.identifier}}/dismiss",
"params": {
"method": "POST"
},
"onSuccess": [
{
"handler": "refetchDataSource",
"params": {
"dataSourceId": "dashboard_alerts"
}
}
],
"onError": [
{
"handler": "toast",
"params": {
"type": "error",
"message": "{{error.message}}"
}
}
]
}
],
"children": [
{
"type": "basic",
"name": "Icon",
"props": {
"name": "times",
"size": "xs"
}
},
{
"type": "basic",
"name": "Span",
"text": "$t:extensions.alerts.dismiss_action"
}
]
}
]
}
]
}
]
}
]
}
]
}
]
}
]
}
]
},
"transition_overlay": {
"wait_for": [
"dashboard_stats",
"dashboard_activities",
"dashboard_modules",
"dashboard_plugins",
"dashboard_templates",
"dashboard_recent_notifications"
]
}
}