diff --git a/frontend/src/router/routes/core.js b/frontend/src/router/routes/core.js index 287a5f51..f171c784 100644 --- a/frontend/src/router/routes/core.js +++ b/frontend/src/router/routes/core.js @@ -36,40 +36,158 @@ export default [ meta: { requiresAuth: true, requiresAdmin: true } }, { - path: 'settings/vendors', - name: 'vendors', - component: () => import('../../views/vendors/VendorsList.vue'), + path: 'settings/catalog', + name: 'model-catalog', + component: () => import('../../views/settings/ModelCatalogHub.vue'), + meta: { requiresAuth: true, requiresAdmin: true }, + children: [ + { + path: 'vendors', + name: 'model-catalog-vendors', + component: () => import('../../views/vendors/VendorsList.vue') + }, + { + path: 'types', + name: 'model-catalog-types', + component: () => import('../../views/settings/ModelTypesList.vue') + }, + { + path: 'models', + name: 'model-catalog-models', + component: () => import('../../views/settings/ModelsList.vue') + }, + { + path: 'operatingsystems', + name: 'model-catalog-os', + component: () => import('../../.plugins-staged/computers/views/OperatingSystemsList.vue') + } + ] + }, + { + path: 'settings/assetconfig', + name: 'asset-config', + component: () => import('../../views/settings/AssetConfigHub.vue'), + meta: { requiresAuth: true, requiresAdmin: true }, + children: [ + { + path: 'statuses', + name: 'asset-config-statuses', + component: () => import('../../views/settings/StatusesList.vue') + }, + { + path: 'relationships', + name: 'asset-config-relationships', + component: () => import('../../views/settings/RelationshipTypesList.vue') + }, + { + path: 'fields', + name: 'asset-config-fields', + component: () => import('../../views/settings/CustomFieldsList.vue') + }, + { + path: 'identifiers', + name: 'asset-config-identifiers', + component: () => import('../../views/settings/AssetIdentifiersSettings.vue') + } + ] + }, + { + path: 'settings/security', + name: 'security', + component: () => import('../../views/settings/SecurityHub.vue'), + meta: { requiresAuth: true, requiresAdmin: true }, + children: [ + { + path: 'authentication', + name: 'security-authentication', + component: () => import('../../views/settings/AuthenticationSettings.vue') + }, + { + path: 'tokens', + name: 'security-tokens', + component: () => import('../../views/settings/ApiTokensList.vue') + }, + { + path: 'users', + name: 'security-users', + component: () => import('../../views/settings/UsersList.vue') + }, + { + path: 'audit', + name: 'security-audit', + component: () => import('../../views/settings/AuditSettings.vue') + }, + { + path: 'auditlogs', + name: 'security-auditlogs', + component: () => import('../../views/settings/AuditLogs.vue') + } + ] + }, + { + path: 'settings/siteconfig', + name: 'site-config', + component: () => import('../../views/settings/SiteConfigHub.vue'), + meta: { requiresAuth: true, requiresAdmin: true }, + children: [ + { + path: 'site', + name: 'site-config-site', + component: () => import('../../views/settings/SiteSettings.vue') + }, + { + path: 'floormap', + name: 'site-config-floormap', + component: () => import('../../views/settings/FloorMapSettings.vue') + }, + { + path: 'branding', + name: 'site-config-branding', + component: () => import('../../views/settings/BrandingSettings.vue') + }, + { + path: 'imports', + name: 'site-config-imports', + component: () => import('../../views/settings/ImportSiteAssets.vue') + } + ] + }, + { + path: 'settings/integrations', + name: 'integrations', + component: () => import('../../views/settings/IntegrationsHub.vue'), meta: { requiresAuth: true, requiresAdmin: true } }, { - path: 'settings/modeltypes', - name: 'modeltypes', - component: () => import('../../views/settings/ModelTypesList.vue'), + path: 'settings/servicenow', + name: 'servicenow-settings', + component: () => import('../../views/settings/ServiceNowSettings.vue'), meta: { requiresAuth: true, requiresAdmin: true } }, { path: 'settings/locations', - name: 'locations', - component: () => import('../../views/settings/LocationsList.vue'), - meta: { requiresAuth: true, requiresAdmin: true } + name: 'locations-hub', + component: () => import('../../views/settings/LocationsHub.vue'), + meta: { requiresAuth: true, requiresAdmin: true }, + children: [ + { + path: 'list', + name: 'locations-hub-locations', + component: () => import('../../views/settings/LocationsList.vue') + }, + { + path: 'businessunits', + name: 'locations-hub-businessunits', + component: () => import('../../views/settings/BusinessUnitsList.vue') + } + ] }, { - path: 'settings/statuses', - name: 'statuses', - component: () => import('../../views/settings/StatusesList.vue'), - meta: { requiresAuth: true, requiresAdmin: true } - }, - { - path: 'settings/models', - name: 'models', - component: () => import('../../views/settings/ModelsList.vue'), - meta: { requiresAuth: true, requiresAdmin: true } - }, - { - path: 'settings/businessunits', - name: 'businessunits', - component: () => import('../../views/settings/BusinessUnitsList.vue'), - meta: { requiresAuth: true, requiresAdmin: true } + path: 'settings/network', + name: 'network-hub', + component: () => import('../../views/settings/NetworkHub.vue'), + meta: { requiresAuth: true, requiresAdmin: true }, + children: [] }, { path: 'settings/supportteams', @@ -83,18 +201,6 @@ export default [ component: () => import('../../views/settings/DashboardDefaultsList.vue'), meta: { requiresAuth: true, requiresAdmin: true } }, - { - path: 'settings/site', - name: 'site-settings', - component: () => import('../../views/settings/SiteSettings.vue'), - meta: { requiresAuth: true, requiresAdmin: true } - }, - { - path: 'settings/relationshiptypes', - name: 'relationship-types', - component: () => import('../../views/settings/RelationshipTypesList.vue'), - meta: { requiresAuth: true, requiresAdmin: true } - }, { path: 'settings/locationtypes', name: 'location-types', @@ -103,15 +209,22 @@ export default [ }, { path: 'settings/assettypes', - name: 'asset-types', - component: () => import('../../views/settings/AssetTypesList.vue'), - meta: { requiresAuth: true, requiresAdmin: true } - }, - { - path: 'settings/customfields', - name: 'custom-fields', - component: () => import('../../views/settings/CustomFieldsList.vue'), - meta: { requiresAuth: true, requiresAdmin: true } + component: () => import('../../views/settings/AssetTypeHub.vue'), + meta: { requiresAuth: true, requiresAdmin: true }, + children: [ + { + path: '', + name: 'asset-types-hub', + component: () => import('../../views/settings/asset-types/TypesTabWrapper.vue'), + props: route => ({ assettypeid: route.params.assettypeid || route.params.assettypeid }) + }, + { + path: ':assettypeid/subtypes', + name: 'asset-types-hub-subtypes', + component: () => import('../../views/settings/asset-types/TypesTabWrapper.vue'), + props: route => ({ assettypeid: route.params.assettypeid }) + }, + ] }, // The old monolithic System Settings tab page is split into one routed page // per section (below). This redirect keeps old bookmarks working: bare @@ -138,24 +251,6 @@ export default [ }, meta: { requiresAuth: true, requiresAdmin: true } }, - { - path: 'settings/servicenow', - name: 'servicenow-settings', - component: () => import('../../views/settings/ServiceNowSettings.vue'), - meta: { requiresAuth: true, requiresAdmin: true } - }, - { - path: 'settings/branding', - name: 'branding-settings', - component: () => import('../../views/settings/BrandingSettings.vue'), - meta: { requiresAuth: true, requiresAdmin: true } - }, - { - path: 'settings/importsiteassets', - name: 'import-site-assets', - component: () => import('../../views/settings/ImportSiteAssets.vue'), - meta: { requiresAuth: true, requiresAdmin: true } - }, { path: 'settings/printing', name: 'printing-settings', @@ -168,60 +263,18 @@ export default [ component: () => import('../../views/settings/EmailSettings.vue'), meta: { requiresAuth: true, requiresAdmin: true } }, - { - path: 'settings/audit', - name: 'audit-settings', - component: () => import('../../views/settings/AuditSettings.vue'), - meta: { requiresAuth: true, requiresAdmin: true } - }, - { - path: 'settings/authentication', - name: 'authentication-settings', - component: () => import('../../views/settings/AuthenticationSettings.vue'), - meta: { requiresAuth: true, requiresAdmin: true } - }, - { - path: 'settings/apitokens', - name: 'apitokens-settings', - component: () => import('../../views/settings/ApiTokensList.vue'), - meta: { requiresAuth: true, requiresAdmin: true } - }, - { - path: 'settings/assetidentifiers', - name: 'asset-identifiers-settings', - component: () => import('../../views/settings/AssetIdentifiersSettings.vue'), - meta: { requiresAuth: true, requiresAdmin: true } - }, { path: 'settings/globalsearch', name: 'global-search-settings', component: () => import('../../views/settings/GlobalSearchSettings.vue'), meta: { requiresAuth: true, requiresAdmin: true } }, - { - path: 'settings/floormap', - name: 'floor-map-settings', - component: () => import('../../views/settings/FloorMapSettings.vue'), - meta: { requiresAuth: true, requiresAdmin: true } - }, { path: 'settings/plugins', name: 'plugins', component: () => import('../../views/settings/PluginsList.vue'), meta: { requiresAuth: true, requiresAdmin: true } }, - { - path: 'settings/auditlogs', - name: 'audit-logs', - component: () => import('../../views/settings/AuditLogs.vue'), - meta: { requiresAuth: true, requiresAdmin: true } - }, - { - path: 'settings/users', - name: 'users', - component: () => import('../../views/settings/UsersList.vue'), - meta: { requiresAuth: true, requiresAdmin: true } - }, { path: 'settings/shopdbbackups', name: 'shopdbbackups', diff --git a/frontend/src/views/settings/ApiTokensList.vue b/frontend/src/views/settings/ApiTokensList.vue index 8860b9c7..b39f5eba 100644 --- a/frontend/src/views/settings/ApiTokensList.vue +++ b/frontend/src/views/settings/ApiTokensList.vue @@ -327,6 +327,11 @@ async function loadData() { } } catch (err) { console.error('Error loading API tokens:', err) + if (err.response?.data?.error) { + toast.error(err.response.data.error) + } else { + toast.error('Failed to load tokens or permissions. Please refresh the page.') + } } finally { loading.value = false } diff --git a/frontend/src/views/settings/AssetConfigHub.vue b/frontend/src/views/settings/AssetConfigHub.vue new file mode 100644 index 00000000..2e377183 --- /dev/null +++ b/frontend/src/views/settings/AssetConfigHub.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/frontend/src/views/settings/AssetTypeHub.vue b/frontend/src/views/settings/AssetTypeHub.vue new file mode 100644 index 00000000..8ce5d917 --- /dev/null +++ b/frontend/src/views/settings/AssetTypeHub.vue @@ -0,0 +1,306 @@ + + + + + diff --git a/frontend/src/views/settings/IntegrationsHub.vue b/frontend/src/views/settings/IntegrationsHub.vue new file mode 100644 index 00000000..c851ae57 --- /dev/null +++ b/frontend/src/views/settings/IntegrationsHub.vue @@ -0,0 +1,112 @@ + + + + + diff --git a/frontend/src/views/settings/IntegrationsLayout.vue b/frontend/src/views/settings/IntegrationsLayout.vue new file mode 100644 index 00000000..7e350672 --- /dev/null +++ b/frontend/src/views/settings/IntegrationsLayout.vue @@ -0,0 +1,90 @@ + + + diff --git a/frontend/src/views/settings/LocationsHub.vue b/frontend/src/views/settings/LocationsHub.vue new file mode 100644 index 00000000..1720f339 --- /dev/null +++ b/frontend/src/views/settings/LocationsHub.vue @@ -0,0 +1,104 @@ + + + + + diff --git a/frontend/src/views/settings/ModelCatalogHub.vue b/frontend/src/views/settings/ModelCatalogHub.vue new file mode 100644 index 00000000..bc4c20bd --- /dev/null +++ b/frontend/src/views/settings/ModelCatalogHub.vue @@ -0,0 +1,112 @@ + + + + + diff --git a/frontend/src/views/settings/NetworkHub.vue b/frontend/src/views/settings/NetworkHub.vue new file mode 100644 index 00000000..2af0ddd8 --- /dev/null +++ b/frontend/src/views/settings/NetworkHub.vue @@ -0,0 +1,104 @@ + + + + + diff --git a/frontend/src/views/settings/SecurityHub.vue b/frontend/src/views/settings/SecurityHub.vue new file mode 100644 index 00000000..c9c9e618 --- /dev/null +++ b/frontend/src/views/settings/SecurityHub.vue @@ -0,0 +1,118 @@ + + + + + diff --git a/frontend/src/views/settings/ServiceNowSettings.vue b/frontend/src/views/settings/ServiceNowSettings.vue index ffcd7a8b..8de20c4e 100644 --- a/frontend/src/views/settings/ServiceNowSettings.vue +++ b/frontend/src/views/settings/ServiceNowSettings.vue @@ -1,7 +1,32 @@