@quazardous/qdadm 2.30.0 → 2.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/styles/qdadm.css +1 -1
- package/package.json +1 -1
- package/src/components/layout/AppLayout.vue +23 -11
- package/src/components/layout/defaults/DefaultMenu.vue +23 -11
- package/src/components/show/ShowDisplay.vue +16 -11
- package/src/composables/useEntityItemShowPage.ts +1 -0
- package/src/composables/useListPage.ts +1 -1
- package/src/composables/useLiveEntity.ts +25 -5
- package/src/composables/useNavigation.ts +39 -3
- package/src/entity/EntityManager.types.ts +16 -1
- package/src/index.ts +1 -1
- package/src/kernel/Kernel.registries.ts +1 -1
- package/src/kernel/Kernel.ts +3 -0
- package/src/kernel/Kernel.types.ts +5 -0
- package/src/kernel/KernelContext.types.ts +11 -1
- package/src/module/moduleRegistry.ts +17 -1
- package/src/styles/_sidebar.scss +8 -0
- package/src/types/index.ts +2 -0
- package/src/utils/formatters.ts +33 -14
- package/src/utils/index.ts +2 -0
package/dist/styles/qdadm.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{--fad-text-primary: var(--p-text-color);--fad-text-secondary: var(--p-text-muted-color);--fad-text-disabled: var(--p-surface-400);--fad-text-inverse: var(--p-surface-0);--fad-surface-ground: var(--p-surface-ground);--fad-surface-card: var(--p-surface-card);--fad-surface-overlay: var(--p-surface-overlay);--fad-surface-border: var(--p-surface-200);--fad-surface-hover: var(--p-surface-100);--fad-primary: var(--p-primary-color);--fad-primary-hover: var(--p-primary-600);--fad-primary-text: var(--p-primary-contrast-color);--fad-success: var(--p-green-500);--fad-warning: var(--p-orange-500);--fad-danger: var(--p-red-500);--fad-info: var(--p-blue-500);--fad-space-2xs: 0.125rem;--fad-space-xs: 0.25rem;--fad-space-sm: 0.375rem;--fad-space-md: 0.75rem;--fad-space-lg: 1rem;--fad-space-xl: 1.5rem;--fad-space-2xl: 2rem;--fad-font-family: Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif;--fad-font-size-2xs: 0.6875rem;--fad-font-size-xs: 0.75rem;--fad-font-size-sm: 0.875rem;--fad-font-size-md: 0.9375rem;--fad-font-size-lg: 1rem;--fad-font-size-xl: 1.125rem;--fad-font-size-2xl: 1.25rem;--fad-font-weight-light: 300;--fad-font-weight-normal: 400;--fad-font-weight-medium: 500;--fad-font-weight-semibold: 600;--fad-font-weight-bold: 700;--fad-line-height-tight: 1.25;--fad-line-height-normal: 1.5;--fad-line-height-relaxed: 1.75;--fad-border-width: 1px;--fad-border-color: var(--fad-surface-border);--fad-radius-sm: 0.25rem;--fad-radius-md: 0.375rem;--fad-radius-lg: 0.5rem;--fad-radius-xl: 0.75rem;--fad-radius-full: 9999px;--fad-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);--fad-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);--fad-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);--fad-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1);--fad-transition-instant: 50ms ease;--fad-transition-fast: 100ms ease;--fad-transition-normal: 150ms ease;--fad-transition-slow: 200ms ease;--fad-z-dropdown: 1000;--fad-z-sticky: 1020;--fad-z-fixed: 1030;--fad-z-modal-backdrop: 1040;--fad-z-modal: 1050;--fad-z-popover: 1060;--fad-z-tooltip: 1070;--fad-sidebar-width: 15rem;--fad-sidebar-width-tablet: 13rem;--fad-sidebar-width-collapsed: 2.5rem;--fad-header-height: 2.75rem;--fad-content-max-width: 87.5rem;--fad-nav-item-height: 2rem;--fad-nav-item-padding-x: 0.75rem;--fad-nav-item-padding-y: 0.5rem;--fad-nav-icon-size: 1rem;--fad-nav-section-gap: 0.5rem}.dark-mode{--fad-surface-border: var(--p-surface-700);--fad-surface-hover: var(--p-surface-800);--fad-text-disabled: var(--p-surface-500)}.d-none{display:none !important}.d-block{display:block !important}.d-flex{display:flex !important}.d-inline-flex{display:inline-flex !important}.d-grid{display:grid !important}.flex-row{flex-direction:row}.flex-column{flex-direction:column}.flex-wrap{flex-wrap:wrap}.flex-1{flex:1}.flex-auto{flex:auto}.flex-none{flex:none}.items-start{align-items:flex-start}.items-center{align-items:center}.items-end{align-items:flex-end}.items-stretch{align-items:stretch}.justify-start{justify-content:flex-start}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.gap-xs{gap:.25rem}.gap-sm{gap:.375rem}.gap-md{gap:.75rem}.gap-lg{gap:1rem}.gap-xl{gap:1.5rem}.w-full{width:100% !important}.w-auto{width:auto !important}.w-50{width:50%}.max-w-full{max-width:100%}.m-0{margin:0}.m-auto{margin:auto}.mt-0{margin-top:0}.mt-xs{margin-top:.25rem}.mt-sm{margin-top:.375rem}.mt-md{margin-top:.75rem}.mt-lg{margin-top:1rem}.mt-xl{margin-top:1.5rem}.mb-0{margin-bottom:0}.mb-xs{margin-bottom:.25rem}.mb-sm{margin-bottom:.375rem}.mb-md{margin-bottom:.75rem}.mb-lg{margin-bottom:1rem}.mb-xl{margin-bottom:1.5rem}.ml-auto{margin-left:auto}.mr-auto{margin-right:auto}.mx-auto{margin-left:auto;margin-right:auto}.p-0{padding:0}.p-xs{padding:.25rem}.p-sm{padding:.375rem}.p-md{padding:.75rem}.p-lg{padding:1rem}.p-xl{padding:1.5rem}.px-md{padding-left:.75rem;padding-right:.75rem}.py-md{padding-top:.75rem;padding-bottom:.75rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-xs{font-size:.75rem}.text-sm{font-size:.875rem}.text-md{font-size:.9375rem}.text-lg{font-size:1rem}.text-xl{font-size:1.125rem}.text-2xl{font-size:1.25rem}.font-normal{font-weight:400}.font-medium{font-weight:500}.font-semibold{font-weight:600}.font-bold{font-weight:700}.text-primary{color:var(--fad-text-primary)}.text-secondary{color:var(--fad-text-secondary)}.text-muted{color:var(--fad-text-secondary)}.text-success{color:var(--fad-success)}.text-warning{color:var(--fad-warning)}.text-danger{color:var(--fad-danger)}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.visible{visibility:visible}.invisible{visibility:hidden}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-100{opacity:1}.border{border:1px solid var(--fad-border-color)}.border-0{border:none}.border-top{border-top:1px solid var(--fad-border-color)}.border-bottom{border-bottom:1px solid var(--fad-border-color)}.rounded-sm{border-radius:.25rem}.rounded-md{border-radius:.375rem}.rounded-lg{border-radius:.5rem}.rounded-full{border-radius:9999px}.cursor-pointer{cursor:pointer}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.overflow-hidden{overflow:hidden}.overflow-auto{overflow:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-auto{overflow-x:auto}.relative{position:relative}.absolute{position:absolute}.fixed{position:fixed}.sticky{position:sticky}.fad-card{background:var(--fad-surface-card);border:1px solid var(--fad-border-color);border-radius:.5rem;padding:1rem}.fad-card-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:.75rem;padding-bottom:.75rem;border-bottom:1px solid var(--fad-border-color)}.fad-card-title{font-size:1rem;font-weight:600;color:var(--fad-text-primary);margin:0}.fad-card-body{color:var(--fad-text-primary)}.fad-card-footer{display:flex;align-items:center;justify-content:flex-end;gap:.375rem;margin-top:.75rem;padding-top:.75rem;border-top:1px solid var(--fad-border-color)}.fad-form-field{display:flex;flex-direction:column;gap:.375rem;margin-bottom:1rem}.fad-form-label{font-weight:500;color:var(--fad-text-primary);font-size:.875rem}.fad-form-label-required::after{content:" *";color:var(--fad-danger)}.fad-form-help{font-size:.75rem;color:var(--fad-text-secondary)}.fad-form-error{font-size:.75rem;color:var(--fad-danger)}.fad-form-row{display:grid;grid-template-columns:repeat(auto-fit, minmax(250px, 1fr));gap:.75rem}.fad-form-section-title{font-size:1rem;font-weight:600;color:var(--fad-text-primary);margin:1.5rem 0 .75rem 0;padding-bottom:.375rem;border-bottom:1px solid var(--fad-border-color)}.fad-form-section-title:first-child{margin-top:0}.fad-form-field--dirty{position:relative}.fad-form-field--dirty::before{content:"";position:absolute;left:-8px;top:0;bottom:0;width:3px;background:var(--fad-warning);border-radius:9999px}.fad-form-field--error .fad-form-label{color:var(--fad-danger)}.fad-page-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:1rem;padding-bottom:.75rem}.fad-page-title{font-size:1.25rem;font-weight:600;color:var(--fad-text-primary);margin:0}.fad-page-actions{display:flex;gap:.375rem}.fad-empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem .75rem;text-align:center;color:var(--fad-text-secondary)}.fad-empty-state-icon{font-size:3rem;opacity:.5;margin-bottom:.75rem}.fad-empty-state-title{font-size:1rem;font-weight:500;color:var(--fad-text-primary);margin:0 0 .375rem 0}.fad-empty-state-description{font-size:.875rem;max-width:400px}.fad-loading{display:flex;align-items:center;justify-content:center;padding:2rem}.fad-loading-spinner{animation:fad-spin 1s linear infinite}@keyframes fad-spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}.fad-badge{display:inline-flex;align-items:center;padding:.25rem .375rem;font-size:.75rem;font-weight:500;border-radius:9999px;text-transform:uppercase;letter-spacing:.025em}.fad-badge--success{background:var(--p-green-100);color:var(--p-green-700)}.fad-badge--warning{background:var(--p-orange-100);color:var(--p-orange-700)}.fad-badge--danger{background:var(--p-red-100);color:var(--p-red-700)}.fad-badge--info{background:var(--p-blue-100);color:var(--p-blue-700)}.fad-badge--neutral{background:var(--p-surface-100);color:var(--p-surface-600)}.fad-tabs{display:flex;gap:.25rem;border-bottom:1px solid var(--fad-border-color);margin-bottom:1rem}.fad-tab{display:flex;align-items:center;gap:.25rem;padding:.375rem .75rem;font-size:.875rem;font-weight:500;color:var(--fad-text-secondary);background:rgba(0,0,0,0);border:none;border-bottom:2px solid rgba(0,0,0,0);cursor:pointer;transition:all 100ms ease;margin-bottom:-1px}.fad-tab:hover{color:var(--fad-text-primary)}.fad-tab--active{color:var(--fad-primary);border-bottom-color:var(--fad-primary)}.fad-tab-count{background:var(--p-surface-100);color:var(--fad-text-secondary);padding:.125rem .375rem;border-radius:9999px;font-size:.75rem}.fad-tab--active .fad-tab-count{background:var(--p-primary-100);color:var(--fad-primary)}.fad-page-nav{display:flex;justify-content:space-between;align-items:center;margin-bottom:.375rem}.fad-page-nav-breadcrumb{flex:1}.fad-page-nav-links{display:flex;align-items:center;gap:.375rem;font-size:.875rem}.fad-page-nav-separator{color:var(--fad-text-secondary)}.fad-page-nav-link{color:var(--fad-primary);text-decoration:none;transition:color 100ms ease}.fad-page-nav-link:hover{color:var(--fad-primary-hover);text-decoration:underline}.fad-page-nav-link--active{color:var(--fad-text-primary);font-weight:500;pointer-events:none}*{margin:0;padding:0;box-sizing:border-box}html,body,#app{height:100%;font-family:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif}input::placeholder,textarea::placeholder,.p-inputtext::placeholder,.p-autocomplete-input::placeholder,.p-textarea::placeholder,.p-select-label.p-placeholder,.p-dropdown-label.p-placeholder{color:#a0aec0 !important;opacity:1}.page-content{flex:1;padding:1rem}.page-header{margin-bottom:1rem}.page-title{font-size:1.25rem;font-weight:600;color:var(--p-surface-900)}.header-actions{display:flex;gap:.75rem;align-items:center}.p-tabpanels{padding-top:.75rem}.p-tabpanel>.form-field:first-child,.p-tabpanel>.form-row:first-child,.p-tabpanel>.form-grid:first-child{margin-top:.375rem}.info-block{background:var(--p-surface-50);padding:1rem;border-radius:.5rem}.info-block-title{margin:0 0 .75rem 0;font-size:.9375rem;color:var(--p-surface-600)}.info-block-title i{margin-right:.375rem}.info-label{font-weight:500;color:var(--p-surface-500);display:block;margin-bottom:.25rem}.info-label-sm{font-size:.75rem;font-weight:600;color:var(--p-surface-500);text-transform:uppercase;display:block;margin-bottom:.25rem}.value-lg{font-size:1.25rem;font-weight:600}.accent-block{background:var(--p-surface-50);border-left:3px solid var(--p-primary-500);padding:.75rem;border-radius:.25rem}.code-block{word-break:break-all;font-family:ui-monospace,"Cascadia Code","Source Code Pro",Menlo,Consolas,monospace;background:var(--p-surface-100);padding:.75rem;border-radius:.25rem}.section-header{display:flex;align-items:center;gap:.375rem;margin-bottom:.75rem}.section-header h3{margin:0;font-size:.9375rem;font-weight:600}.section-header i{color:var(--p-primary-500)}.section-description{color:var(--p-surface-500);font-size:.875rem;margin-bottom:.75rem}.section-title{margin:0 0 .75rem 0;font-size:.9375rem;font-weight:600;color:var(--p-surface-700)}.section-title--separator{border-top:1px solid var(--p-surface-200);padding-top:1rem}.section-title i{margin-right:.375rem}.image-gallery{display:grid;grid-template-columns:repeat(auto-fill, minmax(200px, 1fr));gap:.75rem}.image-card{position:relative;border-radius:.5rem;overflow:hidden;border:1px solid var(--p-surface-200)}.image-card img{width:100%;aspect-ratio:16/9;object-fit:cover}.image-card--clickable{cursor:pointer;transition:border-color 150ms ease,box-shadow 150ms ease}.image-card--clickable:hover{border-color:var(--p-primary-300);box-shadow:0 2px 8px rgba(0,0,0,.1)}.image-caption{padding:.375rem;background:var(--p-surface-50);font-size:.75rem;color:var(--p-surface-600)}.info-row{display:flex;justify-content:space-between;align-items:center;padding:.375rem 0;border-bottom:1px solid var(--p-surface-100)}.info-row:last-child{border-bottom:none}.layout-breadcrumb{padding:.75rem 1rem;background:var(--p-surface-0, white);border-bottom:1px solid var(--p-surface-200, #e2e8f0)}.layout-breadcrumb .p-breadcrumb-list{margin:0;padding:0}.breadcrumb-link{display:flex;align-items:center;gap:.375rem;color:var(--p-primary-600, #2563eb);text-decoration:none;font-size:.875rem}.breadcrumb-link:hover{text-decoration:underline}.breadcrumb-link i{font-size:.875rem}.breadcrumb-current{display:flex;align-items:center;gap:.375rem;color:var(--p-surface-600, #475569);font-size:.875rem}.breadcrumb-current i{font-size:.875rem}.breadcrumb{margin-bottom:.5rem}.breadcrumb-list{display:flex;align-items:center;gap:.5rem;list-style:none;margin:0;padding:0;font-size:.875rem}.breadcrumb-item{display:flex;align-items:center;gap:.5rem}.breadcrumb-item.is-current .breadcrumb-text{color:var(--p-text-color, #333);font-weight:500}.breadcrumb-separator{font-size:.75rem;color:var(--p-text-muted-color, #888)}.breadcrumb-item .breadcrumb-link{display:flex;align-items:center;gap:.375rem;color:var(--p-primary-color, #3b82f6);text-decoration:none;transition:color .2s}.breadcrumb-item .breadcrumb-link:hover{color:var(--p-primary-hover-color, #2563eb);text-decoration:underline}.breadcrumb-item .breadcrumb-link i{font-size:.875rem}.breadcrumb-text{display:flex;align-items:center;gap:.375rem;color:var(--p-text-muted-color, #888)}.form-grid{display:grid;grid-template-columns:repeat(2, 1fr);gap:1rem;padding:.75rem 0}@media(max-width: 767px){.form-grid{grid-template-columns:1fr}}.form-row{display:grid;grid-template-columns:repeat(2, 1fr);gap:1rem;align-items:end}.form-row-3{display:grid;grid-template-columns:repeat(3, 1fr);gap:.75rem;align-items:end}.form-row.metadata{display:grid;grid-template-columns:repeat(4, 1fr);gap:.75rem;padding:.75rem;background:var(--p-surface-50);border-radius:.5rem;align-items:end}.form-row.checkboxes{display:flex;gap:3rem}.form-row+.form-field,.form-field+.form-row{margin-top:1.25rem}.form-field{display:flex;flex-direction:column;gap:.375rem}.form-field label{font-weight:500;color:var(--p-surface-700)}.form-field small{color:var(--p-surface-500);font-size:.85rem}.form-field .p-inputtext,.form-field .p-textarea,.form-field .p-select,.form-field .p-multiselect,.form-field .p-inputwrapper,.form-field input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]),.form-field textarea{width:100% !important;box-sizing:border-box}.form-field>input[type=checkbox],.form-field>input[type=radio]{align-self:flex-start}.form-field .p-textarea,.form-field textarea{min-height:100px;resize:vertical}.meta-item{display:flex;flex-direction:column;gap:.25rem}.meta-item label{font-size:.85rem;color:var(--p-surface-500)}.meta-item span{font-weight:500}.toggle-field{flex-direction:row;align-items:center;gap:.75rem}.toggle-field label{min-width:120px}.checkbox-field{display:flex;align-items:center;gap:.375rem}.checkbox-field label{margin:0;cursor:pointer}.url-field,.lock-field,.envelope-field{display:flex;gap:.375rem;align-items:center}.url-field input,.lock-field input,.envelope-field input{flex:1}.field-hint,.form-hint{font-size:.8rem;color:var(--p-surface-400);margin-top:.25rem;display:block}.field-error{font-size:.8rem;color:var(--p-red-500);margin-top:.25rem;display:block}.editor-box{border:1px solid var(--p-surface-200);border-radius:.5rem;padding:.75rem;background:var(--p-surface-50)}.editor-chips{display:flex;flex-wrap:wrap;gap:.375rem;margin-bottom:.75rem;padding-bottom:.75rem;border-bottom:1px solid var(--p-surface-200)}.chip-code{font-family:ui-monospace,"Cascadia Code","Source Code Pro",Menlo,Consolas,monospace;font-size:.875rem}.chip-namespace{color:var(--p-surface-500)}.chip-action{color:var(--p-primary-600);font-weight:500}.chip-wildcard{color:var(--p-orange-500);font-weight:700}.info-grid{display:grid;grid-template-columns:repeat(2, 1fr);gap:.75rem}.info-grid>div{display:flex;flex-direction:column;gap:.25rem}.info-grid label{font-size:.85rem;color:var(--p-surface-500)}.info-item{display:flex;flex-direction:column;gap:.25rem}.info-item label{font-size:.75rem;font-weight:600;color:var(--p-surface-500);text-transform:uppercase}.info-value{font-size:.9rem}.timestamps-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));gap:.75rem}.timestamps-grid--separator{padding-top:.75rem;border-top:1px solid var(--p-surface-200)}.timestamp{display:flex;flex-direction:column;gap:.25rem}.timestamp label{font-size:.75rem;color:var(--text-color-secondary);text-transform:uppercase}.form-dirty-indicator{display:flex;align-items:center;gap:.375rem;padding:.375rem .75rem;background:color-mix(in srgb, var(--p-orange-100) 50%, transparent);border:1px solid var(--p-orange-300);border-radius:.375rem;color:var(--p-orange-700);font-size:.875rem;margin-bottom:.75rem}.form-dirty-indicator i{color:var(--p-orange-500)}.form-field.field-dirty .p-inputtext,.form-field.field-dirty .p-select,.form-field.field-dirty .p-textarea,.form-field.field-dirty .p-password input,.form-field.field-dirty .p-multiselect,.form-field.field-dirty .p-calendar input,.form-field.field-dirty .p-inputnumber input,.form-field.field-dirty .p-dropdown{border-color:#f97316 !important;box-shadow:0 0 0 1px #f97316 !important}.form-card{background:var(--p-surface-0);padding:1.5rem;border-radius:.5rem}.config-intro{display:flex;align-items:flex-start;gap:.75rem;padding:.75rem;background:var(--p-surface-50);border-radius:.5rem;margin-bottom:1rem}.config-intro i{font-size:1.25rem;color:var(--p-primary-500);margin-top:.125rem}.config-field{margin-bottom:1rem}.form-actions{display:flex;justify-content:space-between;align-items:center;margin-top:1.5rem;padding-top:1.5rem;border-top:1px solid var(--p-surface-200)}.form-actions-left{display:flex;align-items:center;gap:.5rem}.dirty-indicator{color:var(--p-orange-500);font-size:.5rem;animation:pulse 2s infinite}@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}.field-invalid input,.field-invalid textarea,.field-invalid .p-inputtext,.field-invalid .p-select,.field-invalid .p-dropdown{border-color:var(--p-red-500)}.field-invalid input:focus,.field-invalid textarea:focus,.field-invalid .p-inputtext:focus,.field-invalid .p-select:focus,.field-invalid .p-dropdown:focus{box-shadow:0 0 0 1px var(--p-red-500)}.form-page{display:flex;flex-direction:column;gap:1rem}.form-error-message{margin:1rem 0}.validation-summary{margin-bottom:1rem}.validation-errors{margin:0;padding-left:1.5rem}.validation-errors li{margin:.25rem 0}.stats-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));gap:1rem;margin-bottom:1.5rem}.stat-card{background:var(--p-surface-0);border-radius:.5rem;padding:1rem;border:1px solid var(--p-surface-200)}.stat-card-title{font-size:.875rem;color:var(--p-surface-500);margin-bottom:.375rem}.stat-card-value{font-size:2rem;font-weight:600;color:var(--p-surface-900)}.stat-card-subtitle{font-size:.75rem;color:var(--p-surface-400);margin-top:.25rem}.loading-state{display:flex;justify-content:center;align-items:center;padding:2rem}.loading-state--inline{padding:.75rem;gap:.75rem;color:var(--p-text-muted-color)}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:2rem;color:var(--text-color-secondary)}.empty-state--sm{padding:1rem}.empty-state--lg{padding:3rem}.empty-state-icon{font-size:2rem;margin-bottom:.5rem;opacity:.5}.empty-state--sm .empty-state-icon{font-size:1.5rem}.empty-state--lg .empty-state-icon{font-size:3rem;margin-bottom:1rem}.empty-state-title{margin:0 0 .25rem 0;font-weight:500;color:var(--text-color-secondary)}.empty-state--lg .empty-state-title{font-size:1.1rem}.empty-state-description{opacity:.7;max-width:300px}.empty-state-actions{margin-top:1rem}.error-message{background:var(--p-red-50);border:1px solid var(--p-red-200);border-radius:.375rem;padding:.75rem .75rem;color:var(--p-red-700);font-family:ui-monospace,"Cascadia Code","Source Code Pro",Menlo,Consolas,monospace;font-size:.85rem;white-space:pre-wrap;word-break:break-word}.error-content{background:var(--p-red-50);color:var(--p-red-700);padding:.75rem;border-radius:.5rem;overflow-x:auto;margin:0;font-size:.85rem;white-space:pre-wrap}:root[data-theme=dark] .error-message,:root[data-theme=dark] .error-content{background:rgba(239,68,68,.1);color:var(--p-red-400);border-color:var(--p-red-900)}.p-datatable .p-datatable-tbody>tr>td{padding:.5rem .75rem !important}.p-datatable .p-datatable-thead>tr>th{padding:.625rem .75rem !important}.table-actions{display:flex;gap:.25rem;justify-content:flex-start}.text-muted{color:var(--p-surface-400)}.status-tags,.type-tags{display:flex;gap:.25rem;flex-wrap:wrap}.external-link{display:flex;align-items:center;gap:.375rem;color:var(--p-primary-500);text-decoration:none}.external-link:hover{text-decoration:underline}.external-link i{font-size:.75rem;opacity:.6}.domain-cell{display:flex;align-items:center}.domain-link{display:flex;align-items:center;gap:.375rem;color:var(--p-primary-500);text-decoration:none}.domain-link:hover{text-decoration:underline}.domain-link i{font-size:.75rem;opacity:.6}.domain-tags{display:flex;gap:.25rem}.clickable-cell{cursor:pointer;color:var(--p-primary-color)}.clickable-cell:hover{text-decoration:underline}.entity-link{display:flex;align-items:center;gap:.375rem;text-decoration:none;color:var(--p-primary-color)}.entity-link:hover{text-decoration:underline}.entity-type{font-weight:500}.entity-id{font-size:.75rem;background:var(--p-surface-100);padding:.15rem .35rem;border-radius:3px}.tags-preview{display:flex;gap:.25rem;flex-wrap:wrap}.tags-preview .p-tag{font-size:.75rem}.p-role-label,.p-permission-label{display:inline-flex;align-items:center;background:rgba(0,0,0,0);font-size:.75rem;font-weight:normal !important;padding:.25rem .75rem;border-radius:var(--p-tag-border-radius, 6px);white-space:nowrap}.p-role-label--pill,.p-permission-label--pill{border-radius:999px}.p-role-label--more,.p-permission-label--more{border-style:dashed}.p-role-label{border:1px solid var(--p-blue-500);color:var(--p-blue-600)}.p-permission-label{border:1px solid var(--p-surface-400);color:var(--p-surface-600)}.p-role-labels,.p-permission-labels{display:flex;flex-wrap:wrap;gap:.25rem}.p-chip{background:none !important;border:1px solid var(--p-surface-300) !important;color:var(--p-surface-600) !important;font-size:.75rem !important;padding:.2rem .6rem !important}.datatable-row-clickable .p-datatable-tbody>tr{cursor:pointer}.datatable-row-clickable .p-datatable-tbody>tr:active{background:var(--p-primary-50) !important}.filter-bar{display:flex;justify-content:space-between;align-items:center;gap:.75rem;margin-bottom:.75rem}.filter-bar-left{display:flex;align-items:center;gap:.75rem}.filter-bar-right{display:flex;align-items:center;gap:.75rem}.p-select.filter-default,.p-autocomplete.filter-default .p-inputtext{border-color:var(--p-primary-400, #60a5fa) !important;box-shadow:0 0 0 1px var(--p-primary-400, #60a5fa) !important}.p-select.filter-modified,.p-autocomplete.filter-modified .p-inputtext{border-color:#f97316 !important;box-shadow:0 0 0 1px #f97316 !important}.p-select.filter-active,.p-inputtext.filter-active{border-color:#f97316 !important;box-shadow:0 0 0 1px #f97316 !important}.filter-bar .p-button,.filter-bar-left .p-button,.flex.align-items-end .p-button,.flex.items-end .p-button{height:2.5rem;align-self:flex-end;margin-bottom:0}.spinner-lg{font-size:2rem}.spinner-md{font-size:1.5rem}.card{background:var(--p-surface-0);border-radius:.5rem;padding:1rem;border:1px solid var(--p-surface-200)}.info-card{background:var(--p-surface-50);padding:.75rem;border-radius:.5rem}.card-title{display:flex;align-items:center;justify-content:space-between}.card h3,.card-heading{margin:0 0 .75rem;font-size:.9375rem;font-weight:600;color:var(--p-surface-700)}.warning-card{background:var(--p-yellow-50);padding:.75rem;border-radius:.5rem;border:1px solid var(--p-yellow-200)}.warning-card i{color:var(--p-yellow-600)}.warning-card span{margin-left:.375rem;color:var(--p-yellow-700)}.span-full{grid-column:1/-1}.grid{display:grid}.grid-cols-2{grid-template-columns:repeat(2, 1fr)}.grid-cols-3{grid-template-columns:repeat(3, 1fr)}.text-uppercase{text-transform:uppercase}.text-center{text-align:center}.monospace{font-family:ui-monospace,"Cascadia Code","Source Code Pro",Menlo,Consolas,monospace}.break-all{word-break:break-all}.w-full{width:100% !important}.mb-0{margin-bottom:0}.mb-1{margin-bottom:.375rem}.mb-2{margin-bottom:.75rem}.mb-3{margin-bottom:1rem}.mt-0{margin-top:0}.mt-1{margin-top:.375rem}.mt-2{margin-top:.75rem}.p-2{padding:.75rem}.p-3{padding:1rem}.pt-2{padding-top:.75rem}.gap-1{gap:.375rem}.gap-2{gap:.75rem}.gap-3{gap:1rem}.flex{display:flex}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.flex-1{flex:1}.alert-warning{background:var(--p-yellow-50);padding:.75rem;margin-bottom:.75rem;border-radius:.5rem;border:1px solid var(--p-yellow-200)}.alert-warning-icon{color:var(--p-yellow-600)}.alert-warning-text{margin-left:.375rem;color:var(--p-yellow-700)}.alert-info{color:var(--p-orange-600);margin-bottom:.75rem}.key-info-panel{background:var(--p-surface-50);padding:.75rem;margin-bottom:.75rem;border-radius:.5rem}.key-info-grid{display:flex;flex-wrap:wrap;gap:1.5rem;align-items:center}.key-info-item small{color:var(--p-surface-500);display:block}.info-banner{margin-top:.75rem;margin-bottom:.75rem}.info-banner:first-child{margin-top:0}.info-banner .p-message-content{padding:.375rem .75rem}.info-banner .p-message-icon{display:none}.info-banner-content{display:flex;align-items:center;gap:.625rem}.info-banner-icon{font-size:1.125rem;flex-shrink:0}.info-banner-text{flex:1}.info-banner-text strong{font-weight:600}.p-button{background:rgba(0,0,0,0) !important;border:1px solid var(--p-primary-500) !important;color:var(--p-primary-500) !important;transition:background-color .2s,border-color .2s,color .2s}.p-button:hover:not(:disabled){background:var(--p-primary-50) !important;border-color:var(--p-primary-600) !important;color:var(--p-primary-600) !important}.p-button:focus{box-shadow:0 0 0 2px var(--p-surface-0),0 0 0 4px var(--p-primary-200)}.p-button:disabled{opacity:.6;cursor:not-allowed}.p-button-secondary{border-color:var(--p-surface-400) !important;color:var(--p-surface-600) !important}.p-button-secondary:hover:not(:disabled){background:var(--p-surface-100) !important;border-color:var(--p-surface-500) !important;color:var(--p-surface-700) !important}.p-button-secondary:focus{box-shadow:0 0 0 2px var(--p-surface-0),0 0 0 4px var(--p-surface-200)}.p-button-success{border-color:var(--p-green-500) !important;color:var(--p-green-600) !important}.p-button-success:hover:not(:disabled){background:var(--p-green-50) !important;border-color:var(--p-green-600) !important;color:var(--p-green-700) !important}.p-button-success:focus{box-shadow:0 0 0 2px var(--p-surface-0),0 0 0 4px var(--p-green-200)}.p-button-warn{border-color:var(--p-orange-500) !important;color:var(--p-orange-600) !important}.p-button-warn:hover:not(:disabled){background:var(--p-orange-50) !important;border-color:var(--p-orange-600) !important;color:var(--p-orange-700) !important}.p-button-warn:focus{box-shadow:0 0 0 2px var(--p-surface-0),0 0 0 4px var(--p-orange-200)}.p-button-danger{border-color:var(--p-red-500) !important;color:var(--p-red-600) !important}.p-button-danger:hover:not(:disabled){background:var(--p-red-50) !important;border-color:var(--p-red-600) !important;color:var(--p-red-700) !important}.p-button-danger:focus{box-shadow:0 0 0 2px var(--p-surface-0),0 0 0 4px var(--p-red-200)}.p-button-info{border-color:var(--p-blue-500) !important;color:var(--p-blue-600) !important}.p-button-info:hover:not(:disabled){background:var(--p-blue-50) !important;border-color:var(--p-blue-600) !important;color:var(--p-blue-700) !important}.p-button-info:focus{box-shadow:0 0 0 2px var(--p-surface-0),0 0 0 4px var(--p-blue-200)}.p-button-contrast{border-color:var(--p-surface-900) !important;color:var(--p-surface-900) !important}.p-button-contrast:hover:not(:disabled){background:var(--p-surface-900) !important;border-color:var(--p-surface-900) !important;color:var(--p-surface-0) !important}.p-button-filled,.p-button.p-button-raised{background:var(--p-primary-500) !important;border-color:var(--p-primary-500) !important;color:#fff !important}.p-button-filled:hover:not(:disabled),.p-button.p-button-raised:hover:not(:disabled){background:var(--p-primary-600) !important;border-color:var(--p-primary-600) !important;color:#fff !important}.p-button-filled.p-button-secondary,.p-button.p-button-raised.p-button-secondary{background:var(--p-surface-500) !important;border-color:var(--p-surface-500) !important;color:#fff !important}.p-button-filled.p-button-secondary:hover:not(:disabled),.p-button.p-button-raised.p-button-secondary:hover:not(:disabled){background:var(--p-surface-600) !important;border-color:var(--p-surface-600) !important}.p-button-filled.p-button-success,.p-button.p-button-raised.p-button-success{background:var(--p-green-500) !important;border-color:var(--p-green-500) !important;color:#fff !important}.p-button-filled.p-button-success:hover:not(:disabled),.p-button.p-button-raised.p-button-success:hover:not(:disabled){background:var(--p-green-600) !important;border-color:var(--p-green-600) !important}.p-button-filled.p-button-warn,.p-button.p-button-raised.p-button-warn{background:var(--p-orange-500) !important;border-color:var(--p-orange-500) !important;color:#fff !important}.p-button-filled.p-button-warn:hover:not(:disabled),.p-button.p-button-raised.p-button-warn:hover:not(:disabled){background:var(--p-orange-600) !important;border-color:var(--p-orange-600) !important}.p-button-filled.p-button-danger,.p-button.p-button-raised.p-button-danger{background:var(--p-red-500) !important;border-color:var(--p-red-500) !important;color:#fff !important}.p-button-filled.p-button-danger:hover:not(:disabled),.p-button.p-button-raised.p-button-danger:hover:not(:disabled){background:var(--p-red-600) !important;border-color:var(--p-red-600) !important}.p-button-text{background:rgba(0,0,0,0) !important;border-color:rgba(0,0,0,0) !important}.p-button-text:hover:not(:disabled){background:var(--p-primary-50) !important;border-color:rgba(0,0,0,0) !important}.p-button-text.p-button-secondary:hover:not(:disabled){background:var(--p-surface-100) !important}.p-button-text.p-button-success:hover:not(:disabled){background:var(--p-green-50) !important}.p-button-text.p-button-warn:hover:not(:disabled){background:var(--p-orange-50) !important}.p-button-text.p-button-danger:hover:not(:disabled){background:var(--p-red-50) !important}.p-button-link{background:rgba(0,0,0,0) !important;border-color:rgba(0,0,0,0) !important;color:var(--p-primary-500) !important;text-decoration:none}.p-button-link:hover:not(:disabled){background:rgba(0,0,0,0) !important;border-color:rgba(0,0,0,0) !important;text-decoration:underline}.p-button-icon-only{background:unset !important;border:unset !important;color:unset !important}.p-button-icon-only:hover:not(:disabled){background:unset !important;border-color:unset !important;color:unset !important}.p-button-sm{font-size:.875rem;padding:.375rem .75rem}.p-button-lg{font-size:1rem;padding:.75rem 1.25rem}.p-tag{background:rgba(0,0,0,0) !important;border:1px solid var(--p-primary-500) !important;color:var(--p-primary-500) !important;font-weight:normal !important;white-space:nowrap}.p-tag-secondary{background:rgba(0,0,0,0) !important;border-color:var(--p-surface-400) !important;color:var(--p-surface-600) !important}.p-tag-success{background:rgba(0,0,0,0) !important;border-color:var(--p-green-500) !important;color:var(--p-green-600) !important}.p-tag-warn{background:rgba(0,0,0,0) !important;border-color:var(--p-orange-500) !important;color:var(--p-orange-600) !important}.p-tag-danger{background:rgba(0,0,0,0) !important;border-color:var(--p-red-500) !important;color:var(--p-red-600) !important}.p-tag-info{background:rgba(0,0,0,0) !important;border-color:var(--p-blue-500) !important;color:var(--p-blue-600) !important}.p-tag-contrast{background:rgba(0,0,0,0) !important;border-color:var(--p-surface-900) !important;color:var(--p-surface-900) !important}.p-splitbutton .p-splitbutton-defaultbutton{border:1px solid var(--p-primary-500) !important;border-right:none !important;border-top-right-radius:0 !important;border-bottom-right-radius:0 !important;background:rgba(0,0,0,0) !important;color:var(--p-primary-500) !important}.p-splitbutton .p-splitbutton-defaultbutton:hover:not(:disabled){background:var(--p-primary-50) !important}.p-splitbutton .p-splitbutton-dropdown{border:1px solid var(--p-primary-500) !important;border-left:none !important;border-top-left-radius:0 !important;border-bottom-left-radius:0 !important;background:rgba(0,0,0,0) !important;color:var(--p-primary-500) !important}.p-splitbutton .p-splitbutton-dropdown:hover:not(:disabled){background:var(--p-primary-50) !important}.p-splitbutton.p-splitbutton-danger .p-splitbutton-defaultbutton,.p-splitbutton.p-splitbutton-danger .p-splitbutton-dropdown{border-color:var(--p-red-500) !important;color:var(--p-red-600) !important}.p-splitbutton.p-splitbutton-danger .p-splitbutton-defaultbutton:hover:not(:disabled),.p-splitbutton.p-splitbutton-danger .p-splitbutton-dropdown:hover:not(:disabled){background:var(--p-red-50) !important}.p-splitbutton.p-splitbutton-secondary .p-splitbutton-defaultbutton,.p-splitbutton.p-splitbutton-secondary .p-splitbutton-dropdown{border-color:var(--p-surface-400) !important;color:var(--p-surface-600) !important}.p-splitbutton.p-splitbutton-secondary .p-splitbutton-defaultbutton:hover:not(:disabled),.p-splitbutton.p-splitbutton-secondary .p-splitbutton-dropdown:hover:not(:disabled){background:var(--p-surface-100) !important}.p-splitbutton.p-splitbutton-success .p-splitbutton-defaultbutton,.p-splitbutton.p-splitbutton-success .p-splitbutton-dropdown{border-color:var(--p-green-500) !important;color:var(--p-green-600) !important}.p-splitbutton.p-splitbutton-success .p-splitbutton-defaultbutton:hover:not(:disabled),.p-splitbutton.p-splitbutton-success .p-splitbutton-dropdown:hover:not(:disabled){background:var(--p-green-50) !important}.cards-grid{display:grid;gap:1rem;margin-bottom:1.5rem}.cards-grid--cols-auto{grid-template-columns:repeat(auto-fit, minmax(180px, 1fr))}.cards-grid--cols-2{grid-template-columns:repeat(2, 1fr)}.cards-grid--cols-3{grid-template-columns:repeat(3, 1fr)}.cards-grid--cols-4{grid-template-columns:repeat(4, 1fr)}.stat-card,.card-custom{background:var(--p-surface-0);border:1px solid var(--p-surface-200);border-radius:.5rem;padding:1.25rem}.stat-card{text-align:center}.stat-card--clickable{cursor:pointer;transition:border-color .2s,box-shadow .2s}.stat-card--clickable:hover{border-color:var(--p-primary-300);box-shadow:0 2px 8px rgba(0,0,0,.08)}.stat-value{font-size:2rem;font-weight:600;color:var(--p-primary-500);display:flex;align-items:center;justify-content:center;gap:.5rem}.stat-value--success{color:var(--p-green-500)}.stat-value--danger{color:var(--p-red-500)}.stat-value--warning{color:var(--p-orange-500)}.stat-value--info{color:var(--p-blue-500)}.stat-icon{font-size:1.5rem}.stat-label{font-size:.875rem;color:var(--p-surface-600);margin-top:.25rem}@media(max-width: 768px){.cards-grid--cols-3,.cards-grid--cols-4{grid-template-columns:repeat(2, 1fr)}}@media(max-width: 480px){.cards-grid{grid-template-columns:1fr}}.rich-cards-grid{display:grid;gap:1rem;margin-bottom:1.5rem}.rich-cards-grid--cols-auto{grid-template-columns:repeat(auto-fit, minmax(200px, 1fr))}.rich-cards-grid--cols-2{grid-template-columns:repeat(2, 1fr)}.rich-cards-grid--cols-3{grid-template-columns:repeat(3, 1fr)}.rich-cards-grid--cols-4{grid-template-columns:repeat(4, 1fr)}.rich-cards-grid--cols-5{grid-template-columns:repeat(5, 1fr)}.rich-cards-grid--cols-6{grid-template-columns:repeat(6, 1fr)}.rich-card{background:var(--p-surface-0);border:1px solid var(--p-surface-200);border-radius:.5rem;padding:1.25rem}.rich-card--clickable{cursor:pointer;transition:border-color .2s,box-shadow .2s,transform .2s}.rich-card--clickable:hover{border-color:var(--p-primary-300);box-shadow:0 4px 12px rgba(0,0,0,.1);transform:translateY(-2px)}.rich-card--success .rich-card-value{color:var(--p-green-600)}.rich-card--success .rich-card-icon{color:var(--p-green-500)}.rich-card--danger .rich-card-value{color:var(--p-red-600)}.rich-card--danger .rich-card-icon{color:var(--p-red-500)}.rich-card--warning .rich-card-value{color:var(--p-orange-600)}.rich-card--warning .rich-card-icon{color:var(--p-orange-500)}.rich-card--info .rich-card-value{color:var(--p-blue-600)}.rich-card--info .rich-card-icon{color:var(--p-blue-500)}.rich-card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:.75rem}.rich-card-title{font-size:.875rem;font-weight:500;color:var(--p-surface-600)}.rich-card-icon{font-size:1.25rem;color:var(--p-surface-400)}.rich-card-value{font-size:2rem;font-weight:700;color:var(--p-surface-900);line-height:1.2}.rich-card-subtitle{font-size:.8rem;color:var(--p-surface-500);margin-top:.25rem}@media(max-width: 1024px){.rich-cards-grid--cols-5,.rich-cards-grid--cols-6{grid-template-columns:repeat(3, 1fr)}}@media(max-width: 768px){.rich-cards-grid--cols-3,.rich-cards-grid--cols-4,.rich-cards-grid--cols-5,.rich-cards-grid--cols-6{grid-template-columns:repeat(2, 1fr)}}@media(max-width: 480px){.rich-cards-grid{grid-template-columns:1fr}}.code-box{word-break:break-all;font-family:ui-monospace,"Cascadia Code","Source Code Pro",Menlo,Consolas,monospace;background:var(--p-surface-100);padding:.75rem;border-radius:.25rem}.code-box--sm{padding:.375rem;font-size:.875rem}.tag-sm{font-size:.7rem}.tag-xs{font-size:.75rem}.tags-constrained{max-width:300px}.json-viewer{background:var(--p-surface-50);border:1px solid var(--p-surface-200);border-radius:.375rem;overflow:auto;font-family:"Monaco","Menlo","Ubuntu Mono",monospace;font-size:.8125rem;line-height:1.5}.json-content{padding:1rem;margin:0;white-space:pre-wrap;word-break:break-word}.json-key{color:#881391}.json-string{color:#1a1aa6}.json-number{color:#1c00cf}.json-boolean{color:#0d22aa;font-weight:500}.json-null{color:gray;font-style:italic}.dialog-content{display:flex;align-items:flex-start;gap:1rem;margin-bottom:.75rem}.dialog-content p{margin:0;line-height:1.5}.dialog-section-title{font-size:.875rem;font-weight:600;margin-bottom:.375rem;color:var(--p-surface-600)}.dialog-icon{font-size:2rem;color:var(--p-orange-500);flex-shrink:0}.multi-step-dialog{padding:.5rem 0}.step-content{padding:1rem 0}.step-actions{display:flex;justify-content:flex-end;gap:.5rem;margin-top:1.5rem;padding-top:1rem;border-top:1px solid var(--p-surface-200)}.markdown-rendered{background:var(--p-surface-100);padding:.75rem;border-radius:.25rem;max-height:400px;overflow-y:auto;font-size:.875rem}.markdown-rendered h1,.markdown-rendered h2,.markdown-rendered h3,.markdown-rendered h4{margin:.75rem 0 .375rem 0;font-weight:600}.markdown-rendered h1{font-size:1.25rem}.markdown-rendered h2{font-size:1.125rem}.markdown-rendered h3{font-size:1rem}.markdown-rendered p{margin:.375rem 0}.markdown-rendered ul,.markdown-rendered ol{margin:.375rem 0;padding-left:1rem}.markdown-rendered a{color:var(--p-primary-500);text-decoration:underline}.markdown-rendered code{background:var(--p-surface-200);padding:.1rem .3rem;border-radius:.25rem;font-family:ui-monospace,"Cascadia Code","Source Code Pro",Menlo,Consolas,monospace;font-size:.85em}.markdown-rendered pre{background:var(--p-surface-200);padding:.75rem;border-radius:.25rem;overflow-x:auto}.markdown-rendered blockquote{border-left:3px solid var(--p-surface-300);margin:.375rem 0;padding-left:.75rem;color:var(--p-surface-600)}.markdown-rendered .md-figure{margin:.75rem 0;padding:0}.markdown-rendered .md-figure img{max-width:100%;height:auto;border-radius:.25rem;display:block}.markdown-rendered .md-figure figcaption{margin-top:.375rem;font-size:.75rem;color:var(--p-surface-500);font-style:italic;line-height:1.5}.header-left{display:flex;align-items:center;gap:.75rem}.content-grid{display:grid;gap:1rem}.meta-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));gap:1rem}@media(max-width: 767px){.meta-grid{grid-template-columns:1fr}}.summary-text,.intent-text{line-height:1.5}.tags-wrap{display:flex;gap:.375rem;flex-wrap:wrap}.url-actions{display:flex;gap:.375rem;align-items:center}.content-box{background:var(--p-surface-100);padding:.75rem;border-radius:.25rem;white-space:pre-wrap;word-break:break-word}.content-box--scrollable{max-height:300px;overflow-y:auto}.content-box--tall{max-height:400px}.status-row{display:flex;justify-content:space-between;align-items:flex-start}.tags-row{display:flex;gap:.375rem}.show-field{display:flex;flex-direction:column;gap:.25rem}.show-field--horizontal{flex-direction:row;align-items:flex-start;gap:1rem}.show-field__label{font-weight:600;color:var(--p-text-muted-color, #6c757d);font-size:.875rem}.show-field--horizontal .show-field__label{padding-top:.125rem}.show-field__value{color:var(--p-text-color, #1e293b);line-height:1.5}.show-display{display:inline}.show-display--badge{display:inline-flex;align-items:center}.show-display--empty{color:var(--p-text-muted-color, #6c757d)}.show-display--email,.show-display--url,.show-display--reference{color:var(--p-primary-color, #3b82f6);text-decoration:none}.show-display--email:hover,.show-display--url:hover,.show-display--reference:hover{text-decoration:underline}.show-display--password{font-family:monospace;letter-spacing:.1em}.show-display--number,.show-display--currency{font-variant-numeric:tabular-nums}.show-display--boolean{display:inline-flex;align-items:center;gap:.5rem}.show-display--boolean.text-success{color:var(--p-green-500, #22c55e)}.show-display--boolean.text-danger{color:var(--p-red-500, #ef4444)}.show-display--boolean.text-muted{color:var(--p-text-muted-color, #6c757d)}.show-display--textarea{white-space:pre-wrap;line-height:1.6}.show-display--image img{border-radius:4px;object-fit:cover}.show-display--json{background:var(--p-surface-100, #f1f5f9);padding:.75rem;border-radius:4px;font-size:.875rem;overflow-x:auto;margin:0}.sidebar-box{position:relative;z-index:1;padding:.5rem .75rem;border-top:1px solid var(--p-surface-700, #334155);height:3.5rem;box-sizing:border-box;cursor:pointer;transition:background var(--fad-transition-fast);display:flex;align-items:center}.sidebar-box:hover{background:var(--p-surface-700, #334155)}.sidebar-box:focus-within{z-index:20}.sidebar-box-inner{display:flex;align-items:center;gap:.5rem;padding-left:2rem;height:100%;width:100%}.sidebar-box-icon{position:absolute;left:.5rem;top:50%;transform:translateY(-50%);width:28px;height:28px;display:flex;align-items:center;justify-content:center;font-size:.875rem;color:var(--p-surface-400, #94a3b8)}.sidebar-box:hover .sidebar-box-icon{color:var(--p-surface-200, #e2e8f0)}.sidebar-box-content{flex:1;min-width:0;opacity:1;transition:opacity .1s ease .2s}.sidebar-box-title{font-weight:var(--fad-font-weight-normal);font-size:var(--fad-font-size-sm);color:var(--p-surface-100, #f1f5f9);white-space:nowrap}.sidebar-box-subtitle{font-size:var(--fad-font-size-2xs);color:var(--p-surface-400, #94a3b8);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sidebar-box--impersonator .sidebar-box-icon{color:var(--p-orange-400, #fb923c)}.sidebar-box--impersonator:hover .sidebar-box-icon{color:var(--p-orange-300, #fdba74)}.sidebar-box--impersonator .sidebar-box-title{color:var(--p-orange-400, #fb923c)}.sidebar-box--impersonator .sidebar-box-subtitle{color:var(--p-orange-300, #fdba74)}.sidebar--collapsed .sidebar-box{padding:0}.sidebar--collapsed .sidebar-box-inner{justify-content:center;padding-left:0}.sidebar--collapsed .sidebar-box-content{opacity:0;visibility:hidden;transition:opacity 0s,visibility 0s}.sidebar-box-controls{padding:.5rem 1rem}.sidebar-box-controls-label{font-size:var(--fad-font-size-xs);font-weight:var(--fad-font-weight-medium);color:var(--p-surface-500, #64748b);text-transform:uppercase;letter-spacing:.06em;display:block;margin-bottom:.375rem}.sidebar-box-controls-input{width:100%}.sidebar-box-controls-input :deep(.p-autocomplete-input){width:100%;font-size:var(--fad-font-size-sm)}.sidebar-box-controls-input :deep(.p-autocomplete-overlay){z-index:9999 !important}.sidebar--collapsed .sidebar-box-controls{display:none !important}.sidebar-box--full{height:auto;cursor:default}.sidebar-box--full:hover{background:rgba(0,0,0,0)}.sidebar-box-full{width:100%;padding:.25rem 0;opacity:1;transition:opacity .1s ease .2s}.sidebar-box-full-label{font-size:var(--fad-font-size-xs);font-weight:var(--fad-font-weight-medium);color:var(--p-surface-500, #64748b);text-transform:uppercase;letter-spacing:.06em;display:block;margin-bottom:.375rem}.sidebar-box-full-input{width:100%}.sidebar-box-full-input :deep(.p-autocomplete-input){width:100%;font-size:var(--fad-font-size-sm)}.sidebar-box-full-input :deep(.p-autocomplete-overlay){z-index:9999 !important}.sidebar--collapsed .sidebar-box--full{opacity:0;visibility:hidden;height:0;padding:0;border:none;overflow:hidden;transition:opacity 0s,visibility 0s,height 0s,padding 0s}.sidebar--collapsed .sidebar-box-full{opacity:0;transition:opacity 0s}.sidebar-nav .nav-item{position:relative;display:flex;align-items:center;padding:var(--fad-nav-item-padding-y) var(--fad-nav-item-padding-x);padding-left:calc((var(--fad-sidebar-width-collapsed) + 1.125rem)/2 + .375rem);color:var(--p-surface-300, #cbd5e1);text-decoration:none;font-size:var(--fad-font-size-sm);font-weight:var(--fad-font-weight-normal);border-radius:0;transition:background var(--fad-transition-fast),color var(--fad-transition-fast),padding var(--fad-transition-slow)}.sidebar-nav .nav-item i{position:absolute;left:calc((var(--fad-sidebar-width-collapsed) - 1.125rem)/2);top:50%;transform:translateY(-50%);font-size:var(--fad-nav-icon-size);width:1.125rem;text-align:center;opacity:.8}.sidebar-nav .nav-item span{white-space:nowrap;opacity:1;transition:opacity .1s ease .2s}.sidebar-nav .nav-item:hover{background:var(--p-surface-700, #334155);color:var(--p-surface-0, white)}.sidebar-nav .nav-item-active{background:var(--p-surface-700, #334155);color:var(--p-primary-300, #93c5fd);border-left:2px solid var(--p-primary-400, #60a5fa);padding-left:calc(calc((var(--fad-sidebar-width-collapsed) + 1.125rem) / 2 + 0.375rem) - 2px)}.sidebar-nav .nav-item-active:hover{background:var(--p-surface-600, #475569)}.sidebar-nav .nav-item-active i{opacity:1}.sidebar--collapsed .sidebar-nav .nav-item{padding:.5rem 0;padding-left:calc((var(--fad-sidebar-width-collapsed) + 1.125rem)/2 + .375rem);border-left:none !important}.sidebar--collapsed .sidebar-nav .nav-item-active{border-left:none !important;padding-left:calc((var(--fad-sidebar-width-collapsed) + 1.125rem)/2 + .375rem)}.sidebar--collapsed .sidebar-nav .nav-item span{opacity:0;visibility:hidden;transition:opacity 0s,visibility 0s}@media(max-width: 767px){.sidebar.sidebar--collapsed .sidebar-nav .nav-item{justify-content:flex-start;padding:var(--fad-nav-item-padding-y) var(--fad-nav-item-padding-x);padding-left:calc((var(--fad-sidebar-width-collapsed) + 1.125rem)/2 + .375rem)}.sidebar.sidebar--collapsed .sidebar-nav .nav-item span{display:inline;opacity:1;visibility:visible;width:auto;transition:opacity .1s ease .2s}.sidebar.sidebar--collapsed .sidebar-nav .nav-item i{position:absolute;left:calc((var(--fad-sidebar-width-collapsed) - 1.125rem)/2);font-size:var(--fad-nav-icon-size);width:1.125rem}}:root{--qdadm-sidebar-width: 15rem;--qdadm-sidebar-collapsed-width: 2.5rem;--qdadm-sidebar-transition: 200ms ease;--qdadm-content-padding: 1rem;--qdadm-content-padding-mobile: 0.75rem}@media(max-width: 1023px){.app-layout{--qdadm-sidebar-width: 12rem}}@media(max-width: 767px){.app-layout{--qdadm-sidebar-width: 100vw}}.sidebar{transition:transform var(--qdadm-sidebar-transition),width var(--qdadm-sidebar-transition)}@media(max-width: 767px){.sidebar{transform:translateX(-100%);position:fixed;z-index:1000;box-shadow:none;width:100vw !important;height:100dvh;max-height:100dvh;overflow-y:auto}.sidebar.sidebar--open{transform:translateX(0);box-shadow:none}}.main-content{transition:margin-left var(--qdadm-sidebar-transition)}@media(max-width: 767px){.main-content{margin-left:0 !important}}.sidebar-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:99;opacity:0;transition:opacity .25s}@media(max-width: 767px){.sidebar-overlay{display:block;pointer-events:none}.sidebar-overlay.sidebar-overlay--visible{opacity:1;pointer-events:auto}}.hamburger-btn{display:none !important}@media(max-width: 767px){.hamburger-btn{display:flex !important}}@media(max-width: 767px){.page-content{padding:var(--qdadm-content-padding-mobile)}}@media(max-width: 767px){.page-header{flex-direction:column;gap:1rem;align-items:flex-start !important}}@media(max-width: 767px){.page-header .page-header-actions{width:100%;justify-content:flex-start}}.p-datatable .p-datatable-table-container{overflow-x:auto;-webkit-overflow-scrolling:touch}@media(max-width: 767px){.p-datatable .p-datatable-table{min-width:600px}}@media(max-width: 479px){.p-datatable .p-datatable-table{min-width:500px}}@media(max-width: 767px){.filter-bar{flex-wrap:wrap;gap:.75rem}}@media(max-width: 767px){.filter-bar-left{flex-wrap:wrap;gap:.5rem;width:100%}}@media(max-width: 767px){.card{border-radius:0;border:none;background:rgba(0,0,0,0);padding:0}}@media(max-width: 767px){.form-grid{grid-template-columns:1fr !important}}@media(max-width: 479px){.form-actions{flex-direction:column;gap:.5rem}.form-actions .form-actions-left{width:100%;flex-direction:column}.form-actions .p-button{width:100%;white-space:nowrap}}@media(max-width: 767px){.cards-grid{grid-template-columns:repeat(2, 1fr)}}@media(max-width: 479px){.cards-grid{grid-template-columns:1fr}}@media(max-width: 767px){.hide-mobile{display:none !important}}.hide-desktop{display:none !important}@media(max-width: 767px){.hide-desktop{display:block !important}}@media(max-width: 767px){.hide-mobile-inline{display:none !important}}.show-mobile-inline{display:none !important}@media(max-width: 767px){.show-mobile-inline{display:inline !important}}
|
|
1
|
+
:root{--fad-text-primary: var(--p-text-color);--fad-text-secondary: var(--p-text-muted-color);--fad-text-disabled: var(--p-surface-400);--fad-text-inverse: var(--p-surface-0);--fad-surface-ground: var(--p-surface-ground);--fad-surface-card: var(--p-surface-card);--fad-surface-overlay: var(--p-surface-overlay);--fad-surface-border: var(--p-surface-200);--fad-surface-hover: var(--p-surface-100);--fad-primary: var(--p-primary-color);--fad-primary-hover: var(--p-primary-600);--fad-primary-text: var(--p-primary-contrast-color);--fad-success: var(--p-green-500);--fad-warning: var(--p-orange-500);--fad-danger: var(--p-red-500);--fad-info: var(--p-blue-500);--fad-space-2xs: 0.125rem;--fad-space-xs: 0.25rem;--fad-space-sm: 0.375rem;--fad-space-md: 0.75rem;--fad-space-lg: 1rem;--fad-space-xl: 1.5rem;--fad-space-2xl: 2rem;--fad-font-family: Inter, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif;--fad-font-size-2xs: 0.6875rem;--fad-font-size-xs: 0.75rem;--fad-font-size-sm: 0.875rem;--fad-font-size-md: 0.9375rem;--fad-font-size-lg: 1rem;--fad-font-size-xl: 1.125rem;--fad-font-size-2xl: 1.25rem;--fad-font-weight-light: 300;--fad-font-weight-normal: 400;--fad-font-weight-medium: 500;--fad-font-weight-semibold: 600;--fad-font-weight-bold: 700;--fad-line-height-tight: 1.25;--fad-line-height-normal: 1.5;--fad-line-height-relaxed: 1.75;--fad-border-width: 1px;--fad-border-color: var(--fad-surface-border);--fad-radius-sm: 0.25rem;--fad-radius-md: 0.375rem;--fad-radius-lg: 0.5rem;--fad-radius-xl: 0.75rem;--fad-radius-full: 9999px;--fad-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);--fad-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);--fad-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);--fad-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1);--fad-transition-instant: 50ms ease;--fad-transition-fast: 100ms ease;--fad-transition-normal: 150ms ease;--fad-transition-slow: 200ms ease;--fad-z-dropdown: 1000;--fad-z-sticky: 1020;--fad-z-fixed: 1030;--fad-z-modal-backdrop: 1040;--fad-z-modal: 1050;--fad-z-popover: 1060;--fad-z-tooltip: 1070;--fad-sidebar-width: 15rem;--fad-sidebar-width-tablet: 13rem;--fad-sidebar-width-collapsed: 2.5rem;--fad-header-height: 2.75rem;--fad-content-max-width: 87.5rem;--fad-nav-item-height: 2rem;--fad-nav-item-padding-x: 0.75rem;--fad-nav-item-padding-y: 0.5rem;--fad-nav-icon-size: 1rem;--fad-nav-section-gap: 0.5rem}.dark-mode{--fad-surface-border: var(--p-surface-700);--fad-surface-hover: var(--p-surface-800);--fad-text-disabled: var(--p-surface-500)}.d-none{display:none !important}.d-block{display:block !important}.d-flex{display:flex !important}.d-inline-flex{display:inline-flex !important}.d-grid{display:grid !important}.flex-row{flex-direction:row}.flex-column{flex-direction:column}.flex-wrap{flex-wrap:wrap}.flex-1{flex:1}.flex-auto{flex:auto}.flex-none{flex:none}.items-start{align-items:flex-start}.items-center{align-items:center}.items-end{align-items:flex-end}.items-stretch{align-items:stretch}.justify-start{justify-content:flex-start}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.gap-xs{gap:.25rem}.gap-sm{gap:.375rem}.gap-md{gap:.75rem}.gap-lg{gap:1rem}.gap-xl{gap:1.5rem}.w-full{width:100% !important}.w-auto{width:auto !important}.w-50{width:50%}.max-w-full{max-width:100%}.m-0{margin:0}.m-auto{margin:auto}.mt-0{margin-top:0}.mt-xs{margin-top:.25rem}.mt-sm{margin-top:.375rem}.mt-md{margin-top:.75rem}.mt-lg{margin-top:1rem}.mt-xl{margin-top:1.5rem}.mb-0{margin-bottom:0}.mb-xs{margin-bottom:.25rem}.mb-sm{margin-bottom:.375rem}.mb-md{margin-bottom:.75rem}.mb-lg{margin-bottom:1rem}.mb-xl{margin-bottom:1.5rem}.ml-auto{margin-left:auto}.mr-auto{margin-right:auto}.mx-auto{margin-left:auto;margin-right:auto}.p-0{padding:0}.p-xs{padding:.25rem}.p-sm{padding:.375rem}.p-md{padding:.75rem}.p-lg{padding:1rem}.p-xl{padding:1.5rem}.px-md{padding-left:.75rem;padding-right:.75rem}.py-md{padding-top:.75rem;padding-bottom:.75rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-xs{font-size:.75rem}.text-sm{font-size:.875rem}.text-md{font-size:.9375rem}.text-lg{font-size:1rem}.text-xl{font-size:1.125rem}.text-2xl{font-size:1.25rem}.font-normal{font-weight:400}.font-medium{font-weight:500}.font-semibold{font-weight:600}.font-bold{font-weight:700}.text-primary{color:var(--fad-text-primary)}.text-secondary{color:var(--fad-text-secondary)}.text-muted{color:var(--fad-text-secondary)}.text-success{color:var(--fad-success)}.text-warning{color:var(--fad-warning)}.text-danger{color:var(--fad-danger)}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.visible{visibility:visible}.invisible{visibility:hidden}.opacity-0{opacity:0}.opacity-50{opacity:.5}.opacity-100{opacity:1}.border{border:1px solid var(--fad-border-color)}.border-0{border:none}.border-top{border-top:1px solid var(--fad-border-color)}.border-bottom{border-bottom:1px solid var(--fad-border-color)}.rounded-sm{border-radius:.25rem}.rounded-md{border-radius:.375rem}.rounded-lg{border-radius:.5rem}.rounded-full{border-radius:9999px}.cursor-pointer{cursor:pointer}.cursor-default{cursor:default}.cursor-not-allowed{cursor:not-allowed}.overflow-hidden{overflow:hidden}.overflow-auto{overflow:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-auto{overflow-x:auto}.relative{position:relative}.absolute{position:absolute}.fixed{position:fixed}.sticky{position:sticky}.fad-card{background:var(--fad-surface-card);border:1px solid var(--fad-border-color);border-radius:.5rem;padding:1rem}.fad-card-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:.75rem;padding-bottom:.75rem;border-bottom:1px solid var(--fad-border-color)}.fad-card-title{font-size:1rem;font-weight:600;color:var(--fad-text-primary);margin:0}.fad-card-body{color:var(--fad-text-primary)}.fad-card-footer{display:flex;align-items:center;justify-content:flex-end;gap:.375rem;margin-top:.75rem;padding-top:.75rem;border-top:1px solid var(--fad-border-color)}.fad-form-field{display:flex;flex-direction:column;gap:.375rem;margin-bottom:1rem}.fad-form-label{font-weight:500;color:var(--fad-text-primary);font-size:.875rem}.fad-form-label-required::after{content:" *";color:var(--fad-danger)}.fad-form-help{font-size:.75rem;color:var(--fad-text-secondary)}.fad-form-error{font-size:.75rem;color:var(--fad-danger)}.fad-form-row{display:grid;grid-template-columns:repeat(auto-fit, minmax(250px, 1fr));gap:.75rem}.fad-form-section-title{font-size:1rem;font-weight:600;color:var(--fad-text-primary);margin:1.5rem 0 .75rem 0;padding-bottom:.375rem;border-bottom:1px solid var(--fad-border-color)}.fad-form-section-title:first-child{margin-top:0}.fad-form-field--dirty{position:relative}.fad-form-field--dirty::before{content:"";position:absolute;left:-8px;top:0;bottom:0;width:3px;background:var(--fad-warning);border-radius:9999px}.fad-form-field--error .fad-form-label{color:var(--fad-danger)}.fad-page-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:1rem;padding-bottom:.75rem}.fad-page-title{font-size:1.25rem;font-weight:600;color:var(--fad-text-primary);margin:0}.fad-page-actions{display:flex;gap:.375rem}.fad-empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem .75rem;text-align:center;color:var(--fad-text-secondary)}.fad-empty-state-icon{font-size:3rem;opacity:.5;margin-bottom:.75rem}.fad-empty-state-title{font-size:1rem;font-weight:500;color:var(--fad-text-primary);margin:0 0 .375rem 0}.fad-empty-state-description{font-size:.875rem;max-width:400px}.fad-loading{display:flex;align-items:center;justify-content:center;padding:2rem}.fad-loading-spinner{animation:fad-spin 1s linear infinite}@keyframes fad-spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}.fad-badge{display:inline-flex;align-items:center;padding:.25rem .375rem;font-size:.75rem;font-weight:500;border-radius:9999px;text-transform:uppercase;letter-spacing:.025em}.fad-badge--success{background:var(--p-green-100);color:var(--p-green-700)}.fad-badge--warning{background:var(--p-orange-100);color:var(--p-orange-700)}.fad-badge--danger{background:var(--p-red-100);color:var(--p-red-700)}.fad-badge--info{background:var(--p-blue-100);color:var(--p-blue-700)}.fad-badge--neutral{background:var(--p-surface-100);color:var(--p-surface-600)}.fad-tabs{display:flex;gap:.25rem;border-bottom:1px solid var(--fad-border-color);margin-bottom:1rem}.fad-tab{display:flex;align-items:center;gap:.25rem;padding:.375rem .75rem;font-size:.875rem;font-weight:500;color:var(--fad-text-secondary);background:rgba(0,0,0,0);border:none;border-bottom:2px solid rgba(0,0,0,0);cursor:pointer;transition:all 100ms ease;margin-bottom:-1px}.fad-tab:hover{color:var(--fad-text-primary)}.fad-tab--active{color:var(--fad-primary);border-bottom-color:var(--fad-primary)}.fad-tab-count{background:var(--p-surface-100);color:var(--fad-text-secondary);padding:.125rem .375rem;border-radius:9999px;font-size:.75rem}.fad-tab--active .fad-tab-count{background:var(--p-primary-100);color:var(--fad-primary)}.fad-page-nav{display:flex;justify-content:space-between;align-items:center;margin-bottom:.375rem}.fad-page-nav-breadcrumb{flex:1}.fad-page-nav-links{display:flex;align-items:center;gap:.375rem;font-size:.875rem}.fad-page-nav-separator{color:var(--fad-text-secondary)}.fad-page-nav-link{color:var(--fad-primary);text-decoration:none;transition:color 100ms ease}.fad-page-nav-link:hover{color:var(--fad-primary-hover);text-decoration:underline}.fad-page-nav-link--active{color:var(--fad-text-primary);font-weight:500;pointer-events:none}*{margin:0;padding:0;box-sizing:border-box}html,body,#app{height:100%;font-family:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif}input::placeholder,textarea::placeholder,.p-inputtext::placeholder,.p-autocomplete-input::placeholder,.p-textarea::placeholder,.p-select-label.p-placeholder,.p-dropdown-label.p-placeholder{color:#a0aec0 !important;opacity:1}.page-content{flex:1;padding:1rem}.page-header{margin-bottom:1rem}.page-title{font-size:1.25rem;font-weight:600;color:var(--p-surface-900)}.header-actions{display:flex;gap:.75rem;align-items:center}.p-tabpanels{padding-top:.75rem}.p-tabpanel>.form-field:first-child,.p-tabpanel>.form-row:first-child,.p-tabpanel>.form-grid:first-child{margin-top:.375rem}.info-block{background:var(--p-surface-50);padding:1rem;border-radius:.5rem}.info-block-title{margin:0 0 .75rem 0;font-size:.9375rem;color:var(--p-surface-600)}.info-block-title i{margin-right:.375rem}.info-label{font-weight:500;color:var(--p-surface-500);display:block;margin-bottom:.25rem}.info-label-sm{font-size:.75rem;font-weight:600;color:var(--p-surface-500);text-transform:uppercase;display:block;margin-bottom:.25rem}.value-lg{font-size:1.25rem;font-weight:600}.accent-block{background:var(--p-surface-50);border-left:3px solid var(--p-primary-500);padding:.75rem;border-radius:.25rem}.code-block{word-break:break-all;font-family:ui-monospace,"Cascadia Code","Source Code Pro",Menlo,Consolas,monospace;background:var(--p-surface-100);padding:.75rem;border-radius:.25rem}.section-header{display:flex;align-items:center;gap:.375rem;margin-bottom:.75rem}.section-header h3{margin:0;font-size:.9375rem;font-weight:600}.section-header i{color:var(--p-primary-500)}.section-description{color:var(--p-surface-500);font-size:.875rem;margin-bottom:.75rem}.section-title{margin:0 0 .75rem 0;font-size:.9375rem;font-weight:600;color:var(--p-surface-700)}.section-title--separator{border-top:1px solid var(--p-surface-200);padding-top:1rem}.section-title i{margin-right:.375rem}.image-gallery{display:grid;grid-template-columns:repeat(auto-fill, minmax(200px, 1fr));gap:.75rem}.image-card{position:relative;border-radius:.5rem;overflow:hidden;border:1px solid var(--p-surface-200)}.image-card img{width:100%;aspect-ratio:16/9;object-fit:cover}.image-card--clickable{cursor:pointer;transition:border-color 150ms ease,box-shadow 150ms ease}.image-card--clickable:hover{border-color:var(--p-primary-300);box-shadow:0 2px 8px rgba(0,0,0,.1)}.image-caption{padding:.375rem;background:var(--p-surface-50);font-size:.75rem;color:var(--p-surface-600)}.info-row{display:flex;justify-content:space-between;align-items:center;padding:.375rem 0;border-bottom:1px solid var(--p-surface-100)}.info-row:last-child{border-bottom:none}.layout-breadcrumb{padding:.75rem 1rem;background:var(--p-surface-0, white);border-bottom:1px solid var(--p-surface-200, #e2e8f0)}.layout-breadcrumb .p-breadcrumb-list{margin:0;padding:0}.breadcrumb-link{display:flex;align-items:center;gap:.375rem;color:var(--p-primary-600, #2563eb);text-decoration:none;font-size:.875rem}.breadcrumb-link:hover{text-decoration:underline}.breadcrumb-link i{font-size:.875rem}.breadcrumb-current{display:flex;align-items:center;gap:.375rem;color:var(--p-surface-600, #475569);font-size:.875rem}.breadcrumb-current i{font-size:.875rem}.breadcrumb{margin-bottom:.5rem}.breadcrumb-list{display:flex;align-items:center;gap:.5rem;list-style:none;margin:0;padding:0;font-size:.875rem}.breadcrumb-item{display:flex;align-items:center;gap:.5rem}.breadcrumb-item.is-current .breadcrumb-text{color:var(--p-text-color, #333);font-weight:500}.breadcrumb-separator{font-size:.75rem;color:var(--p-text-muted-color, #888)}.breadcrumb-item .breadcrumb-link{display:flex;align-items:center;gap:.375rem;color:var(--p-primary-color, #3b82f6);text-decoration:none;transition:color .2s}.breadcrumb-item .breadcrumb-link:hover{color:var(--p-primary-hover-color, #2563eb);text-decoration:underline}.breadcrumb-item .breadcrumb-link i{font-size:.875rem}.breadcrumb-text{display:flex;align-items:center;gap:.375rem;color:var(--p-text-muted-color, #888)}.form-grid{display:grid;grid-template-columns:repeat(2, 1fr);gap:1rem;padding:.75rem 0}@media(max-width: 767px){.form-grid{grid-template-columns:1fr}}.form-row{display:grid;grid-template-columns:repeat(2, 1fr);gap:1rem;align-items:end}.form-row-3{display:grid;grid-template-columns:repeat(3, 1fr);gap:.75rem;align-items:end}.form-row.metadata{display:grid;grid-template-columns:repeat(4, 1fr);gap:.75rem;padding:.75rem;background:var(--p-surface-50);border-radius:.5rem;align-items:end}.form-row.checkboxes{display:flex;gap:3rem}.form-row+.form-field,.form-field+.form-row{margin-top:1.25rem}.form-field{display:flex;flex-direction:column;gap:.375rem}.form-field label{font-weight:500;color:var(--p-surface-700)}.form-field small{color:var(--p-surface-500);font-size:.85rem}.form-field .p-inputtext,.form-field .p-textarea,.form-field .p-select,.form-field .p-multiselect,.form-field .p-inputwrapper,.form-field input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]),.form-field textarea{width:100% !important;box-sizing:border-box}.form-field>input[type=checkbox],.form-field>input[type=radio]{align-self:flex-start}.form-field .p-textarea,.form-field textarea{min-height:100px;resize:vertical}.meta-item{display:flex;flex-direction:column;gap:.25rem}.meta-item label{font-size:.85rem;color:var(--p-surface-500)}.meta-item span{font-weight:500}.toggle-field{flex-direction:row;align-items:center;gap:.75rem}.toggle-field label{min-width:120px}.checkbox-field{display:flex;align-items:center;gap:.375rem}.checkbox-field label{margin:0;cursor:pointer}.url-field,.lock-field,.envelope-field{display:flex;gap:.375rem;align-items:center}.url-field input,.lock-field input,.envelope-field input{flex:1}.field-hint,.form-hint{font-size:.8rem;color:var(--p-surface-400);margin-top:.25rem;display:block}.field-error{font-size:.8rem;color:var(--p-red-500);margin-top:.25rem;display:block}.editor-box{border:1px solid var(--p-surface-200);border-radius:.5rem;padding:.75rem;background:var(--p-surface-50)}.editor-chips{display:flex;flex-wrap:wrap;gap:.375rem;margin-bottom:.75rem;padding-bottom:.75rem;border-bottom:1px solid var(--p-surface-200)}.chip-code{font-family:ui-monospace,"Cascadia Code","Source Code Pro",Menlo,Consolas,monospace;font-size:.875rem}.chip-namespace{color:var(--p-surface-500)}.chip-action{color:var(--p-primary-600);font-weight:500}.chip-wildcard{color:var(--p-orange-500);font-weight:700}.info-grid{display:grid;grid-template-columns:repeat(2, 1fr);gap:.75rem}.info-grid>div{display:flex;flex-direction:column;gap:.25rem}.info-grid label{font-size:.85rem;color:var(--p-surface-500)}.info-item{display:flex;flex-direction:column;gap:.25rem}.info-item label{font-size:.75rem;font-weight:600;color:var(--p-surface-500);text-transform:uppercase}.info-value{font-size:.9rem}.timestamps-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));gap:.75rem}.timestamps-grid--separator{padding-top:.75rem;border-top:1px solid var(--p-surface-200)}.timestamp{display:flex;flex-direction:column;gap:.25rem}.timestamp label{font-size:.75rem;color:var(--text-color-secondary);text-transform:uppercase}.form-dirty-indicator{display:flex;align-items:center;gap:.375rem;padding:.375rem .75rem;background:color-mix(in srgb, var(--p-orange-100) 50%, transparent);border:1px solid var(--p-orange-300);border-radius:.375rem;color:var(--p-orange-700);font-size:.875rem;margin-bottom:.75rem}.form-dirty-indicator i{color:var(--p-orange-500)}.form-field.field-dirty .p-inputtext,.form-field.field-dirty .p-select,.form-field.field-dirty .p-textarea,.form-field.field-dirty .p-password input,.form-field.field-dirty .p-multiselect,.form-field.field-dirty .p-calendar input,.form-field.field-dirty .p-inputnumber input,.form-field.field-dirty .p-dropdown{border-color:#f97316 !important;box-shadow:0 0 0 1px #f97316 !important}.form-card{background:var(--p-surface-0);padding:1.5rem;border-radius:.5rem}.config-intro{display:flex;align-items:flex-start;gap:.75rem;padding:.75rem;background:var(--p-surface-50);border-radius:.5rem;margin-bottom:1rem}.config-intro i{font-size:1.25rem;color:var(--p-primary-500);margin-top:.125rem}.config-field{margin-bottom:1rem}.form-actions{display:flex;justify-content:space-between;align-items:center;margin-top:1.5rem;padding-top:1.5rem;border-top:1px solid var(--p-surface-200)}.form-actions-left{display:flex;align-items:center;gap:.5rem}.dirty-indicator{color:var(--p-orange-500);font-size:.5rem;animation:pulse 2s infinite}@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}.field-invalid input,.field-invalid textarea,.field-invalid .p-inputtext,.field-invalid .p-select,.field-invalid .p-dropdown{border-color:var(--p-red-500)}.field-invalid input:focus,.field-invalid textarea:focus,.field-invalid .p-inputtext:focus,.field-invalid .p-select:focus,.field-invalid .p-dropdown:focus{box-shadow:0 0 0 1px var(--p-red-500)}.form-page{display:flex;flex-direction:column;gap:1rem}.form-error-message{margin:1rem 0}.validation-summary{margin-bottom:1rem}.validation-errors{margin:0;padding-left:1.5rem}.validation-errors li{margin:.25rem 0}.stats-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));gap:1rem;margin-bottom:1.5rem}.stat-card{background:var(--p-surface-0);border-radius:.5rem;padding:1rem;border:1px solid var(--p-surface-200)}.stat-card-title{font-size:.875rem;color:var(--p-surface-500);margin-bottom:.375rem}.stat-card-value{font-size:2rem;font-weight:600;color:var(--p-surface-900)}.stat-card-subtitle{font-size:.75rem;color:var(--p-surface-400);margin-top:.25rem}.loading-state{display:flex;justify-content:center;align-items:center;padding:2rem}.loading-state--inline{padding:.75rem;gap:.75rem;color:var(--p-text-muted-color)}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:2rem;color:var(--text-color-secondary)}.empty-state--sm{padding:1rem}.empty-state--lg{padding:3rem}.empty-state-icon{font-size:2rem;margin-bottom:.5rem;opacity:.5}.empty-state--sm .empty-state-icon{font-size:1.5rem}.empty-state--lg .empty-state-icon{font-size:3rem;margin-bottom:1rem}.empty-state-title{margin:0 0 .25rem 0;font-weight:500;color:var(--text-color-secondary)}.empty-state--lg .empty-state-title{font-size:1.1rem}.empty-state-description{opacity:.7;max-width:300px}.empty-state-actions{margin-top:1rem}.error-message{background:var(--p-red-50);border:1px solid var(--p-red-200);border-radius:.375rem;padding:.75rem .75rem;color:var(--p-red-700);font-family:ui-monospace,"Cascadia Code","Source Code Pro",Menlo,Consolas,monospace;font-size:.85rem;white-space:pre-wrap;word-break:break-word}.error-content{background:var(--p-red-50);color:var(--p-red-700);padding:.75rem;border-radius:.5rem;overflow-x:auto;margin:0;font-size:.85rem;white-space:pre-wrap}:root[data-theme=dark] .error-message,:root[data-theme=dark] .error-content{background:rgba(239,68,68,.1);color:var(--p-red-400);border-color:var(--p-red-900)}.p-datatable .p-datatable-tbody>tr>td{padding:.5rem .75rem !important}.p-datatable .p-datatable-thead>tr>th{padding:.625rem .75rem !important}.table-actions{display:flex;gap:.25rem;justify-content:flex-start}.text-muted{color:var(--p-surface-400)}.status-tags,.type-tags{display:flex;gap:.25rem;flex-wrap:wrap}.external-link{display:flex;align-items:center;gap:.375rem;color:var(--p-primary-500);text-decoration:none}.external-link:hover{text-decoration:underline}.external-link i{font-size:.75rem;opacity:.6}.domain-cell{display:flex;align-items:center}.domain-link{display:flex;align-items:center;gap:.375rem;color:var(--p-primary-500);text-decoration:none}.domain-link:hover{text-decoration:underline}.domain-link i{font-size:.75rem;opacity:.6}.domain-tags{display:flex;gap:.25rem}.clickable-cell{cursor:pointer;color:var(--p-primary-color)}.clickable-cell:hover{text-decoration:underline}.entity-link{display:flex;align-items:center;gap:.375rem;text-decoration:none;color:var(--p-primary-color)}.entity-link:hover{text-decoration:underline}.entity-type{font-weight:500}.entity-id{font-size:.75rem;background:var(--p-surface-100);padding:.15rem .35rem;border-radius:3px}.tags-preview{display:flex;gap:.25rem;flex-wrap:wrap}.tags-preview .p-tag{font-size:.75rem}.p-role-label,.p-permission-label{display:inline-flex;align-items:center;background:rgba(0,0,0,0);font-size:.75rem;font-weight:normal !important;padding:.25rem .75rem;border-radius:var(--p-tag-border-radius, 6px);white-space:nowrap}.p-role-label--pill,.p-permission-label--pill{border-radius:999px}.p-role-label--more,.p-permission-label--more{border-style:dashed}.p-role-label{border:1px solid var(--p-blue-500);color:var(--p-blue-600)}.p-permission-label{border:1px solid var(--p-surface-400);color:var(--p-surface-600)}.p-role-labels,.p-permission-labels{display:flex;flex-wrap:wrap;gap:.25rem}.p-chip{background:none !important;border:1px solid var(--p-surface-300) !important;color:var(--p-surface-600) !important;font-size:.75rem !important;padding:.2rem .6rem !important}.datatable-row-clickable .p-datatable-tbody>tr{cursor:pointer}.datatable-row-clickable .p-datatable-tbody>tr:active{background:var(--p-primary-50) !important}.filter-bar{display:flex;justify-content:space-between;align-items:center;gap:.75rem;margin-bottom:.75rem}.filter-bar-left{display:flex;align-items:center;gap:.75rem}.filter-bar-right{display:flex;align-items:center;gap:.75rem}.p-select.filter-default,.p-autocomplete.filter-default .p-inputtext{border-color:var(--p-primary-400, #60a5fa) !important;box-shadow:0 0 0 1px var(--p-primary-400, #60a5fa) !important}.p-select.filter-modified,.p-autocomplete.filter-modified .p-inputtext{border-color:#f97316 !important;box-shadow:0 0 0 1px #f97316 !important}.p-select.filter-active,.p-inputtext.filter-active{border-color:#f97316 !important;box-shadow:0 0 0 1px #f97316 !important}.filter-bar .p-button,.filter-bar-left .p-button,.flex.align-items-end .p-button,.flex.items-end .p-button{height:2.5rem;align-self:flex-end;margin-bottom:0}.spinner-lg{font-size:2rem}.spinner-md{font-size:1.5rem}.card{background:var(--p-surface-0);border-radius:.5rem;padding:1rem;border:1px solid var(--p-surface-200)}.info-card{background:var(--p-surface-50);padding:.75rem;border-radius:.5rem}.card-title{display:flex;align-items:center;justify-content:space-between}.card h3,.card-heading{margin:0 0 .75rem;font-size:.9375rem;font-weight:600;color:var(--p-surface-700)}.warning-card{background:var(--p-yellow-50);padding:.75rem;border-radius:.5rem;border:1px solid var(--p-yellow-200)}.warning-card i{color:var(--p-yellow-600)}.warning-card span{margin-left:.375rem;color:var(--p-yellow-700)}.span-full{grid-column:1/-1}.grid{display:grid}.grid-cols-2{grid-template-columns:repeat(2, 1fr)}.grid-cols-3{grid-template-columns:repeat(3, 1fr)}.text-uppercase{text-transform:uppercase}.text-center{text-align:center}.monospace{font-family:ui-monospace,"Cascadia Code","Source Code Pro",Menlo,Consolas,monospace}.break-all{word-break:break-all}.w-full{width:100% !important}.mb-0{margin-bottom:0}.mb-1{margin-bottom:.375rem}.mb-2{margin-bottom:.75rem}.mb-3{margin-bottom:1rem}.mt-0{margin-top:0}.mt-1{margin-top:.375rem}.mt-2{margin-top:.75rem}.p-2{padding:.75rem}.p-3{padding:1rem}.pt-2{padding-top:.75rem}.gap-1{gap:.375rem}.gap-2{gap:.75rem}.gap-3{gap:1rem}.flex{display:flex}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.flex-1{flex:1}.alert-warning{background:var(--p-yellow-50);padding:.75rem;margin-bottom:.75rem;border-radius:.5rem;border:1px solid var(--p-yellow-200)}.alert-warning-icon{color:var(--p-yellow-600)}.alert-warning-text{margin-left:.375rem;color:var(--p-yellow-700)}.alert-info{color:var(--p-orange-600);margin-bottom:.75rem}.key-info-panel{background:var(--p-surface-50);padding:.75rem;margin-bottom:.75rem;border-radius:.5rem}.key-info-grid{display:flex;flex-wrap:wrap;gap:1.5rem;align-items:center}.key-info-item small{color:var(--p-surface-500);display:block}.info-banner{margin-top:.75rem;margin-bottom:.75rem}.info-banner:first-child{margin-top:0}.info-banner .p-message-content{padding:.375rem .75rem}.info-banner .p-message-icon{display:none}.info-banner-content{display:flex;align-items:center;gap:.625rem}.info-banner-icon{font-size:1.125rem;flex-shrink:0}.info-banner-text{flex:1}.info-banner-text strong{font-weight:600}.p-button{background:rgba(0,0,0,0) !important;border:1px solid var(--p-primary-500) !important;color:var(--p-primary-500) !important;transition:background-color .2s,border-color .2s,color .2s}.p-button:hover:not(:disabled){background:var(--p-primary-50) !important;border-color:var(--p-primary-600) !important;color:var(--p-primary-600) !important}.p-button:focus{box-shadow:0 0 0 2px var(--p-surface-0),0 0 0 4px var(--p-primary-200)}.p-button:disabled{opacity:.6;cursor:not-allowed}.p-button-secondary{border-color:var(--p-surface-400) !important;color:var(--p-surface-600) !important}.p-button-secondary:hover:not(:disabled){background:var(--p-surface-100) !important;border-color:var(--p-surface-500) !important;color:var(--p-surface-700) !important}.p-button-secondary:focus{box-shadow:0 0 0 2px var(--p-surface-0),0 0 0 4px var(--p-surface-200)}.p-button-success{border-color:var(--p-green-500) !important;color:var(--p-green-600) !important}.p-button-success:hover:not(:disabled){background:var(--p-green-50) !important;border-color:var(--p-green-600) !important;color:var(--p-green-700) !important}.p-button-success:focus{box-shadow:0 0 0 2px var(--p-surface-0),0 0 0 4px var(--p-green-200)}.p-button-warn{border-color:var(--p-orange-500) !important;color:var(--p-orange-600) !important}.p-button-warn:hover:not(:disabled){background:var(--p-orange-50) !important;border-color:var(--p-orange-600) !important;color:var(--p-orange-700) !important}.p-button-warn:focus{box-shadow:0 0 0 2px var(--p-surface-0),0 0 0 4px var(--p-orange-200)}.p-button-danger{border-color:var(--p-red-500) !important;color:var(--p-red-600) !important}.p-button-danger:hover:not(:disabled){background:var(--p-red-50) !important;border-color:var(--p-red-600) !important;color:var(--p-red-700) !important}.p-button-danger:focus{box-shadow:0 0 0 2px var(--p-surface-0),0 0 0 4px var(--p-red-200)}.p-button-info{border-color:var(--p-blue-500) !important;color:var(--p-blue-600) !important}.p-button-info:hover:not(:disabled){background:var(--p-blue-50) !important;border-color:var(--p-blue-600) !important;color:var(--p-blue-700) !important}.p-button-info:focus{box-shadow:0 0 0 2px var(--p-surface-0),0 0 0 4px var(--p-blue-200)}.p-button-contrast{border-color:var(--p-surface-900) !important;color:var(--p-surface-900) !important}.p-button-contrast:hover:not(:disabled){background:var(--p-surface-900) !important;border-color:var(--p-surface-900) !important;color:var(--p-surface-0) !important}.p-button-filled,.p-button.p-button-raised{background:var(--p-primary-500) !important;border-color:var(--p-primary-500) !important;color:#fff !important}.p-button-filled:hover:not(:disabled),.p-button.p-button-raised:hover:not(:disabled){background:var(--p-primary-600) !important;border-color:var(--p-primary-600) !important;color:#fff !important}.p-button-filled.p-button-secondary,.p-button.p-button-raised.p-button-secondary{background:var(--p-surface-500) !important;border-color:var(--p-surface-500) !important;color:#fff !important}.p-button-filled.p-button-secondary:hover:not(:disabled),.p-button.p-button-raised.p-button-secondary:hover:not(:disabled){background:var(--p-surface-600) !important;border-color:var(--p-surface-600) !important}.p-button-filled.p-button-success,.p-button.p-button-raised.p-button-success{background:var(--p-green-500) !important;border-color:var(--p-green-500) !important;color:#fff !important}.p-button-filled.p-button-success:hover:not(:disabled),.p-button.p-button-raised.p-button-success:hover:not(:disabled){background:var(--p-green-600) !important;border-color:var(--p-green-600) !important}.p-button-filled.p-button-warn,.p-button.p-button-raised.p-button-warn{background:var(--p-orange-500) !important;border-color:var(--p-orange-500) !important;color:#fff !important}.p-button-filled.p-button-warn:hover:not(:disabled),.p-button.p-button-raised.p-button-warn:hover:not(:disabled){background:var(--p-orange-600) !important;border-color:var(--p-orange-600) !important}.p-button-filled.p-button-danger,.p-button.p-button-raised.p-button-danger{background:var(--p-red-500) !important;border-color:var(--p-red-500) !important;color:#fff !important}.p-button-filled.p-button-danger:hover:not(:disabled),.p-button.p-button-raised.p-button-danger:hover:not(:disabled){background:var(--p-red-600) !important;border-color:var(--p-red-600) !important}.p-button-text{background:rgba(0,0,0,0) !important;border-color:rgba(0,0,0,0) !important}.p-button-text:hover:not(:disabled){background:var(--p-primary-50) !important;border-color:rgba(0,0,0,0) !important}.p-button-text.p-button-secondary:hover:not(:disabled){background:var(--p-surface-100) !important}.p-button-text.p-button-success:hover:not(:disabled){background:var(--p-green-50) !important}.p-button-text.p-button-warn:hover:not(:disabled){background:var(--p-orange-50) !important}.p-button-text.p-button-danger:hover:not(:disabled){background:var(--p-red-50) !important}.p-button-link{background:rgba(0,0,0,0) !important;border-color:rgba(0,0,0,0) !important;color:var(--p-primary-500) !important;text-decoration:none}.p-button-link:hover:not(:disabled){background:rgba(0,0,0,0) !important;border-color:rgba(0,0,0,0) !important;text-decoration:underline}.p-button-icon-only{background:unset !important;border:unset !important;color:unset !important}.p-button-icon-only:hover:not(:disabled){background:unset !important;border-color:unset !important;color:unset !important}.p-button-sm{font-size:.875rem;padding:.375rem .75rem}.p-button-lg{font-size:1rem;padding:.75rem 1.25rem}.p-tag{background:rgba(0,0,0,0) !important;border:1px solid var(--p-primary-500) !important;color:var(--p-primary-500) !important;font-weight:normal !important;white-space:nowrap}.p-tag-secondary{background:rgba(0,0,0,0) !important;border-color:var(--p-surface-400) !important;color:var(--p-surface-600) !important}.p-tag-success{background:rgba(0,0,0,0) !important;border-color:var(--p-green-500) !important;color:var(--p-green-600) !important}.p-tag-warn{background:rgba(0,0,0,0) !important;border-color:var(--p-orange-500) !important;color:var(--p-orange-600) !important}.p-tag-danger{background:rgba(0,0,0,0) !important;border-color:var(--p-red-500) !important;color:var(--p-red-600) !important}.p-tag-info{background:rgba(0,0,0,0) !important;border-color:var(--p-blue-500) !important;color:var(--p-blue-600) !important}.p-tag-contrast{background:rgba(0,0,0,0) !important;border-color:var(--p-surface-900) !important;color:var(--p-surface-900) !important}.p-splitbutton .p-splitbutton-defaultbutton{border:1px solid var(--p-primary-500) !important;border-right:none !important;border-top-right-radius:0 !important;border-bottom-right-radius:0 !important;background:rgba(0,0,0,0) !important;color:var(--p-primary-500) !important}.p-splitbutton .p-splitbutton-defaultbutton:hover:not(:disabled){background:var(--p-primary-50) !important}.p-splitbutton .p-splitbutton-dropdown{border:1px solid var(--p-primary-500) !important;border-left:none !important;border-top-left-radius:0 !important;border-bottom-left-radius:0 !important;background:rgba(0,0,0,0) !important;color:var(--p-primary-500) !important}.p-splitbutton .p-splitbutton-dropdown:hover:not(:disabled){background:var(--p-primary-50) !important}.p-splitbutton.p-splitbutton-danger .p-splitbutton-defaultbutton,.p-splitbutton.p-splitbutton-danger .p-splitbutton-dropdown{border-color:var(--p-red-500) !important;color:var(--p-red-600) !important}.p-splitbutton.p-splitbutton-danger .p-splitbutton-defaultbutton:hover:not(:disabled),.p-splitbutton.p-splitbutton-danger .p-splitbutton-dropdown:hover:not(:disabled){background:var(--p-red-50) !important}.p-splitbutton.p-splitbutton-secondary .p-splitbutton-defaultbutton,.p-splitbutton.p-splitbutton-secondary .p-splitbutton-dropdown{border-color:var(--p-surface-400) !important;color:var(--p-surface-600) !important}.p-splitbutton.p-splitbutton-secondary .p-splitbutton-defaultbutton:hover:not(:disabled),.p-splitbutton.p-splitbutton-secondary .p-splitbutton-dropdown:hover:not(:disabled){background:var(--p-surface-100) !important}.p-splitbutton.p-splitbutton-success .p-splitbutton-defaultbutton,.p-splitbutton.p-splitbutton-success .p-splitbutton-dropdown{border-color:var(--p-green-500) !important;color:var(--p-green-600) !important}.p-splitbutton.p-splitbutton-success .p-splitbutton-defaultbutton:hover:not(:disabled),.p-splitbutton.p-splitbutton-success .p-splitbutton-dropdown:hover:not(:disabled){background:var(--p-green-50) !important}.cards-grid{display:grid;gap:1rem;margin-bottom:1.5rem}.cards-grid--cols-auto{grid-template-columns:repeat(auto-fit, minmax(180px, 1fr))}.cards-grid--cols-2{grid-template-columns:repeat(2, 1fr)}.cards-grid--cols-3{grid-template-columns:repeat(3, 1fr)}.cards-grid--cols-4{grid-template-columns:repeat(4, 1fr)}.stat-card,.card-custom{background:var(--p-surface-0);border:1px solid var(--p-surface-200);border-radius:.5rem;padding:1.25rem}.stat-card{text-align:center}.stat-card--clickable{cursor:pointer;transition:border-color .2s,box-shadow .2s}.stat-card--clickable:hover{border-color:var(--p-primary-300);box-shadow:0 2px 8px rgba(0,0,0,.08)}.stat-value{font-size:2rem;font-weight:600;color:var(--p-primary-500);display:flex;align-items:center;justify-content:center;gap:.5rem}.stat-value--success{color:var(--p-green-500)}.stat-value--danger{color:var(--p-red-500)}.stat-value--warning{color:var(--p-orange-500)}.stat-value--info{color:var(--p-blue-500)}.stat-icon{font-size:1.5rem}.stat-label{font-size:.875rem;color:var(--p-surface-600);margin-top:.25rem}@media(max-width: 768px){.cards-grid--cols-3,.cards-grid--cols-4{grid-template-columns:repeat(2, 1fr)}}@media(max-width: 480px){.cards-grid{grid-template-columns:1fr}}.rich-cards-grid{display:grid;gap:1rem;margin-bottom:1.5rem}.rich-cards-grid--cols-auto{grid-template-columns:repeat(auto-fit, minmax(200px, 1fr))}.rich-cards-grid--cols-2{grid-template-columns:repeat(2, 1fr)}.rich-cards-grid--cols-3{grid-template-columns:repeat(3, 1fr)}.rich-cards-grid--cols-4{grid-template-columns:repeat(4, 1fr)}.rich-cards-grid--cols-5{grid-template-columns:repeat(5, 1fr)}.rich-cards-grid--cols-6{grid-template-columns:repeat(6, 1fr)}.rich-card{background:var(--p-surface-0);border:1px solid var(--p-surface-200);border-radius:.5rem;padding:1.25rem}.rich-card--clickable{cursor:pointer;transition:border-color .2s,box-shadow .2s,transform .2s}.rich-card--clickable:hover{border-color:var(--p-primary-300);box-shadow:0 4px 12px rgba(0,0,0,.1);transform:translateY(-2px)}.rich-card--success .rich-card-value{color:var(--p-green-600)}.rich-card--success .rich-card-icon{color:var(--p-green-500)}.rich-card--danger .rich-card-value{color:var(--p-red-600)}.rich-card--danger .rich-card-icon{color:var(--p-red-500)}.rich-card--warning .rich-card-value{color:var(--p-orange-600)}.rich-card--warning .rich-card-icon{color:var(--p-orange-500)}.rich-card--info .rich-card-value{color:var(--p-blue-600)}.rich-card--info .rich-card-icon{color:var(--p-blue-500)}.rich-card-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:.75rem}.rich-card-title{font-size:.875rem;font-weight:500;color:var(--p-surface-600)}.rich-card-icon{font-size:1.25rem;color:var(--p-surface-400)}.rich-card-value{font-size:2rem;font-weight:700;color:var(--p-surface-900);line-height:1.2}.rich-card-subtitle{font-size:.8rem;color:var(--p-surface-500);margin-top:.25rem}@media(max-width: 1024px){.rich-cards-grid--cols-5,.rich-cards-grid--cols-6{grid-template-columns:repeat(3, 1fr)}}@media(max-width: 768px){.rich-cards-grid--cols-3,.rich-cards-grid--cols-4,.rich-cards-grid--cols-5,.rich-cards-grid--cols-6{grid-template-columns:repeat(2, 1fr)}}@media(max-width: 480px){.rich-cards-grid{grid-template-columns:1fr}}.code-box{word-break:break-all;font-family:ui-monospace,"Cascadia Code","Source Code Pro",Menlo,Consolas,monospace;background:var(--p-surface-100);padding:.75rem;border-radius:.25rem}.code-box--sm{padding:.375rem;font-size:.875rem}.tag-sm{font-size:.7rem}.tag-xs{font-size:.75rem}.tags-constrained{max-width:300px}.json-viewer{background:var(--p-surface-50);border:1px solid var(--p-surface-200);border-radius:.375rem;overflow:auto;font-family:"Monaco","Menlo","Ubuntu Mono",monospace;font-size:.8125rem;line-height:1.5}.json-content{padding:1rem;margin:0;white-space:pre-wrap;word-break:break-word}.json-key{color:#881391}.json-string{color:#1a1aa6}.json-number{color:#1c00cf}.json-boolean{color:#0d22aa;font-weight:500}.json-null{color:gray;font-style:italic}.dialog-content{display:flex;align-items:flex-start;gap:1rem;margin-bottom:.75rem}.dialog-content p{margin:0;line-height:1.5}.dialog-section-title{font-size:.875rem;font-weight:600;margin-bottom:.375rem;color:var(--p-surface-600)}.dialog-icon{font-size:2rem;color:var(--p-orange-500);flex-shrink:0}.multi-step-dialog{padding:.5rem 0}.step-content{padding:1rem 0}.step-actions{display:flex;justify-content:flex-end;gap:.5rem;margin-top:1.5rem;padding-top:1rem;border-top:1px solid var(--p-surface-200)}.markdown-rendered{background:var(--p-surface-100);padding:.75rem;border-radius:.25rem;max-height:400px;overflow-y:auto;font-size:.875rem}.markdown-rendered h1,.markdown-rendered h2,.markdown-rendered h3,.markdown-rendered h4{margin:.75rem 0 .375rem 0;font-weight:600}.markdown-rendered h1{font-size:1.25rem}.markdown-rendered h2{font-size:1.125rem}.markdown-rendered h3{font-size:1rem}.markdown-rendered p{margin:.375rem 0}.markdown-rendered ul,.markdown-rendered ol{margin:.375rem 0;padding-left:1rem}.markdown-rendered a{color:var(--p-primary-500);text-decoration:underline}.markdown-rendered code{background:var(--p-surface-200);padding:.1rem .3rem;border-radius:.25rem;font-family:ui-monospace,"Cascadia Code","Source Code Pro",Menlo,Consolas,monospace;font-size:.85em}.markdown-rendered pre{background:var(--p-surface-200);padding:.75rem;border-radius:.25rem;overflow-x:auto}.markdown-rendered blockquote{border-left:3px solid var(--p-surface-300);margin:.375rem 0;padding-left:.75rem;color:var(--p-surface-600)}.markdown-rendered .md-figure{margin:.75rem 0;padding:0}.markdown-rendered .md-figure img{max-width:100%;height:auto;border-radius:.25rem;display:block}.markdown-rendered .md-figure figcaption{margin-top:.375rem;font-size:.75rem;color:var(--p-surface-500);font-style:italic;line-height:1.5}.header-left{display:flex;align-items:center;gap:.75rem}.content-grid{display:grid;gap:1rem}.meta-grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));gap:1rem}@media(max-width: 767px){.meta-grid{grid-template-columns:1fr}}.summary-text,.intent-text{line-height:1.5}.tags-wrap{display:flex;gap:.375rem;flex-wrap:wrap}.url-actions{display:flex;gap:.375rem;align-items:center}.content-box{background:var(--p-surface-100);padding:.75rem;border-radius:.25rem;white-space:pre-wrap;word-break:break-word}.content-box--scrollable{max-height:300px;overflow-y:auto}.content-box--tall{max-height:400px}.status-row{display:flex;justify-content:space-between;align-items:flex-start}.tags-row{display:flex;gap:.375rem}.show-field{display:flex;flex-direction:column;gap:.25rem}.show-field--horizontal{flex-direction:row;align-items:flex-start;gap:1rem}.show-field__label{font-weight:600;color:var(--p-text-muted-color, #6c757d);font-size:.875rem}.show-field--horizontal .show-field__label{padding-top:.125rem}.show-field__value{color:var(--p-text-color, #1e293b);line-height:1.5}.show-display{display:inline}.show-display--badge{display:inline-flex;align-items:center}.show-display--empty{color:var(--p-text-muted-color, #6c757d)}.show-display--email,.show-display--url,.show-display--reference{color:var(--p-primary-color, #3b82f6);text-decoration:none}.show-display--email:hover,.show-display--url:hover,.show-display--reference:hover{text-decoration:underline}.show-display--password{font-family:monospace;letter-spacing:.1em}.show-display--number,.show-display--currency{font-variant-numeric:tabular-nums}.show-display--boolean{display:inline-flex;align-items:center;gap:.5rem}.show-display--boolean.text-success{color:var(--p-green-500, #22c55e)}.show-display--boolean.text-danger{color:var(--p-red-500, #ef4444)}.show-display--boolean.text-muted{color:var(--p-text-muted-color, #6c757d)}.show-display--textarea{white-space:pre-wrap;line-height:1.6}.show-display--image img{border-radius:4px;object-fit:cover}.show-display--json{background:var(--p-surface-100, #f1f5f9);padding:.75rem;border-radius:4px;font-size:.875rem;overflow-x:auto;margin:0}.sidebar-box{position:relative;z-index:1;padding:.5rem .75rem;border-top:1px solid var(--p-surface-700, #334155);height:3.5rem;box-sizing:border-box;cursor:pointer;transition:background var(--fad-transition-fast);display:flex;align-items:center}.sidebar-box:hover{background:var(--p-surface-700, #334155)}.sidebar-box:focus-within{z-index:20}.sidebar-box-inner{display:flex;align-items:center;gap:.5rem;padding-left:2rem;height:100%;width:100%}.sidebar-box-icon{position:absolute;left:.5rem;top:50%;transform:translateY(-50%);width:28px;height:28px;display:flex;align-items:center;justify-content:center;font-size:.875rem;color:var(--p-surface-400, #94a3b8)}.sidebar-box:hover .sidebar-box-icon{color:var(--p-surface-200, #e2e8f0)}.sidebar-box-content{flex:1;min-width:0;opacity:1;transition:opacity .1s ease .2s}.sidebar-box-title{font-weight:var(--fad-font-weight-normal);font-size:var(--fad-font-size-sm);color:var(--p-surface-100, #f1f5f9);white-space:nowrap}.sidebar-box-subtitle{font-size:var(--fad-font-size-2xs);color:var(--p-surface-400, #94a3b8);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sidebar-box--impersonator .sidebar-box-icon{color:var(--p-orange-400, #fb923c)}.sidebar-box--impersonator:hover .sidebar-box-icon{color:var(--p-orange-300, #fdba74)}.sidebar-box--impersonator .sidebar-box-title{color:var(--p-orange-400, #fb923c)}.sidebar-box--impersonator .sidebar-box-subtitle{color:var(--p-orange-300, #fdba74)}.sidebar--collapsed .sidebar-box{padding:0}.sidebar--collapsed .sidebar-box-inner{justify-content:center;padding-left:0}.sidebar--collapsed .sidebar-box-content{opacity:0;visibility:hidden;transition:opacity 0s,visibility 0s}.sidebar-box-controls{padding:.5rem 1rem}.sidebar-box-controls-label{font-size:var(--fad-font-size-xs);font-weight:var(--fad-font-weight-medium);color:var(--p-surface-500, #64748b);text-transform:uppercase;letter-spacing:.06em;display:block;margin-bottom:.375rem}.sidebar-box-controls-input{width:100%}.sidebar-box-controls-input :deep(.p-autocomplete-input){width:100%;font-size:var(--fad-font-size-sm)}.sidebar-box-controls-input :deep(.p-autocomplete-overlay){z-index:9999 !important}.sidebar--collapsed .sidebar-box-controls{display:none !important}.sidebar-box--full{height:auto;cursor:default}.sidebar-box--full:hover{background:rgba(0,0,0,0)}.sidebar-box-full{width:100%;padding:.25rem 0;opacity:1;transition:opacity .1s ease .2s}.sidebar-box-full-label{font-size:var(--fad-font-size-xs);font-weight:var(--fad-font-weight-medium);color:var(--p-surface-500, #64748b);text-transform:uppercase;letter-spacing:.06em;display:block;margin-bottom:.375rem}.sidebar-box-full-input{width:100%}.sidebar-box-full-input :deep(.p-autocomplete-input){width:100%;font-size:var(--fad-font-size-sm)}.sidebar-box-full-input :deep(.p-autocomplete-overlay){z-index:9999 !important}.sidebar--collapsed .sidebar-box--full{opacity:0;visibility:hidden;height:0;padding:0;border:none;overflow:hidden;transition:opacity 0s,visibility 0s,height 0s,padding 0s}.sidebar--collapsed .sidebar-box-full{opacity:0;transition:opacity 0s}.sidebar-nav .nav-item{position:relative;display:flex;align-items:center;padding:var(--fad-nav-item-padding-y) var(--fad-nav-item-padding-x);padding-left:calc((var(--fad-sidebar-width-collapsed) + 1.125rem)/2 + .375rem);color:var(--p-surface-300, #cbd5e1);text-decoration:none;font-size:var(--fad-font-size-sm);font-weight:var(--fad-font-weight-normal);border-radius:0;transition:background var(--fad-transition-fast),color var(--fad-transition-fast),padding var(--fad-transition-slow)}.sidebar-nav .nav-item i{position:absolute;left:calc((var(--fad-sidebar-width-collapsed) - 1.125rem)/2);top:50%;transform:translateY(-50%);font-size:var(--fad-nav-icon-size);width:1.125rem;text-align:center;opacity:.8}.sidebar-nav .nav-item span{white-space:nowrap;opacity:1;transition:opacity .1s ease .2s}.sidebar-nav .nav-item .nav-item-external{margin-left:.375rem;font-size:.625rem;color:var(--p-surface-400, #94a3b8)}.sidebar-nav .nav-item:hover{background:var(--p-surface-700, #334155);color:var(--p-surface-0, white)}.sidebar-nav .nav-item-active{background:var(--p-surface-700, #334155);color:var(--p-primary-300, #93c5fd);border-left:2px solid var(--p-primary-400, #60a5fa);padding-left:calc(calc((var(--fad-sidebar-width-collapsed) + 1.125rem) / 2 + 0.375rem) - 2px)}.sidebar-nav .nav-item-active:hover{background:var(--p-surface-600, #475569)}.sidebar-nav .nav-item-active i{opacity:1}.sidebar--collapsed .sidebar-nav .nav-item{padding:.5rem 0;padding-left:calc((var(--fad-sidebar-width-collapsed) + 1.125rem)/2 + .375rem);border-left:none !important}.sidebar--collapsed .sidebar-nav .nav-item-active{border-left:none !important;padding-left:calc((var(--fad-sidebar-width-collapsed) + 1.125rem)/2 + .375rem)}.sidebar--collapsed .sidebar-nav .nav-item span{opacity:0;visibility:hidden;transition:opacity 0s,visibility 0s}@media(max-width: 767px){.sidebar.sidebar--collapsed .sidebar-nav .nav-item{justify-content:flex-start;padding:var(--fad-nav-item-padding-y) var(--fad-nav-item-padding-x);padding-left:calc((var(--fad-sidebar-width-collapsed) + 1.125rem)/2 + .375rem)}.sidebar.sidebar--collapsed .sidebar-nav .nav-item span{display:inline;opacity:1;visibility:visible;width:auto;transition:opacity .1s ease .2s}.sidebar.sidebar--collapsed .sidebar-nav .nav-item i{position:absolute;left:calc((var(--fad-sidebar-width-collapsed) - 1.125rem)/2);font-size:var(--fad-nav-icon-size);width:1.125rem}}:root{--qdadm-sidebar-width: 15rem;--qdadm-sidebar-collapsed-width: 2.5rem;--qdadm-sidebar-transition: 200ms ease;--qdadm-content-padding: 1rem;--qdadm-content-padding-mobile: 0.75rem}@media(max-width: 1023px){.app-layout{--qdadm-sidebar-width: 12rem}}@media(max-width: 767px){.app-layout{--qdadm-sidebar-width: 100vw}}.sidebar{transition:transform var(--qdadm-sidebar-transition),width var(--qdadm-sidebar-transition)}@media(max-width: 767px){.sidebar{transform:translateX(-100%);position:fixed;z-index:1000;box-shadow:none;width:100vw !important;height:100dvh;max-height:100dvh;overflow-y:auto}.sidebar.sidebar--open{transform:translateX(0);box-shadow:none}}.main-content{transition:margin-left var(--qdadm-sidebar-transition)}@media(max-width: 767px){.main-content{margin-left:0 !important}}.sidebar-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:99;opacity:0;transition:opacity .25s}@media(max-width: 767px){.sidebar-overlay{display:block;pointer-events:none}.sidebar-overlay.sidebar-overlay--visible{opacity:1;pointer-events:auto}}.hamburger-btn{display:none !important}@media(max-width: 767px){.hamburger-btn{display:flex !important}}@media(max-width: 767px){.page-content{padding:var(--qdadm-content-padding-mobile)}}@media(max-width: 767px){.page-header{flex-direction:column;gap:1rem;align-items:flex-start !important}}@media(max-width: 767px){.page-header .page-header-actions{width:100%;justify-content:flex-start}}.p-datatable .p-datatable-table-container{overflow-x:auto;-webkit-overflow-scrolling:touch}@media(max-width: 767px){.p-datatable .p-datatable-table{min-width:600px}}@media(max-width: 479px){.p-datatable .p-datatable-table{min-width:500px}}@media(max-width: 767px){.filter-bar{flex-wrap:wrap;gap:.75rem}}@media(max-width: 767px){.filter-bar-left{flex-wrap:wrap;gap:.5rem;width:100%}}@media(max-width: 767px){.card{border-radius:0;border:none;background:rgba(0,0,0,0);padding:0}}@media(max-width: 767px){.form-grid{grid-template-columns:1fr !important}}@media(max-width: 479px){.form-actions{flex-direction:column;gap:.5rem}.form-actions .form-actions-left{width:100%;flex-direction:column}.form-actions .p-button{width:100%;white-space:nowrap}}@media(max-width: 767px){.cards-grid{grid-template-columns:repeat(2, 1fr)}}@media(max-width: 479px){.cards-grid{grid-template-columns:1fr}}@media(max-width: 767px){.hide-mobile{display:none !important}}.hide-desktop{display:none !important}@media(max-width: 767px){.hide-desktop{display:block !important}}@media(max-width: 767px){.hide-mobile-inline{display:none !important}}.show-mobile-inline{display:none !important}@media(max-width: 767px){.show-mobile-inline{display:inline !important}}
|
package/package.json
CHANGED
|
@@ -313,17 +313,29 @@ const showBreadcrumb = computed<boolean>(() => {
|
|
|
313
313
|
></i>
|
|
314
314
|
</div>
|
|
315
315
|
<div class="nav-section-items" :class="{ 'nav-section-collapsed': !isSectionExpanded(section) }">
|
|
316
|
-
<
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
316
|
+
<template v-for="item in section.items" :key="item.route ?? item.href">
|
|
317
|
+
<a
|
|
318
|
+
v-if="item.href"
|
|
319
|
+
:href="item.href"
|
|
320
|
+
:target="item.target"
|
|
321
|
+
:rel="item.rel"
|
|
322
|
+
class="nav-item nav-item-link"
|
|
323
|
+
>
|
|
324
|
+
<i :class="item.icon"></i>
|
|
325
|
+
<span>{{ item.label }}</span>
|
|
326
|
+
<span v-if="item.external" class="pi pi-external-link nav-item-external" aria-label="opens in a new tab"></span>
|
|
327
|
+
</a>
|
|
328
|
+
<RouterLink
|
|
329
|
+
v-else
|
|
330
|
+
:to="{ name: item.route }"
|
|
331
|
+
class="nav-item"
|
|
332
|
+
:class="{ 'nav-item-active': isNavActive(item) }"
|
|
333
|
+
@click="handleNavClick($event, item)"
|
|
334
|
+
>
|
|
335
|
+
<i :class="item.icon"></i>
|
|
336
|
+
<span>{{ item.label }}</span>
|
|
337
|
+
</RouterLink>
|
|
338
|
+
</template>
|
|
327
339
|
</div>
|
|
328
340
|
</div>
|
|
329
341
|
</nav>
|
|
@@ -110,17 +110,29 @@ watch(() => route?.path, () => {
|
|
|
110
110
|
></i>
|
|
111
111
|
</div>
|
|
112
112
|
<div class="nav-section-items" :class="{ 'nav-section-collapsed': !isSectionExpanded(section) }">
|
|
113
|
-
<
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
113
|
+
<template v-for="item in section.items" :key="item.route ?? item.href">
|
|
114
|
+
<a
|
|
115
|
+
v-if="item.href"
|
|
116
|
+
:href="item.href"
|
|
117
|
+
:target="item.target"
|
|
118
|
+
:rel="item.rel"
|
|
119
|
+
class="nav-item nav-item-link"
|
|
120
|
+
>
|
|
121
|
+
<i :class="item.icon"></i>
|
|
122
|
+
<span>{{ item.label }}</span>
|
|
123
|
+
<span v-if="item.external" class="pi pi-external-link nav-item-external" aria-label="opens in a new tab"></span>
|
|
124
|
+
</a>
|
|
125
|
+
<RouterLink
|
|
126
|
+
v-else
|
|
127
|
+
:to="{ name: item.route }"
|
|
128
|
+
class="nav-item"
|
|
129
|
+
:class="{ 'nav-item-active': isNavActive(item) }"
|
|
130
|
+
@click="handleNavClick($event, item)"
|
|
131
|
+
>
|
|
132
|
+
<i :class="item.icon"></i>
|
|
133
|
+
<span>{{ item.label }}</span>
|
|
134
|
+
</RouterLink>
|
|
135
|
+
</template>
|
|
124
136
|
</div>
|
|
125
137
|
</div>
|
|
126
138
|
</nav>
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
import { computed, type PropType } from 'vue'
|
|
29
29
|
import { RouterLink, type RouteLocationRaw } from 'vue-router'
|
|
30
30
|
import Tag from 'primevue/tag'
|
|
31
|
-
import { formatDate, formatNumber, formatCurrency } from '../../utils/formatters'
|
|
31
|
+
import { formatDate, formatNumber, formatCurrency, getEmptyPlaceholder } from '../../utils/formatters'
|
|
32
32
|
|
|
33
33
|
type DisplayType =
|
|
34
34
|
| 'text'
|
|
@@ -80,6 +80,8 @@ interface FieldConfig {
|
|
|
80
80
|
imageHeight?: string
|
|
81
81
|
// Custom render
|
|
82
82
|
render?: (value: unknown) => string
|
|
83
|
+
/** Shown for an empty value; defaults to the kernel's `display.emptyPlaceholder` (#2772). */
|
|
84
|
+
emptyText?: string
|
|
83
85
|
}
|
|
84
86
|
|
|
85
87
|
const props = defineProps({
|
|
@@ -97,16 +99,19 @@ const isEmpty = computed(() => {
|
|
|
97
99
|
return props.value === null || props.value === undefined || props.value === ''
|
|
98
100
|
})
|
|
99
101
|
|
|
102
|
+
// What an empty value shows: the field's own, else the app-wide placeholder (#2772)
|
|
103
|
+
const emptyText = computed(() => props.field.emptyText ?? getEmptyPlaceholder())
|
|
104
|
+
|
|
100
105
|
// Format text value
|
|
101
106
|
const textValue = computed(() => {
|
|
102
|
-
if (isEmpty.value) return
|
|
107
|
+
if (isEmpty.value) return emptyText.value
|
|
103
108
|
if (props.field.render) return props.field.render(props.value)
|
|
104
109
|
return String(props.value)
|
|
105
110
|
})
|
|
106
111
|
|
|
107
112
|
// Format number value (browser locale by default — utils/formatters policy)
|
|
108
113
|
const numberValue = computed(() => {
|
|
109
|
-
if (isEmpty.value) return
|
|
114
|
+
if (isEmpty.value) return emptyText.value
|
|
110
115
|
const num = Number(props.value)
|
|
111
116
|
if (isNaN(num)) return String(props.value)
|
|
112
117
|
// useGrouping: false → no locale digit grouping (years, ids… "1965",
|
|
@@ -117,7 +122,7 @@ const numberValue = computed(() => {
|
|
|
117
122
|
|
|
118
123
|
// Format currency value
|
|
119
124
|
const currencyValue = computed(() => {
|
|
120
|
-
if (isEmpty.value) return
|
|
125
|
+
if (isEmpty.value) return emptyText.value
|
|
121
126
|
const num = Number(props.value)
|
|
122
127
|
if (isNaN(num)) return String(props.value)
|
|
123
128
|
return formatCurrency(num, props.field.currencyCode || 'USD', props.field.locale)
|
|
@@ -125,7 +130,7 @@ const currencyValue = computed(() => {
|
|
|
125
130
|
|
|
126
131
|
// Format boolean value
|
|
127
132
|
const booleanValue = computed(() => {
|
|
128
|
-
if (isEmpty.value) return
|
|
133
|
+
if (isEmpty.value) return emptyText.value
|
|
129
134
|
const labels = props.field.booleanLabels || { true: 'Yes', false: 'No' }
|
|
130
135
|
return props.value ? labels.true : labels.false
|
|
131
136
|
})
|
|
@@ -142,7 +147,7 @@ const booleanIconClass = computed(() => {
|
|
|
142
147
|
|
|
143
148
|
// Format date value
|
|
144
149
|
const dateValue = computed(() => {
|
|
145
|
-
if (isEmpty.value) return
|
|
150
|
+
if (isEmpty.value) return emptyText.value
|
|
146
151
|
const v = props.value as unknown
|
|
147
152
|
const date = (typeof v === 'object' && v instanceof Date) ? v : new Date(String(v))
|
|
148
153
|
if (isNaN(date.getTime())) return String(props.value)
|
|
@@ -152,7 +157,7 @@ const dateValue = computed(() => {
|
|
|
152
157
|
|
|
153
158
|
// Format datetime value
|
|
154
159
|
const datetimeValue = computed(() => {
|
|
155
|
-
if (isEmpty.value) return
|
|
160
|
+
if (isEmpty.value) return emptyText.value
|
|
156
161
|
const v = props.value as unknown
|
|
157
162
|
const date = (typeof v === 'object' && v instanceof Date) ? v : new Date(String(v))
|
|
158
163
|
if (isNaN(date.getTime())) return String(props.value)
|
|
@@ -161,7 +166,7 @@ const datetimeValue = computed(() => {
|
|
|
161
166
|
|
|
162
167
|
// Format select value (lookup label from options)
|
|
163
168
|
const selectValue = computed(() => {
|
|
164
|
-
if (isEmpty.value) return
|
|
169
|
+
if (isEmpty.value) return emptyText.value
|
|
165
170
|
const options = props.field.options || []
|
|
166
171
|
const optionValue = props.field.optionValue || 'value'
|
|
167
172
|
const optionLabel = props.field.optionLabel || 'label'
|
|
@@ -184,7 +189,7 @@ const referenceRoute = computed<RouteLocationRaw | null>(() => {
|
|
|
184
189
|
|
|
185
190
|
// Reference label
|
|
186
191
|
const referenceLabel = computed(() => {
|
|
187
|
-
if (isEmpty.value) return
|
|
192
|
+
if (isEmpty.value) return emptyText.value
|
|
188
193
|
if (typeof props.field.referenceLabel === 'function') {
|
|
189
194
|
return props.field.referenceLabel(props.value)
|
|
190
195
|
}
|
|
@@ -215,7 +220,7 @@ const badgeIcon = computed(() => badgeDescriptor.value.icon)
|
|
|
215
220
|
|
|
216
221
|
// JSON formatted
|
|
217
222
|
const jsonValue = computed(() => {
|
|
218
|
-
if (isEmpty.value) return
|
|
223
|
+
if (isEmpty.value) return emptyText.value
|
|
219
224
|
try {
|
|
220
225
|
return JSON.stringify(props.value, null, 2)
|
|
221
226
|
} catch {
|
|
@@ -232,7 +237,7 @@ const imageStyle = computed(() => ({
|
|
|
232
237
|
|
|
233
238
|
<template>
|
|
234
239
|
<!-- Empty value -->
|
|
235
|
-
<span v-if="isEmpty" class="show-display show-display--empty"
|
|
240
|
+
<span v-if="isEmpty" class="show-display show-display--empty">{{ emptyText }}</span>
|
|
236
241
|
|
|
237
242
|
<!-- Text -->
|
|
238
243
|
<span v-else-if="displayType === 'text'" class="show-display show-display--text">
|
|
@@ -361,6 +361,7 @@ export function useEntityItemShowPage<T = Record<string, unknown>>(
|
|
|
361
361
|
notifications.addNotification({ severity: 'info', keep: 'short', ...to, ...entry })
|
|
362
362
|
}, {
|
|
363
363
|
id: () => entityId.value,
|
|
364
|
+
screen: 'show',
|
|
364
365
|
// The update has arrived: say so on the badge at once — before the reload is
|
|
365
366
|
// coalesced, let alone finished (#2679).
|
|
366
367
|
onEvent: () => notifications.flash(),
|
|
@@ -1227,7 +1227,7 @@ export function useListPage<T = unknown>(config: UseListPageOptions<T>): UseList
|
|
|
1227
1227
|
keptKeys.has((item as Record<string, unknown>)[resolvedDataKey])
|
|
1228
1228
|
) as T[]
|
|
1229
1229
|
}
|
|
1230
|
-
})
|
|
1230
|
+
}, { screen: 'list' })
|
|
1231
1231
|
|
|
1232
1232
|
// ============ UTILITIES ============
|
|
1233
1233
|
// Browser-locale by default (unified policy — see utils/formatters).
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
* There is no page-level opt-in, deliberately. The gate is upstream: only an
|
|
11
11
|
* entity the app declared in `sse.entities` ever produces a remote event, so an
|
|
12
12
|
* app with no live backend sees no behaviour change and has no flag to forget.
|
|
13
|
-
* What the entity does about it is its own policy (`live` on the manager)
|
|
13
|
+
* What the entity does about it is its own policy (`live` on the manager) —
|
|
14
|
+
* which may differ per screen and per change kind (#2971); the page builders
|
|
15
|
+
* only say which screen they are.
|
|
14
16
|
*
|
|
15
17
|
* @experimental Shape may change in a minor release — see docs/API_STABILITY.md.
|
|
16
18
|
*/
|
|
@@ -33,10 +35,17 @@ export interface UseLiveEntityOptions {
|
|
|
33
35
|
* notification badge's flash.
|
|
34
36
|
*/
|
|
35
37
|
onEvent?: () => void
|
|
38
|
+
/**
|
|
39
|
+
* Which screen this is, to pick its rule when the entity's `live.refresh`
|
|
40
|
+
* is set per screen (#2971). Without it, a per-screen policy means `'mounted'`.
|
|
41
|
+
*/
|
|
42
|
+
screen?: 'list' | 'show'
|
|
36
43
|
}
|
|
37
44
|
|
|
45
|
+
type RefreshRule = 'mounted' | false | string[]
|
|
46
|
+
|
|
38
47
|
interface LivePolicy {
|
|
39
|
-
refresh?:
|
|
48
|
+
refresh?: RefreshRule | { list?: RefreshRule; show?: RefreshRule }
|
|
40
49
|
coalesceMs?: number
|
|
41
50
|
}
|
|
42
51
|
|
|
@@ -62,9 +71,15 @@ export function useLiveEntity(
|
|
|
62
71
|
if (!signals || !entityName) return () => {}
|
|
63
72
|
|
|
64
73
|
const policy = (manager as { live?: LivePolicy } | null | undefined)?.live ?? {}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
74
|
+
const refresh = policy.refresh
|
|
75
|
+
const rule: RefreshRule = refresh !== null && typeof refresh === 'object' && !Array.isArray(refresh)
|
|
76
|
+
? (options.screen ? refresh[options.screen] : undefined) ?? 'mounted'
|
|
77
|
+
: refresh ?? 'mounted'
|
|
78
|
+
// `false` — invalidation still happened upstream, the screen just does not
|
|
79
|
+
// chase it. Subscribing would only burn a listener.
|
|
80
|
+
if (rule === false) return () => {}
|
|
81
|
+
// An array names the change kinds worth a reload (#2971).
|
|
82
|
+
const kinds = Array.isArray(rule) ? new Set(rule) : null
|
|
68
83
|
|
|
69
84
|
const coalesceMs = policy.coalesceMs ?? 300
|
|
70
85
|
let timer: ReturnType<typeof setTimeout> | null = null
|
|
@@ -114,6 +129,7 @@ export function useLiveEntity(
|
|
|
114
129
|
const payload = (event.data || {}) as {
|
|
115
130
|
entity?: string
|
|
116
131
|
id?: string | number
|
|
132
|
+
action?: string
|
|
117
133
|
source?: string
|
|
118
134
|
}
|
|
119
135
|
|
|
@@ -135,6 +151,10 @@ export function useLiveEntity(
|
|
|
135
151
|
}
|
|
136
152
|
}
|
|
137
153
|
|
|
154
|
+
// A kind this screen ignores is not news to it: no cue, no reload. An event
|
|
155
|
+
// carrying no kind is an update, as the live router reports it by default.
|
|
156
|
+
if (kinds && !kinds.has(payload.action ?? 'updated')) return
|
|
157
|
+
|
|
138
158
|
options.onEvent?.()
|
|
139
159
|
|
|
140
160
|
// Coalesce: a backend replaying fifty rows must cost one reload, not fifty.
|
|
@@ -26,7 +26,19 @@ type Orchestrator = OrchestratorLike
|
|
|
26
26
|
*/
|
|
27
27
|
export interface NavItem {
|
|
28
28
|
label: string
|
|
29
|
-
route
|
|
29
|
+
route?: string
|
|
30
|
+
/**
|
|
31
|
+
* A link out of the app instead of a route (#3675) — exactly one of `route`
|
|
32
|
+
* and `href`. An absolute URL opens in a new tab unless `target` says otherwise.
|
|
33
|
+
*/
|
|
34
|
+
href?: string
|
|
35
|
+
/** Where an `href` entry opens: default `_blank` for an absolute URL, same tab otherwise. */
|
|
36
|
+
target?: string
|
|
37
|
+
/** An `href` entry is shown only to whoever is granted this attribute. */
|
|
38
|
+
permission?: string
|
|
39
|
+
/** Set on `href` entries by `navSections`: opens outside the app in another tab. */
|
|
40
|
+
external?: boolean
|
|
41
|
+
rel?: string
|
|
30
42
|
icon?: string
|
|
31
43
|
entity?: string
|
|
32
44
|
exact?: boolean
|
|
@@ -79,6 +91,8 @@ export function useNavigation(): UseNavigationReturn {
|
|
|
79
91
|
const router = useRouter()
|
|
80
92
|
const orchestrator = inject<Orchestrator | null>('qdadmOrchestrator', null)
|
|
81
93
|
const hooks = inject<HookRegistry | null>('qdadmHooks', null)
|
|
94
|
+
// null default: an app without security shows every link (#3675).
|
|
95
|
+
const securityChecker = inject<{ isGranted(attribute: string, subject?: unknown): boolean } | null>('qdadmSecurityChecker', null)
|
|
82
96
|
|
|
83
97
|
// i18n integration — labels resolve through nav.sections.* / nav.routes.*
|
|
84
98
|
const { i18n: kernelI18n, locale: i18nLocale } = useI18n()
|
|
@@ -96,13 +110,17 @@ export function useNavigation(): UseNavigationReturn {
|
|
|
96
110
|
* Check if user can access a nav item: canList() when it opens a list route, canRead() otherwise
|
|
97
111
|
*/
|
|
98
112
|
function canAccessNavItem(item: NavItem): boolean {
|
|
113
|
+
// A link out has no page to guard: only its own permission, if any (#3675).
|
|
114
|
+
if (item.href) {
|
|
115
|
+
return !item.permission || !securityChecker || securityChecker.isGranted(item.permission)
|
|
116
|
+
}
|
|
99
117
|
if (!item.entity || !orchestrator) return true
|
|
100
118
|
const manager = orchestrator.get(item.entity)
|
|
101
119
|
if (!manager) return true
|
|
102
120
|
// A menu entry is shown to whoever may do what its page does (#2497): a
|
|
103
121
|
// ctx.crud() entry opens a list, so it follows `list`. A route that
|
|
104
122
|
// declares no action, or is not registered, keeps `read`.
|
|
105
|
-
if (routeAction(item.route) === 'list' && manager.canList) return manager.canList()
|
|
123
|
+
if (item.route && routeAction(item.route) === 'list' && manager.canList) return manager.canList()
|
|
106
124
|
return manager.canRead()
|
|
107
125
|
}
|
|
108
126
|
|
|
@@ -126,6 +144,21 @@ export function useNavigation(): UseNavigationReturn {
|
|
|
126
144
|
}
|
|
127
145
|
})
|
|
128
146
|
|
|
147
|
+
/**
|
|
148
|
+
* A link out of the app as the renderers draw it (#3675): an absolute URL
|
|
149
|
+
* opens in a new tab, without handing it this window.
|
|
150
|
+
*/
|
|
151
|
+
function linkItem(item: NavItem): NavItem {
|
|
152
|
+
const absolute = /^([a-z][a-z\d+.-]*:|\/\/)/i.test(item.href!)
|
|
153
|
+
const target = item.target ?? (absolute ? '_blank' : undefined)
|
|
154
|
+
return {
|
|
155
|
+
...item,
|
|
156
|
+
...(target ? { target } : {}),
|
|
157
|
+
external: target === '_blank',
|
|
158
|
+
...(target === '_blank' ? { rel: 'noopener noreferrer' } : {}),
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
129
162
|
/**
|
|
130
163
|
* Resolve a label through i18n with the given convention key, falling back
|
|
131
164
|
* to the inline label when the bundle has no matching translation.
|
|
@@ -153,7 +186,7 @@ export function useNavigation(): UseNavigationReturn {
|
|
|
153
186
|
title: resolveNavLabel(`nav.sections.${section.title}`, section.title),
|
|
154
187
|
items: section.items
|
|
155
188
|
.filter((item) => canAccessNavItem(item))
|
|
156
|
-
.map((item) => ({
|
|
189
|
+
.map((item) => (item.href ? linkItem(item) : {
|
|
157
190
|
...item,
|
|
158
191
|
label: resolveNavLabel(`nav.routes.${item.route}`, item.label),
|
|
159
192
|
})),
|
|
@@ -166,6 +199,8 @@ export function useNavigation(): UseNavigationReturn {
|
|
|
166
199
|
* Uses semantic breadcrumb only - no route segment deduction
|
|
167
200
|
*/
|
|
168
201
|
function isNavActive(item: NavItem): boolean {
|
|
202
|
+
// A link out never stands for the page on screen (#3675).
|
|
203
|
+
if (item.href) return false
|
|
169
204
|
// Exact match mode
|
|
170
205
|
if (item.exact) {
|
|
171
206
|
return route.name === item.route
|
|
@@ -199,6 +234,7 @@ export function useNavigation(): UseNavigationReturn {
|
|
|
199
234
|
* Handle nav click - force navigation if on same route with query params
|
|
200
235
|
*/
|
|
201
236
|
function handleNavClick(event: Event, item: NavItem): void {
|
|
237
|
+
if (item.href) return
|
|
202
238
|
if (route.name === item.route && Object.keys(route.query).length > 0) {
|
|
203
239
|
event.preventDefault()
|
|
204
240
|
router.push({ name: item.route })
|
|
@@ -18,6 +18,7 @@ import type { HookRegistry } from '../hooks/HookRegistry'
|
|
|
18
18
|
import type { EntityManager } from './EntityManager'
|
|
19
19
|
import type { RouteStatePersister } from '../routeState'
|
|
20
20
|
import type { NewNotification } from '../notifications/NotificationStore'
|
|
21
|
+
import type { LiveEntityAction } from '../kernel/LiveEntityRouter'
|
|
21
22
|
|
|
22
23
|
// ============ INTERNAL TYPES ============
|
|
23
24
|
|
|
@@ -224,8 +225,13 @@ export interface LiveEntityPolicy {
|
|
|
224
225
|
/**
|
|
225
226
|
* `'mounted'` (default) — a screen currently showing this entity reloads.
|
|
226
227
|
* `false` — invalidate the cache only; the screen updates next time it asks.
|
|
228
|
+
* An array of change kinds — reload only for those (`['created', 'deleted']`:
|
|
229
|
+
* rows came or went). An event carrying no kind counts as `'updated'`.
|
|
230
|
+
*
|
|
231
|
+
* One rule applies to every screen; `{ list, show }` sets it per screen
|
|
232
|
+
* (#2971), a screen left out keeping `'mounted'`.
|
|
227
233
|
*/
|
|
228
|
-
refresh?:
|
|
234
|
+
refresh?: LiveRefreshRule | LiveRefreshPolicy
|
|
229
235
|
/**
|
|
230
236
|
* Window (ms) over which a burst of events collapses into a single reload.
|
|
231
237
|
* Default 300. `0` reloads on every event — rarely what you want.
|
|
@@ -246,6 +252,15 @@ export interface LiveEntityPolicy {
|
|
|
246
252
|
describeUpdate?: (before: EntityRecord, after: EntityRecord) => LiveUpdateEntry | null | undefined
|
|
247
253
|
}
|
|
248
254
|
|
|
255
|
+
/** What a screen does about a change made elsewhere (#2971). */
|
|
256
|
+
export type LiveRefreshRule = 'mounted' | false | LiveEntityAction[]
|
|
257
|
+
|
|
258
|
+
/** `LiveEntityPolicy.refresh` per screen (#2971): the list pages and the detail pages. */
|
|
259
|
+
export interface LiveRefreshPolicy {
|
|
260
|
+
list?: LiveRefreshRule
|
|
261
|
+
show?: LiveRefreshRule
|
|
262
|
+
}
|
|
263
|
+
|
|
249
264
|
/** The notification entry `LiveEntityPolicy.describeUpdate` asks for (#2685). */
|
|
250
265
|
export type LiveUpdateEntry = Omit<NewNotification, 'severity'> & Partial<Pick<NewNotification, 'severity'>>
|
|
251
266
|
|
package/src/index.ts
CHANGED
|
@@ -41,7 +41,7 @@ export { SSEBridge, createSSEBridge, SSE_SIGNALS } from './kernel/SSEBridge'
|
|
|
41
41
|
export type { SSEBridgeOptions } from './kernel/SSEBridge'
|
|
42
42
|
export { useLiveEntity } from './composables/useLiveEntity'
|
|
43
43
|
export type { UseLiveEntityOptions } from './composables/useLiveEntity'
|
|
44
|
-
export type { LiveEntityPolicy, LiveUpdateEntry } from './entity/EntityManager.types'
|
|
44
|
+
export type { LiveEntityPolicy, LiveUpdateEntry, LiveRefreshRule, LiveRefreshPolicy } from './entity/EntityManager.types'
|
|
45
45
|
export { LiveEntityRouter, createLiveEntityRouter } from './kernel/LiveEntityRouter'
|
|
46
46
|
export type {
|
|
47
47
|
LiveEntityRouterOptions,
|
|
@@ -375,7 +375,7 @@ export function applyRegistryMethods(KernelClass: { prototype: Kernel }): void {
|
|
|
375
375
|
*/
|
|
376
376
|
const KNOWN_KERNEL_OPTIONS = new Set([
|
|
377
377
|
'apiClient', 'app', 'authAdapter', 'authTypes', 'basePath', 'coreRoutes',
|
|
378
|
-
'debug', 'debugBar', 'defaultEntityCacheTtlMs', 'entityAuthAdapter',
|
|
378
|
+
'debug', 'debugBar', 'defaultEntityCacheTtlMs', 'display', 'entityAuthAdapter',
|
|
379
379
|
'eventRouter', 'existingApp', 'existingRouter', 'existingSignals',
|
|
380
380
|
'features', 'hashMode', 'homeRoute', 'i18n', 'layouts', 'managerRegistry',
|
|
381
381
|
'managerResolver', 'managers', 'moduleDefs', 'modules', 'modulesOptions',
|
package/src/kernel/Kernel.ts
CHANGED
|
@@ -81,6 +81,7 @@ import { applyApiMethods } from './Kernel.api'
|
|
|
81
81
|
import { applyI18nMethods } from './Kernel.i18n'
|
|
82
82
|
import type { I18n } from '../i18n/I18n'
|
|
83
83
|
import type { SseSubscriptions } from './sseSubscriptions'
|
|
84
|
+
import { setEmptyPlaceholder } from '../utils/formatters'
|
|
84
85
|
|
|
85
86
|
export class Kernel {
|
|
86
87
|
options: KernelOptions
|
|
@@ -133,6 +134,8 @@ export class Kernel {
|
|
|
133
134
|
_authImpersonationCleanup: (() => void) | null = null
|
|
134
135
|
|
|
135
136
|
constructor(options: KernelOptions) {
|
|
137
|
+
// Module-level, like the formatters that read it: one app per page (#2772).
|
|
138
|
+
if (options.display?.emptyPlaceholder !== undefined) setEmptyPlaceholder(options.display.emptyPlaceholder)
|
|
136
139
|
// Auto-inject DebugModule if debugBar.module is provided
|
|
137
140
|
if (options.debugBar?.module) {
|
|
138
141
|
const DebugModuleClass = options.debugBar.module
|
|
@@ -268,6 +268,11 @@ export interface KernelOptions {
|
|
|
268
268
|
routeState?: string | RouteStatePersister
|
|
269
269
|
debugBar?: DebugBarConfig
|
|
270
270
|
notifications?: NotificationsConfig
|
|
271
|
+
/** App-wide display defaults (#2772). */
|
|
272
|
+
display?: {
|
|
273
|
+
/** What a missing value shows, in detail pages and formatters. Default `'-'`. */
|
|
274
|
+
emptyPlaceholder?: string
|
|
275
|
+
}
|
|
271
276
|
toast?: Record<string, unknown>
|
|
272
277
|
debug?: boolean
|
|
273
278
|
onAuthExpired?: (payload: unknown) => void
|
|
@@ -87,7 +87,17 @@ export type DeferredFactory = () => Promise<void>
|
|
|
87
87
|
/** Navigation item configuration */
|
|
88
88
|
export interface NavItem {
|
|
89
89
|
section: string
|
|
90
|
-
route
|
|
90
|
+
/** Route name — exactly one of `route` and `href`. */
|
|
91
|
+
route?: string
|
|
92
|
+
/**
|
|
93
|
+
* A link out of the app instead of a route (#3675) — exactly one of `route`
|
|
94
|
+
* and `href`. An absolute URL opens in a new tab unless `target` says otherwise.
|
|
95
|
+
*/
|
|
96
|
+
href?: string
|
|
97
|
+
/** Where an `href` entry opens: default `_blank` for an absolute URL, same tab otherwise. */
|
|
98
|
+
target?: string
|
|
99
|
+
/** An `href` entry is shown only to whoever is granted this attribute. */
|
|
100
|
+
permission?: string
|
|
91
101
|
label: string
|
|
92
102
|
icon?: string
|
|
93
103
|
exact?: boolean
|
|
@@ -74,7 +74,16 @@ export interface ModuleRoute {
|
|
|
74
74
|
* Navigation item
|
|
75
75
|
*/
|
|
76
76
|
export interface NavItem {
|
|
77
|
-
route
|
|
77
|
+
route?: string
|
|
78
|
+
/**
|
|
79
|
+
* A link out of the app instead of a route (#3675) — exactly one of `route`
|
|
80
|
+
* and `href`. An absolute URL opens in a new tab unless `target` says otherwise.
|
|
81
|
+
*/
|
|
82
|
+
href?: string
|
|
83
|
+
/** Where an `href` entry opens: default `_blank` for an absolute URL, same tab otherwise. */
|
|
84
|
+
target?: string
|
|
85
|
+
/** An `href` entry is shown only to whoever is granted this attribute. */
|
|
86
|
+
permission?: string
|
|
78
87
|
label: string
|
|
79
88
|
icon?: string
|
|
80
89
|
entity?: string
|
|
@@ -209,6 +218,13 @@ const registry = {
|
|
|
209
218
|
* @param item - { section, route, icon, label, exact? }
|
|
210
219
|
*/
|
|
211
220
|
addNavItem(item: NavItemWithSection): void {
|
|
221
|
+
// An entry is a route or a link out, never both, never neither (#3675):
|
|
222
|
+
// an ambiguous one fails at boot rather than quietly in the menu.
|
|
223
|
+
if (!item.route === !item.href) {
|
|
224
|
+
throw new Error(
|
|
225
|
+
`[qdadm] nav item "${item.label}" in "${item.section}" needs exactly one of route and href`
|
|
226
|
+
)
|
|
227
|
+
}
|
|
212
228
|
const { section, ...navItem } = item
|
|
213
229
|
if (!navSections.has(section)) {
|
|
214
230
|
navSections.set(section, [])
|
package/src/styles/_sidebar.scss
CHANGED
|
@@ -315,6 +315,14 @@ $nav-item-content-left: calc((var(--fad-sidebar-width-collapsed) + #{$nav-item-i
|
|
|
315
315
|
transition: opacity $sidebar-box-content-duration ease $sidebar-box-content-delay;
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
+
// A link out of the app (#3675): a small marker after the label, hidden with it
|
|
319
|
+
// when the sidebar collapses.
|
|
320
|
+
.sidebar-nav .nav-item .nav-item-external {
|
|
321
|
+
margin-left: 0.375rem;
|
|
322
|
+
font-size: 0.625rem;
|
|
323
|
+
color: var(--p-surface-400, #94a3b8);
|
|
324
|
+
}
|
|
325
|
+
|
|
318
326
|
.sidebar-nav .nav-item:hover {
|
|
319
327
|
background: var(--p-surface-700, #334155);
|
|
320
328
|
color: var(--p-surface-0, white);
|
package/src/types/index.ts
CHANGED
|
@@ -109,6 +109,8 @@ export interface FieldConfig {
|
|
|
109
109
|
filterable?: boolean
|
|
110
110
|
searchable?: boolean
|
|
111
111
|
validator?: (value: unknown) => boolean | string
|
|
112
|
+
/** Shown in displays for an empty value; defaults to the kernel's `display.emptyPlaceholder` (#2772). */
|
|
113
|
+
emptyText?: string
|
|
112
114
|
[key: string]: unknown
|
|
113
115
|
}
|
|
114
116
|
|
package/src/utils/formatters.ts
CHANGED
|
@@ -8,6 +8,25 @@
|
|
|
8
8
|
* {{ formatDateTime(item.updated_at) }}
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// EMPTY PLACEHOLDER
|
|
13
|
+
// =============================================================================
|
|
14
|
+
|
|
15
|
+
let emptyPlaceholder = '-'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* What qdadm shows for a missing value (#2772): the formatters and detail
|
|
19
|
+
* pages. Set once by the kernel from `display.emptyPlaceholder`.
|
|
20
|
+
*/
|
|
21
|
+
export function setEmptyPlaceholder(value: string): void {
|
|
22
|
+
emptyPlaceholder = value
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** The current empty placeholder, `'-'` unless configured. */
|
|
26
|
+
export function getEmptyPlaceholder(): string {
|
|
27
|
+
return emptyPlaceholder
|
|
28
|
+
}
|
|
29
|
+
|
|
11
30
|
// =============================================================================
|
|
12
31
|
// DATE/TIME FORMATTING
|
|
13
32
|
// =============================================================================
|
|
@@ -22,16 +41,16 @@
|
|
|
22
41
|
* @param dateStr - ISO date string or Date object
|
|
23
42
|
* @param options - Intl.DateTimeFormat options
|
|
24
43
|
* @param locale - Explicit locale override (default: browser locale)
|
|
25
|
-
* @returns Formatted date string or
|
|
44
|
+
* @returns Formatted date string or the empty placeholder if empty
|
|
26
45
|
*/
|
|
27
46
|
export function formatDate(
|
|
28
47
|
dateStr: string | Date | null | undefined,
|
|
29
48
|
options: Intl.DateTimeFormatOptions = {},
|
|
30
49
|
locale?: string
|
|
31
50
|
): string {
|
|
32
|
-
if (!dateStr) return
|
|
51
|
+
if (!dateStr) return emptyPlaceholder
|
|
33
52
|
const date = new Date(dateStr)
|
|
34
|
-
if (isNaN(date.getTime())) return
|
|
53
|
+
if (isNaN(date.getTime())) return emptyPlaceholder
|
|
35
54
|
|
|
36
55
|
if (Object.keys(options).length === 0) {
|
|
37
56
|
return date.toLocaleString(locale)
|
|
@@ -75,9 +94,9 @@ export function formatDateOnly(
|
|
|
75
94
|
options: Intl.DateTimeFormatOptions = {},
|
|
76
95
|
locale?: string
|
|
77
96
|
): string {
|
|
78
|
-
if (!dateStr) return
|
|
97
|
+
if (!dateStr) return emptyPlaceholder
|
|
79
98
|
const date = new Date(dateStr)
|
|
80
|
-
if (isNaN(date.getTime())) return
|
|
99
|
+
if (isNaN(date.getTime())) return emptyPlaceholder
|
|
81
100
|
return date.toLocaleDateString(locale, {
|
|
82
101
|
year: 'numeric',
|
|
83
102
|
month: '2-digit',
|
|
@@ -90,9 +109,9 @@ export function formatDateOnly(
|
|
|
90
109
|
* Format to time only (14:30:00)
|
|
91
110
|
*/
|
|
92
111
|
export function formatTimeOnly(dateStr: string | Date | null | undefined): string {
|
|
93
|
-
if (!dateStr) return
|
|
112
|
+
if (!dateStr) return emptyPlaceholder
|
|
94
113
|
const date = new Date(dateStr)
|
|
95
|
-
if (isNaN(date.getTime())) return
|
|
114
|
+
if (isNaN(date.getTime())) return emptyPlaceholder
|
|
96
115
|
return date.toLocaleTimeString()
|
|
97
116
|
}
|
|
98
117
|
|
|
@@ -115,7 +134,7 @@ export function formatDateOrNever(dateStr: string | Date | null | undefined): st
|
|
|
115
134
|
* @returns Human-readable duration (e.g., "2h 30m 15s" or "45.2s")
|
|
116
135
|
*/
|
|
117
136
|
export function formatDuration(ms: number | null | undefined): string {
|
|
118
|
-
if (ms === null || ms === undefined) return
|
|
137
|
+
if (ms === null || ms === undefined) return emptyPlaceholder
|
|
119
138
|
if (ms < 1000) return `${ms}ms`
|
|
120
139
|
|
|
121
140
|
const seconds = Math.floor(ms / 1000)
|
|
@@ -142,10 +161,10 @@ export function formatDurationBetween(
|
|
|
142
161
|
startDate: string | Date | null | undefined,
|
|
143
162
|
endDate: string | Date | null | undefined
|
|
144
163
|
): string {
|
|
145
|
-
if (!startDate || !endDate) return
|
|
164
|
+
if (!startDate || !endDate) return emptyPlaceholder
|
|
146
165
|
const start = new Date(startDate)
|
|
147
166
|
const end = new Date(endDate)
|
|
148
|
-
if (isNaN(start.getTime()) || isNaN(end.getTime())) return
|
|
167
|
+
if (isNaN(start.getTime()) || isNaN(end.getTime())) return emptyPlaceholder
|
|
149
168
|
return formatDuration(end.getTime() - start.getTime())
|
|
150
169
|
}
|
|
151
170
|
|
|
@@ -163,7 +182,7 @@ export function formatNumber(
|
|
|
163
182
|
options: Intl.NumberFormatOptions = {},
|
|
164
183
|
locale?: string
|
|
165
184
|
): string {
|
|
166
|
-
if (value === null || value === undefined) return
|
|
185
|
+
if (value === null || value === undefined) return emptyPlaceholder
|
|
167
186
|
return value.toLocaleString(locale, options)
|
|
168
187
|
}
|
|
169
188
|
|
|
@@ -178,7 +197,7 @@ export function formatCurrency(
|
|
|
178
197
|
currencyCode = 'USD',
|
|
179
198
|
locale?: string
|
|
180
199
|
): string {
|
|
181
|
-
if (value === null || value === undefined) return
|
|
200
|
+
if (value === null || value === undefined) return emptyPlaceholder
|
|
182
201
|
return new Intl.NumberFormat(locale, { style: 'currency', currency: currencyCode }).format(value)
|
|
183
202
|
}
|
|
184
203
|
|
|
@@ -186,7 +205,7 @@ export function formatCurrency(
|
|
|
186
205
|
* Format bytes to human-readable size
|
|
187
206
|
*/
|
|
188
207
|
export function formatBytes(bytes: number | null | undefined): string {
|
|
189
|
-
if (bytes === null || bytes === undefined) return
|
|
208
|
+
if (bytes === null || bytes === undefined) return emptyPlaceholder
|
|
190
209
|
if (bytes === 0) return '0 B'
|
|
191
210
|
|
|
192
211
|
const units = ['B', 'KB', 'MB', 'GB', 'TB']
|
|
@@ -200,7 +219,7 @@ export function formatBytes(bytes: number | null | undefined): string {
|
|
|
200
219
|
* Format a percentage
|
|
201
220
|
*/
|
|
202
221
|
export function formatPercent(value: number | null | undefined, decimals = 1): string {
|
|
203
|
-
if (value === null || value === undefined) return
|
|
222
|
+
if (value === null || value === undefined) return emptyPlaceholder
|
|
204
223
|
return `${(value * 100).toFixed(decimals)}%`
|
|
205
224
|
}
|
|
206
225
|
|