@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.
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quazardous/qdadm",
3
- "version": "2.30.0",
3
+ "version": "2.32.0",
4
4
  "description": "Vue 3 framework for admin dashboards with PrimeVue",
5
5
  "author": "quazardous",
6
6
  "license": "MIT",
@@ -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
- <RouterLink
317
- v-for="item in section.items"
318
- :key="item.route"
319
- :to="{ name: item.route }"
320
- class="nav-item"
321
- :class="{ 'nav-item-active': isNavActive(item) }"
322
- @click="handleNavClick($event, item)"
323
- >
324
- <i :class="item.icon"></i>
325
- <span>{{ item.label }}</span>
326
- </RouterLink>
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
- <RouterLink
114
- v-for="item in section.items"
115
- :key="item.route"
116
- :to="{ name: item.route }"
117
- class="nav-item"
118
- :class="{ 'nav-item-active': isNavActive(item) }"
119
- @click="handleNavClick($event, item)"
120
- >
121
- <i :class="item.icon"></i>
122
- <span>{{ item.label }}</span>
123
- </RouterLink>
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">-</span>
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?: 'mounted' | false
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
- // `refresh: false` — invalidation still happened upstream, the screen just
66
- // does not chase it. Subscribing would only burn a listener.
67
- if (policy.refresh === false) return () => {}
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: string
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?: 'mounted' | false
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',
@@ -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: string
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: string
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, [])
@@ -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);
@@ -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
 
@@ -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 '-' if empty
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
 
@@ -16,6 +16,8 @@ export {
16
16
  formatCurrency,
17
17
  formatBytes,
18
18
  formatPercent,
19
+ setEmptyPlaceholder,
20
+ getEmptyPlaceholder,
19
21
  } from './formatters'
20
22
 
21
23
  // Error formatting