@firestitch/system 18.0.44 → 18.0.46

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.
Files changed (25) hide show
  1. package/app/modules/system/components/api-logs/api-logs.component.d.ts +6 -0
  2. package/app/modules/system/components/info/info.component.d.ts +6 -0
  3. package/app/modules/system/components/server-logs/server-logs.component.d.ts +5 -0
  4. package/app/modules/system/components/setting-instructions/index.d.ts +1 -0
  5. package/app/modules/system/components/setting-instructions/setting-instructions.component.d.ts +7 -0
  6. package/app/modules/system/components/settings/settings.component.d.ts +29 -8
  7. package/app/modules/system/components/upgrade-logs/upgrade-logs.component.d.ts +6 -0
  8. package/app/modules/system/data/index.d.ts +3 -0
  9. package/app/modules/system/data/setting.data.d.ts +12 -0
  10. package/app/modules/system/fs-system.module.d.ts +2 -1
  11. package/esm2022/app/modules/system/components/api-logs/api-logs.component.mjs +14 -4
  12. package/esm2022/app/modules/system/components/info/info.component.mjs +14 -3
  13. package/esm2022/app/modules/system/components/server-logs/server-logs.component.mjs +12 -4
  14. package/esm2022/app/modules/system/components/setting-instructions/index.mjs +2 -0
  15. package/esm2022/app/modules/system/components/setting-instructions/setting-instructions.component.mjs +23 -0
  16. package/esm2022/app/modules/system/components/settings/settings.component.mjs +80 -15
  17. package/esm2022/app/modules/system/components/upgrade-logs/upgrade-logs.component.mjs +15 -4
  18. package/esm2022/app/modules/system/data/index.mjs +4 -0
  19. package/esm2022/app/modules/system/data/setting.data.mjs +27 -0
  20. package/esm2022/app/modules/system/fs-system.module.mjs +7 -3
  21. package/esm2022/public_api.mjs +2 -1
  22. package/fesm2022/firestitch-system.mjs +169 -26
  23. package/fesm2022/firestitch-system.mjs.map +1 -1
  24. package/package.json +1 -1
  25. package/public_api.d.ts +1 -0
@@ -10,6 +10,7 @@ export { InfoComponent } from './app/modules/system/components/info';
10
10
  export { ProcessComponent } from './app/modules/system/components/process';
11
11
  export { ProcessesComponent } from './app/modules/system/components/processes';
12
12
  export { ServerLogsComponent } from './app/modules/system/components/server-logs';
13
+ export { SettingInstructionsComponent } from './app/modules/system/components/setting-instructions';
13
14
  export { SettingsComponent } from './app/modules/system/components/settings';
14
15
  export { UpgradeLogsComponent } from './app/modules/system/components/upgrade-logs';
15
16
  export { KeyNameValuePipe } from './app/modules/system/pipes/key-name-value.pipe';
@@ -17,4 +18,4 @@ export { LinkifyPipe } from './app/modules/system/pipes/linkify.pipe';
17
18
  export { ApiLogStates, CronStates, ProcessStates } from './app/modules/system/consts';
18
19
  export { ApiLogState, CronState, ProcessState } from './app/modules/system/enums';
19
20
  export { FS_SYSTEM_CONFIG } from './app/modules/system/injectors/system-config.injector';
20
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljX2FwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wdWJsaWNfYXBpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUV2RSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxxQ0FBcUMsQ0FBQztBQUN6RSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0seUNBQXlDLENBQUM7QUFDMUUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMENBQTBDLENBQUM7QUFDNUUsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLHNDQUFzQyxDQUFDO0FBQ3JFLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLDBDQUEwQyxDQUFDO0FBQzVFLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUN2RSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSwyQ0FBMkMsQ0FBQztBQUMvRSxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sc0NBQXNDLENBQUM7QUFDckUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0seUNBQXlDLENBQUM7QUFDM0UsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sMkNBQTJDLENBQUM7QUFDL0UsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sNkNBQTZDLENBQUM7QUFDbEYsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sMENBQTBDLENBQUM7QUFDN0UsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sOENBQThDLENBQUM7QUFDcEYsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sZ0RBQWdELENBQUM7QUFDbEYsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLHlDQUF5QyxDQUFDO0FBRXRFLE9BQU8sRUFBRSxZQUFZLEVBQUUsVUFBVSxFQUFFLGFBQWEsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQ3RGLE9BQU8sRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBSWxGLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHVEQUF1RCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiXG5leHBvcnQgeyBGc1N5c3RlbU1vZHVsZSB9IGZyb20gJy4vYXBwL21vZHVsZXMvc3lzdGVtL2ZzLXN5c3RlbS5tb2R1bGUnO1xuXG5leHBvcnQgeyBRdWVyeUxvZ3NDb21wb25lbnQgfSBmcm9tICcuL2FwcC9tb2R1bGVzL3F1ZXJ5LWxvZ3MvY29tcG9uZW50cyc7XG5leHBvcnQgeyBBcGlMb2dDb21wb25lbnQgfSBmcm9tICcuL2FwcC9tb2R1bGVzL3N5c3RlbS9jb21wb25lbnRzL2FwaS1sb2cnO1xuZXhwb3J0IHsgQXBpTG9nc0NvbXBvbmVudCB9IGZyb20gJy4vYXBwL21vZHVsZXMvc3lzdGVtL2NvbXBvbmVudHMvYXBpLWxvZ3MnO1xuZXhwb3J0IHsgQ3JvbkNvbXBvbmVudCB9IGZyb20gJy4vYXBwL21vZHVsZXMvc3lzdGVtL2NvbXBvbmVudHMvY3Jvbic7XG5leHBvcnQgeyBDcm9uTG9nQ29tcG9uZW50IH0gZnJvbSAnLi9hcHAvbW9kdWxlcy9zeXN0ZW0vY29tcG9uZW50cy9jcm9uLWxvZyc7XG5leHBvcnQgeyBDcm9uc0NvbXBvbmVudCB9IGZyb20gJy4vYXBwL21vZHVsZXMvc3lzdGVtL2NvbXBvbmVudHMvY3JvbnMnO1xuZXhwb3J0IHsgRGFzaGJvYXJkQ29tcG9uZW50IH0gZnJvbSAnLi9hcHAvbW9kdWxlcy9zeXN0ZW0vY29tcG9uZW50cy9kYXNoYm9hcmQnO1xuZXhwb3J0IHsgSW5mb0NvbXBvbmVudCB9IGZyb20gJy4vYXBwL21vZHVsZXMvc3lzdGVtL2NvbXBvbmVudHMvaW5mbyc7XG5leHBvcnQgeyBQcm9jZXNzQ29tcG9uZW50IH0gZnJvbSAnLi9hcHAvbW9kdWxlcy9zeXN0ZW0vY29tcG9uZW50cy9wcm9jZXNzJztcbmV4cG9ydCB7IFByb2Nlc3Nlc0NvbXBvbmVudCB9IGZyb20gJy4vYXBwL21vZHVsZXMvc3lzdGVtL2NvbXBvbmVudHMvcHJvY2Vzc2VzJztcbmV4cG9ydCB7IFNlcnZlckxvZ3NDb21wb25lbnQgfSBmcm9tICcuL2FwcC9tb2R1bGVzL3N5c3RlbS9jb21wb25lbnRzL3NlcnZlci1sb2dzJztcbmV4cG9ydCB7IFNldHRpbmdzQ29tcG9uZW50IH0gZnJvbSAnLi9hcHAvbW9kdWxlcy9zeXN0ZW0vY29tcG9uZW50cy9zZXR0aW5ncyc7XG5leHBvcnQgeyBVcGdyYWRlTG9nc0NvbXBvbmVudCB9IGZyb20gJy4vYXBwL21vZHVsZXMvc3lzdGVtL2NvbXBvbmVudHMvdXBncmFkZS1sb2dzJztcbmV4cG9ydCB7IEtleU5hbWVWYWx1ZVBpcGUgfSBmcm9tICcuL2FwcC9tb2R1bGVzL3N5c3RlbS9waXBlcy9rZXktbmFtZS12YWx1ZS5waXBlJztcbmV4cG9ydCB7IExpbmtpZnlQaXBlIH0gZnJvbSAnLi9hcHAvbW9kdWxlcy9zeXN0ZW0vcGlwZXMvbGlua2lmeS5waXBlJztcblxuZXhwb3J0IHsgQXBpTG9nU3RhdGVzLCBDcm9uU3RhdGVzLCBQcm9jZXNzU3RhdGVzIH0gZnJvbSAnLi9hcHAvbW9kdWxlcy9zeXN0ZW0vY29uc3RzJztcbmV4cG9ydCB7IEFwaUxvZ1N0YXRlLCBDcm9uU3RhdGUsIFByb2Nlc3NTdGF0ZSB9IGZyb20gJy4vYXBwL21vZHVsZXMvc3lzdGVtL2VudW1zJztcblxuZXhwb3J0IHsgRGFzaGJvYXJkQWN0aW9uLCBGc1N5c3RlbUNvbmZpZywgUHJvY2Vzc0FjdGlvbiwgVXBncmFkZUV2ZW50IH0gZnJvbSAnLi9hcHAvbW9kdWxlcy9zeXN0ZW0vaW50ZXJmYWNlcyc7XG5cbmV4cG9ydCB7IEZTX1NZU1RFTV9DT05GSUcgfSBmcm9tICcuL2FwcC9tb2R1bGVzL3N5c3RlbS9pbmplY3RvcnMvc3lzdGVtLWNvbmZpZy5pbmplY3Rvcic7XG4iXX0=
21
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGljX2FwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wdWJsaWNfYXBpLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUV2RSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxxQ0FBcUMsQ0FBQztBQUN6RSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0seUNBQXlDLENBQUM7QUFDMUUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sMENBQTBDLENBQUM7QUFDNUUsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLHNDQUFzQyxDQUFDO0FBQ3JFLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLDBDQUEwQyxDQUFDO0FBQzVFLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUN2RSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSwyQ0FBMkMsQ0FBQztBQUMvRSxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sc0NBQXNDLENBQUM7QUFDckUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0seUNBQXlDLENBQUM7QUFDM0UsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sMkNBQTJDLENBQUM7QUFDL0UsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sNkNBQTZDLENBQUM7QUFDbEYsT0FBTyxFQUFFLDRCQUE0QixFQUFFLE1BQU0sc0RBQXNELENBQUM7QUFDcEcsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sMENBQTBDLENBQUM7QUFDN0UsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sOENBQThDLENBQUM7QUFDcEYsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sZ0RBQWdELENBQUM7QUFDbEYsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLHlDQUF5QyxDQUFDO0FBRXRFLE9BQU8sRUFBRSxZQUFZLEVBQUUsVUFBVSxFQUFFLGFBQWEsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQ3RGLE9BQU8sRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBSWxGLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLHVEQUF1RCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiXG5leHBvcnQgeyBGc1N5c3RlbU1vZHVsZSB9IGZyb20gJy4vYXBwL21vZHVsZXMvc3lzdGVtL2ZzLXN5c3RlbS5tb2R1bGUnO1xuXG5leHBvcnQgeyBRdWVyeUxvZ3NDb21wb25lbnQgfSBmcm9tICcuL2FwcC9tb2R1bGVzL3F1ZXJ5LWxvZ3MvY29tcG9uZW50cyc7XG5leHBvcnQgeyBBcGlMb2dDb21wb25lbnQgfSBmcm9tICcuL2FwcC9tb2R1bGVzL3N5c3RlbS9jb21wb25lbnRzL2FwaS1sb2cnO1xuZXhwb3J0IHsgQXBpTG9nc0NvbXBvbmVudCB9IGZyb20gJy4vYXBwL21vZHVsZXMvc3lzdGVtL2NvbXBvbmVudHMvYXBpLWxvZ3MnO1xuZXhwb3J0IHsgQ3JvbkNvbXBvbmVudCB9IGZyb20gJy4vYXBwL21vZHVsZXMvc3lzdGVtL2NvbXBvbmVudHMvY3Jvbic7XG5leHBvcnQgeyBDcm9uTG9nQ29tcG9uZW50IH0gZnJvbSAnLi9hcHAvbW9kdWxlcy9zeXN0ZW0vY29tcG9uZW50cy9jcm9uLWxvZyc7XG5leHBvcnQgeyBDcm9uc0NvbXBvbmVudCB9IGZyb20gJy4vYXBwL21vZHVsZXMvc3lzdGVtL2NvbXBvbmVudHMvY3JvbnMnO1xuZXhwb3J0IHsgRGFzaGJvYXJkQ29tcG9uZW50IH0gZnJvbSAnLi9hcHAvbW9kdWxlcy9zeXN0ZW0vY29tcG9uZW50cy9kYXNoYm9hcmQnO1xuZXhwb3J0IHsgSW5mb0NvbXBvbmVudCB9IGZyb20gJy4vYXBwL21vZHVsZXMvc3lzdGVtL2NvbXBvbmVudHMvaW5mbyc7XG5leHBvcnQgeyBQcm9jZXNzQ29tcG9uZW50IH0gZnJvbSAnLi9hcHAvbW9kdWxlcy9zeXN0ZW0vY29tcG9uZW50cy9wcm9jZXNzJztcbmV4cG9ydCB7IFByb2Nlc3Nlc0NvbXBvbmVudCB9IGZyb20gJy4vYXBwL21vZHVsZXMvc3lzdGVtL2NvbXBvbmVudHMvcHJvY2Vzc2VzJztcbmV4cG9ydCB7IFNlcnZlckxvZ3NDb21wb25lbnQgfSBmcm9tICcuL2FwcC9tb2R1bGVzL3N5c3RlbS9jb21wb25lbnRzL3NlcnZlci1sb2dzJztcbmV4cG9ydCB7IFNldHRpbmdJbnN0cnVjdGlvbnNDb21wb25lbnQgfSBmcm9tICcuL2FwcC9tb2R1bGVzL3N5c3RlbS9jb21wb25lbnRzL3NldHRpbmctaW5zdHJ1Y3Rpb25zJztcbmV4cG9ydCB7IFNldHRpbmdzQ29tcG9uZW50IH0gZnJvbSAnLi9hcHAvbW9kdWxlcy9zeXN0ZW0vY29tcG9uZW50cy9zZXR0aW5ncyc7XG5leHBvcnQgeyBVcGdyYWRlTG9nc0NvbXBvbmVudCB9IGZyb20gJy4vYXBwL21vZHVsZXMvc3lzdGVtL2NvbXBvbmVudHMvdXBncmFkZS1sb2dzJztcbmV4cG9ydCB7IEtleU5hbWVWYWx1ZVBpcGUgfSBmcm9tICcuL2FwcC9tb2R1bGVzL3N5c3RlbS9waXBlcy9rZXktbmFtZS12YWx1ZS5waXBlJztcbmV4cG9ydCB7IExpbmtpZnlQaXBlIH0gZnJvbSAnLi9hcHAvbW9kdWxlcy9zeXN0ZW0vcGlwZXMvbGlua2lmeS5waXBlJztcblxuZXhwb3J0IHsgQXBpTG9nU3RhdGVzLCBDcm9uU3RhdGVzLCBQcm9jZXNzU3RhdGVzIH0gZnJvbSAnLi9hcHAvbW9kdWxlcy9zeXN0ZW0vY29uc3RzJztcbmV4cG9ydCB7IEFwaUxvZ1N0YXRlLCBDcm9uU3RhdGUsIFByb2Nlc3NTdGF0ZSB9IGZyb20gJy4vYXBwL21vZHVsZXMvc3lzdGVtL2VudW1zJztcblxuZXhwb3J0IHsgRGFzaGJvYXJkQWN0aW9uLCBGc1N5c3RlbUNvbmZpZywgUHJvY2Vzc0FjdGlvbiwgVXBncmFkZUV2ZW50IH0gZnJvbSAnLi9hcHAvbW9kdWxlcy9zeXN0ZW0vaW50ZXJmYWNlcyc7XG5cbmV4cG9ydCB7IEZTX1NZU1RFTV9DT05GSUcgfSBmcm9tICcuL2FwcC9tb2R1bGVzL3N5c3RlbS9pbmplY3RvcnMvc3lzdGVtLWNvbmZpZy5pbmplY3Rvcic7XG4iXX0=
@@ -9,7 +9,7 @@ import { MatButton, MatButtonModule, MatIconButton } from '@angular/material/but
9
9
  import * as i1$3 from '@angular/material/dialog';
10
10
  import { MatDialogRef, MAT_DIALOG_DATA, MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose, MatDialog, MatDialogModule } from '@angular/material/dialog';
11
11
  import { MatAccordion, MatExpansionPanel, MatExpansionPanelHeader, MatExpansionPanelTitle, MatExpansionModule } from '@angular/material/expansion';
12
- import { MatFormField, MatLabel, MatHint, MatFormFieldModule } from '@angular/material/form-field';
12
+ import { MatFormField, MatLabel, MatHint, MatSuffix, MatFormFieldModule } from '@angular/material/form-field';
13
13
  import { MatIcon, MatIconModule } from '@angular/material/icon';
14
14
  import { MatInput, MatInputModule } from '@angular/material/input';
15
15
  import { MatProgressSpinner, MatProgressSpinnerModule } from '@angular/material/progress-spinner';
@@ -41,9 +41,9 @@ import { FsPopoverModule } from '@firestitch/popover';
41
41
  import * as i1 from '@firestitch/skeleton';
42
42
  import { FsSkeletonModule } from '@firestitch/skeleton';
43
43
  import { CdkScrollable } from '@angular/cdk/scrolling';
44
+ import { FsApi, RequestMethod, StreamEventData, StreamEventType } from '@firestitch/api';
44
45
  import { ItemType } from '@firestitch/filter';
45
46
  import { map, tap, takeUntil, switchMap, filter, take, finalize } from 'rxjs/operators';
46
- import { FsApi, RequestMethod, StreamEventData, StreamEventType } from '@firestitch/api';
47
47
  import { Subject, timer, Observable } from 'rxjs';
48
48
  import { addMinutes, addHours, addDays, subSeconds, differenceInSeconds, getDaysInMonth, differenceInMinutes } from 'date-fns';
49
49
  import { FsMessage } from '@firestitch/message';
@@ -56,6 +56,7 @@ import { index, FsCommonModule } from '@firestitch/common';
56
56
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
57
57
  import * as i3$4 from '@firestitch/clipboard';
58
58
  import { FsClipboard, FsClipboardModule } from '@firestitch/clipboard';
59
+ import { FsMarkdownRendererComponent } from '@firestitch/markdown-editor';
59
60
  import { MatOption } from '@angular/material/core';
60
61
  import { DomSanitizer } from '@angular/platform-browser';
61
62
  import * as i2$5 from '@firestitch/form';
@@ -214,7 +215,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.7", ngImpor
214
215
 
215
216
  class ApiLogsComponent {
216
217
  _dialog = inject(MatDialog);
218
+ _api = inject(FsApi);
217
219
  list;
220
+ /**
221
+ * Optional override. Left unset, the component reads the framework's own
222
+ * system/logs/api endpoint.
223
+ */
218
224
  loadApiLogs;
219
225
  config = null;
220
226
  apiLogStates = indexNameValue(ApiLogStates);
@@ -251,12 +257,16 @@ class ApiLogsComponent {
251
257
  },
252
258
  ],
253
259
  fetch: (query) => {
254
- Object.assign(query, {});
255
- return this.loadApiLogs(query)
256
- .pipe(map((response) => ({ data: response.data, paging: response.paging })));
260
+ return this._apiLogsGet(query);
257
261
  },
258
262
  };
259
263
  }
264
+ _apiLogsGet(query) {
265
+ return this.loadApiLogs
266
+ ? this.loadApiLogs(query)
267
+ : this._api.get('system/logs/api', query, { key: null })
268
+ .pipe(map((response) => ({ data: response.apiLogs, paging: response.paging })));
269
+ }
260
270
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: ApiLogsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
261
271
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.7", type: ApiLogsComponent, isStandalone: true, selector: "fs-system-api-logs", inputs: { loadApiLogs: "loadApiLogs" }, viewQueries: [{ propertyName: "list", first: true, predicate: FsListComponent, descendants: true, static: true }], ngImport: i0, template: "<fs-list [config]=\"config\">\n <fs-list-column>\n <ng-template fs-list-header>Name</ng-template>\n <ng-template fs-list-cell let-row=\"row\">\n <a href=\"javascript:;\" (click)=\"open(row)\">\n {{row.type || 'Unknown'}}\n @if (row.direction=='outgoing') {\n <mat-icon class=\"direction\">arrow_upward</mat-icon>\n }\n @if (row.direction=='incoming') {\n <mat-icon class=\"direction\">arrow_downward</mat-icon>\n }\n </a>\n </ng-template>\n </fs-list-column>\n <fs-list-column>\n <ng-template fs-list-header>Status</ng-template>\n <ng-template fs-list-cell let-row=\"row\">\n {{apiLogStates[row.state]}}\n @if (row.code) {\n <span class=\"code\" [ngClass]=\"{'success': row.code==200,'error': row.code!=200 }\">[{{row.code}}]</span>\n }\n </ng-template>\n </fs-list-column>\n <fs-list-column>\n <ng-template fs-list-header>Message</ng-template>\n <ng-template fs-list-cell let-row=\"row\">\n {{row.message}}\n </ng-template>\n </fs-list-column>\n <fs-list-column>\n <ng-template fs-list-header>Date</ng-template>\n <ng-template fs-list-cell let-row=\"row\">\n <fs-date-ago [date]=\"row.createDate\"></fs-date-ago>\n <div>@if (row.length) {\n <small>Duration: {{ row.length | fsDateDuration }}</small>\n }</div>\n </ng-template>\n </fs-list-column>\n</fs-list>\n", styles: [".code.success{color:green}.code.error{color:red}.direction{font-size:14px;height:14px;line-height:14px;width:14px}\n"], dependencies: [{ kind: "ngmodule", type: FsListModule }, { kind: "component", type: i1$1.FsListComponent, selector: "fs-list", inputs: ["config", "loaderLines"], outputs: ["filtersReady"] }, { kind: "directive", type: i1$1.FsListColumnDirective, selector: "fs-list-column", inputs: ["show", "title", "name", "customizable", "sortable", "sortableDefault", "sortableDirection", "direction", "align", "width", "class"] }, { kind: "directive", type: i1$1.FsListCellDirective, selector: "[fs-list-cell]", inputs: ["colspan", "align", "class"] }, { kind: "directive", type: i1$1.FsListHeaderDirective, selector: "[fs-list-header]", inputs: ["colspan", "align", "class"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FsDateModule }, { kind: "pipe", type: i4.FsDateDurationPipe, name: "fsDateDuration" }, { kind: "component", type: i4.FsDateAgoComponent, selector: "fs-date-ago", inputs: ["date", "showTime", "format", "showTooltip", "tooltipDateFormat"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
262
272
  }
@@ -1020,13 +1030,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.7", ngImpor
1020
1030
  }] } });
1021
1031
 
1022
1032
  class InfoComponent {
1033
+ _api = inject(FsApi);
1023
1034
  iframe;
1035
+ /**
1036
+ * Optional override. Left unset, the component reads the framework's own
1037
+ * system/info endpoint.
1038
+ */
1024
1039
  load;
1025
1040
  ngAfterViewInit() {
1026
1041
  this._load();
1027
1042
  }
1043
+ _infoGet() {
1044
+ return this.load
1045
+ ? this.load()
1046
+ : this._api.get('system/info', {}, { key: 'info' });
1047
+ }
1028
1048
  _load() {
1029
- this.load()
1049
+ this._infoGet()
1030
1050
  .subscribe((info) => {
1031
1051
  info += '<style>body { background: transparent; }</style>';
1032
1052
  const doc = this.iframe.nativeElement.contentDocument || this.iframe.nativeElement.contentWindow;
@@ -1494,6 +1514,10 @@ class ServerLogsComponent {
1494
1514
  _dialog = inject(MatDialog);
1495
1515
  _api = inject(FsApi);
1496
1516
  list;
1517
+ /**
1518
+ * Optional override. Left unset, the component reads the framework's own
1519
+ * system/logs/server endpoint.
1520
+ */
1497
1521
  loadLogs;
1498
1522
  acknowledgedAll = new EventEmitter();
1499
1523
  config = null;
@@ -1569,12 +1593,16 @@ class ServerLogsComponent {
1569
1593
  },
1570
1594
  ],
1571
1595
  fetch: (query) => {
1572
- return this._api
1573
- .get('system/logs/server', query, { key: null })
1574
- .pipe(map((response) => ({ data: response.logs, paging: response.paging })));
1596
+ return this._logsGet(query);
1575
1597
  },
1576
1598
  };
1577
1599
  }
1600
+ _logsGet(query) {
1601
+ return this.loadLogs
1602
+ ? this.loadLogs(query)
1603
+ : this._api.get('system/logs/server', query, { key: null })
1604
+ .pipe(map((response) => ({ data: response.logs, paging: response.paging })));
1605
+ }
1578
1606
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: ServerLogsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1579
1607
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.7", type: ServerLogsComponent, isStandalone: true, selector: "fs-system-server-logs", inputs: { loadLogs: "loadLogs" }, outputs: { acknowledgedAll: "acknowledgedAll" }, viewQueries: [{ propertyName: "list", first: true, predicate: FsListComponent, descendants: true }], ngImport: i0, template: "<fs-list [config]=\"config\">\n <fs-list-column width=\"1%\">\n <ng-template\n fs-list-cell\n let-row=\"row\">\n <a (click)=\"toggleAcknowledged(row)\">\n <fs-badge\n [size]=\"10\"\n [tooltip]=\"row.acknowledged ? 'Acknowledged' : 'Unacknowledged'\"\n [color]=\"row.acknowledged ? '#E3E3E3' : '#F30000'\">\n </fs-badge>\n </a>\n </ng-template>\n </fs-list-column>\n <fs-list-column>\n <ng-template fs-list-header>\n Message\n </ng-template>\n <ng-template\n fs-list-cell\n let-row=\"row\">\n <a\n href=\"javascript:;\"\n (click)=\"open(row)\">\n {{ row.message }}\n </a>\n </ng-template>\n </fs-list-column>\n <fs-list-column class=\"col-date\">\n <ng-template fs-list-header>\n Type\n </ng-template>\n <ng-template\n fs-list-cell\n let-row=\"row\">\n {{ LogTypes[row.type] }}\n </ng-template>\n </fs-list-column>\n <fs-list-column class=\"col-date\">\n <ng-template fs-list-header>\n Date\n </ng-template>\n <ng-template\n fs-list-cell\n let-row=\"row\">\n <fs-date-ago [date]=\"row.createDate\"></fs-date-ago>\n </ng-template>\n </fs-list-column>\n</fs-list>", styles: ["fs-list ::ng-deep .col-date,fs-list ::ng-deep .col-type{white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: FsListModule }, { kind: "component", type: i1$1.FsListComponent, selector: "fs-list", inputs: ["config", "loaderLines"], outputs: ["filtersReady"] }, { kind: "directive", type: i1$1.FsListColumnDirective, selector: "fs-list-column", inputs: ["show", "title", "name", "customizable", "sortable", "sortableDefault", "sortableDirection", "direction", "align", "width", "class"] }, { kind: "directive", type: i1$1.FsListCellDirective, selector: "[fs-list-cell]", inputs: ["colspan", "align", "class"] }, { kind: "directive", type: i1$1.FsListHeaderDirective, selector: "[fs-list-header]", inputs: ["colspan", "align", "class"] }, { kind: "ngmodule", type: FsBadgeModule }, { kind: "component", type: i2$3.FsBadgeComponent, selector: "fs-badge", inputs: ["color", "text", "tooltip", "size", "shape", "image", "icon", "iconSize", "iconSizePercent", "iconColor", "backgroundSize"] }, { kind: "ngmodule", type: FsDateModule }, { kind: "component", type: i4.FsDateAgoComponent, selector: "fs-date-ago", inputs: ["date", "showTime", "format", "showTooltip", "tooltipDateFormat"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1580
1608
  }
@@ -1594,6 +1622,46 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.7", ngImpor
1594
1622
  type: Output
1595
1623
  }] } });
1596
1624
 
1625
+ class SettingInstructionsComponent {
1626
+ _data = inject(MAT_DIALOG_DATA);
1627
+ setting = this._data.setting;
1628
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: SettingInstructionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1629
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.7", type: SettingInstructionsComponent, isStandalone: true, selector: "ng-component", ngImport: i0, template: "<fs-dialog>\n <fs-dialog-title [close]=\"true\">\n {{ setting.label }}\n </fs-dialog-title>\n <mat-dialog-content>\n <fs-markdown-renderer [markdown]=\"setting.instructions\"></fs-markdown-renderer>\n </mat-dialog-content>\n</fs-dialog>\n", styles: ["fs-markdown-renderer{display:block;max-width:700px}:host ::ng-deep fs-markdown-renderer .markdown-renderer-container{overflow-x:auto;overflow-y:hidden}\n"], dependencies: [{ kind: "ngmodule", type: FsDialogModule }, { kind: "component", type: i2.FsDialogComponent, selector: "fs-dialog", inputs: ["mobileMode", "mobileButtonPlacement", "mobileWidth", "mode", "buttonLayout"] }, { kind: "component", type: i2.FsDialogTitleComponent, selector: "fs-dialog-title", inputs: ["close", "dockToggle", "back"] }, { kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "component", type: FsMarkdownRendererComponent, selector: "fs-markdown-renderer", inputs: ["config", "markdown"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1630
+ }
1631
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: SettingInstructionsComponent, decorators: [{
1632
+ type: Component,
1633
+ args: [{ changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [
1634
+ FsDialogModule,
1635
+ CdkScrollable,
1636
+ MatDialogContent,
1637
+ FsMarkdownRendererComponent,
1638
+ ], template: "<fs-dialog>\n <fs-dialog-title [close]=\"true\">\n {{ setting.label }}\n </fs-dialog-title>\n <mat-dialog-content>\n <fs-markdown-renderer [markdown]=\"setting.instructions\"></fs-markdown-renderer>\n </mat-dialog-content>\n</fs-dialog>\n", styles: ["fs-markdown-renderer{display:block;max-width:700px}:host ::ng-deep fs-markdown-renderer .markdown-renderer-container{overflow-x:auto;overflow-y:hidden}\n"] }]
1639
+ }] });
1640
+
1641
+ class SettingData {
1642
+ _api = inject(FsApi);
1643
+ gets(data = {}, config = {}) {
1644
+ return this._api.get('system/settings', data, { key: 'settings', ...config });
1645
+ }
1646
+ bulk(data = {}, config = {}) {
1647
+ return this._api.post('system/settings/bulk', data, { key: 'settings', ...config });
1648
+ }
1649
+ fileUpload(settingId, data = {}, config = {}) {
1650
+ return this._api.post(`system/settings/${settingId}/files`, data, { key: 'setting', ...config });
1651
+ }
1652
+ fileDelete(settingId, data = {}, config = {}) {
1653
+ return this._api.delete(`system/settings/${settingId}/files`, data, { key: 'setting', ...config });
1654
+ }
1655
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: SettingData, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1656
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: SettingData, providedIn: 'root' });
1657
+ }
1658
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: SettingData, decorators: [{
1659
+ type: Injectable,
1660
+ args: [{
1661
+ providedIn: 'root',
1662
+ }]
1663
+ }] });
1664
+
1597
1665
  class KeyNameValuePipe {
1598
1666
  transform(values) {
1599
1667
  return Object.keys(values).map((key) => {
@@ -1662,18 +1730,23 @@ class SettingsComponent {
1662
1730
  _message = inject(FsMessage);
1663
1731
  _clipboard = inject(FsClipboard);
1664
1732
  _prompt = inject(FsPrompt);
1733
+ _dialog = inject(MatDialog);
1734
+ _settingData = inject(SettingData);
1665
1735
  _cdRef = inject(ChangeDetectorRef);
1736
+ /**
1737
+ * Optional overrides. The settings endpoints ship with the framework at a fixed
1738
+ * path (system/settings), so when one of these is left unset the component calls
1739
+ * that endpoint itself through SettingData and a consumer has to pass nothing.
1740
+ */
1666
1741
  loadSettings;
1667
1742
  save;
1668
- fileRemove = new EventEmitter();
1669
- fileSelect = new EventEmitter();
1743
+ fileUpload;
1744
+ fileDelete;
1670
1745
  groupedSettings = [];
1671
1746
  groups = [];
1672
1747
  SettingInterfaceType = SettingInterfaceType;
1673
1748
  ngOnInit() {
1674
- this.loadSettings({
1675
- visible: true,
1676
- })
1749
+ this._loadSettings({ visible: true })
1677
1750
  .subscribe((settings) => {
1678
1751
  settings = settings
1679
1752
  .filter((setting) => {
@@ -1746,18 +1819,74 @@ class SettingsComponent {
1746
1819
  }
1747
1820
  });
1748
1821
  }
1822
+ /**
1823
+ * Opens the long-form setup steps for settings that carry them. The inline hint
1824
+ * covers what the field is; this covers work that has to happen elsewhere before
1825
+ * a value can even be obtained.
1826
+ */
1827
+ openInstructions(setting) {
1828
+ this._dialog.open(SettingInstructionsComponent, {
1829
+ // Sized by its content rather than fixed: instructions run from a couple of
1830
+ // sentences to a dozen steps, and a fixed height leaves the short ones mostly
1831
+ // empty. The caps stop a long one from filling the screen edge to edge.
1832
+ maxWidth: '80vw',
1833
+ maxHeight: '80vh',
1834
+ data: { setting },
1835
+ });
1836
+ }
1749
1837
  saveGroup(group) {
1750
1838
  const settings = this.groupedSettings[group];
1751
1839
  const values = settings.reduce((result, value) => {
1752
1840
  return (result[value.name] = value.value, result);
1753
1841
  }, {});
1754
- this.save(group, values)
1842
+ this._save(group, values)
1755
1843
  .subscribe(() => {
1756
1844
  this._message.success('Changes Saved');
1757
1845
  });
1758
1846
  }
1847
+ /**
1848
+ * The X on a file setting. Persisted on its own because the bulk save deliberately
1849
+ * skips file settings — they have their own endpoint, and without this the field
1850
+ * only cleared on screen while the file stayed.
1851
+ */
1852
+ removeFile(setting) {
1853
+ this._fileDelete(setting)
1854
+ .subscribe((updated) => {
1855
+ setting.value = updated?.value ?? '';
1856
+ this._message.success('Changes Saved');
1857
+ this._cdRef.markForCheck();
1858
+ });
1859
+ }
1860
+ selectFile(setting, file) {
1861
+ this._fileUpload(setting, file)
1862
+ .subscribe((updated) => {
1863
+ setting.value = updated?.value ?? setting.value;
1864
+ this._message.success('Changes Saved');
1865
+ this._cdRef.markForCheck();
1866
+ });
1867
+ }
1868
+ _loadSettings(query) {
1869
+ return this.loadSettings
1870
+ ? this.loadSettings(query)
1871
+ : this._settingData.gets(query);
1872
+ }
1873
+ _save(group, values) {
1874
+ return this.save
1875
+ ? this.save(group, values)
1876
+ : this._settingData.bulk({ group, values });
1877
+ }
1878
+ _fileUpload(setting, file) {
1879
+ return this.fileUpload
1880
+ ? this.fileUpload(setting, file)
1881
+ : this._settingData.fileUpload(setting.id, { file });
1882
+ }
1883
+ _fileDelete(setting) {
1884
+ return this.fileDelete
1885
+ ? this.fileDelete(setting)
1886
+ : this._settingData.fileDelete(setting.id);
1887
+ }
1759
1888
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: SettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1760
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.7", type: SettingsComponent, isStandalone: true, selector: "fs-system-settings", inputs: { loadSettings: "loadSettings", save: "save" }, outputs: { fileRemove: "fileRemove", fileSelect: "fileSelect" }, ngImport: i0, template: "<mat-accordion>\n @for (group of groups; track group) {\n <mat-expansion-panel\n hideToggle\n >\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"title-row\">\n <div class=\"title\">\n {{ group || 'General' }}\n </div>\n <div class=\"actions\">\n <button\n type=\"button\"\n [matTooltip]=\"'Export ' + (group || 'General') + ' Settings'\"\n mat-icon-button\n (click)=\"export($event, groupedSettings[group])\">\n <mat-icon>\n cloud_download\n </mat-icon>\n </button>\n <button\n type=\"button\"\n [matTooltip]=\"'Import ' + (group || 'General') + ' Settings'\"\n mat-icon-button\n (click)=\"import($event, group)\">\n <mat-icon>\n cloud_upload\n </mat-icon>\n </button>\n </div>\n </div>\n </mat-panel-title>\n </mat-expansion-panel-header>\n @for (setting of groupedSettings[group]; track setting) {\n <div\n hideToggle\n class=\"setting\"\n [ngClass]=\"{ 'has-instruction': !! setting.instruction }\">\n @if (setting.interfaceType===SettingInterfaceType.InputBox) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <input\n matInput\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Textarea) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <textarea\n matInput\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n </textarea>\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Select || setting.interfaceType===SettingInterfaceType.SelectMultiple) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <mat-select\n [(ngModel)]=\"setting.value\"\n [multiple]=\"setting.interfaceType===SettingInterfaceType.SelectMultiple\"\n [name]=\"'setting' + setting.id\">\n @for (item of setting.values | keyNameValue; track item) {\n <mat-option\n [value]=\"item.value\">\n {{ item.name }}\n </mat-option>\n }\n </mat-select>\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Date) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <input\n matInput\n fsDatePicker\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Color) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <input\n matInput\n fsColorPicker\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Time) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <input\n matInput\n fsTimePicker\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.File) {\n <fs-file-picker\n [allowRemove]=\"true\"\n [allowDownload]=\"true\"\n (select)=\"fileSelect.emit({ setting: setting, file: $event.file })\"\n (remove)=\"fileRemove.emit(setting)\"\n [url]=\"setting.value\">\n <div fsFileLabel>\n {{ setting.label }}\n </div>\n <div\n fsFileHint\n [innerHTML]=\"setting.instruction | linkify\">\n </div>\n </fs-file-picker>\n }\n </div>\n }\n <button\n mat-stroked-button\n color=\"primary\"\n type=\"button\"\n (click)=\"saveGroup(group)\">\n Save\n </button>\n </mat-expansion-panel>\n }\n</mat-accordion>", styles: [".mat-expansion-panel-header{font-size:19px}.full-width{width:100%}.title-row{flex-direction:row;box-sizing:border-box;display:flex;place-content:center flex-start;align-items:center;width:100%}.title-row .title{display:flex;flex:1}:host ::ng-deep .mat-expansion-panel-header-title{margin:0}:host ::ng-deep .mat-expansion-panel .actions{display:none}:host ::ng-deep .mat-expansion-panel.mat-expanded .actions{display:block}:host ::ng-deep .setting.has-instruction fs-file-picker fs-label-message{display:block;padding-bottom:1em}\n"], dependencies: [{ kind: "directive", type: MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "component", type: MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: FsDatePickerModule }, { kind: "component", type: i2$4.FsDatePickerComponent, selector: "[fsDatePicker]", inputs: ["minYear", "maxYear", "minDate", "maxDate", "startOfDay", "view", "format", "minutes"], outputs: ["change"] }, { kind: "component", type: i2$4.FsTimePickerComponent, selector: "[fsTimePicker]", inputs: ["view"] }, { kind: "ngmodule", type: FsColorPickerModule }, { kind: "component", type: i3$3.FsColorPickerComponent, selector: "[fsColorPicker]", inputs: ["showClear"] }, { kind: "ngmodule", type: FsFileModule }, { kind: "component", type: i4$1.FsFilePickerComponent, selector: "fs-file-picker", inputs: ["minWidth", "minHeight", "imageWidth", "imageHeight", "imageQuality", "label", "showFilename", "allowDownload", "allowReupload", "multiple", "allowRemove", "url", "file", "accept", "disabled", "previewWidth", "previewHeight"], outputs: ["select", "remove", "download"] }, { kind: "directive", type: i4$1.FsFileLabelDirective, selector: "[fsFileLabel]" }, { kind: "component", type: MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "pipe", type: KeyNameValuePipe, name: "keyNameValue" }, { kind: "pipe", type: LinkifyPipe, name: "linkify" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1889
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.7", type: SettingsComponent, isStandalone: true, selector: "fs-system-settings", inputs: { loadSettings: "loadSettings", save: "save", fileUpload: "fileUpload", fileDelete: "fileDelete" }, ngImport: i0, template: "<mat-accordion>\n @for (group of groups; track group) {\n <mat-expansion-panel\n hideToggle\n >\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"title-row\">\n <div class=\"title\">\n {{ group || 'General' }}\n </div>\n <div class=\"actions\">\n <button\n type=\"button\"\n [matTooltip]=\"'Export ' + (group || 'General') + ' Settings'\"\n mat-icon-button\n (click)=\"export($event, groupedSettings[group])\">\n <mat-icon>\n cloud_download\n </mat-icon>\n </button>\n <button\n type=\"button\"\n [matTooltip]=\"'Import ' + (group || 'General') + ' Settings'\"\n mat-icon-button\n (click)=\"import($event, group)\">\n <mat-icon>\n cloud_upload\n </mat-icon>\n </button>\n </div>\n </div>\n </mat-panel-title>\n </mat-expansion-panel-header>\n @for (setting of groupedSettings[group]; track setting) {\n <div\n hideToggle\n class=\"setting\"\n [ngClass]=\"{ 'has-instruction': !! setting.instruction }\">\n @if (setting.interfaceType===SettingInterfaceType.InputBox) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <input\n matInput\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n @if (setting.instructions) {\n <button\n type=\"button\"\n matSuffix\n mat-icon-button\n matTooltip=\"Setup instructions\"\n (click)=\"openInstructions(setting)\">\n <mat-icon>\n help_outline\n </mat-icon>\n </button>\n }\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Textarea) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <textarea\n matInput\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n </textarea>\n @if (setting.instructions) {\n <button\n type=\"button\"\n matSuffix\n mat-icon-button\n matTooltip=\"Setup instructions\"\n (click)=\"openInstructions(setting)\">\n <mat-icon>\n help_outline\n </mat-icon>\n </button>\n }\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Select || setting.interfaceType===SettingInterfaceType.SelectMultiple) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <mat-select\n [(ngModel)]=\"setting.value\"\n [multiple]=\"setting.interfaceType===SettingInterfaceType.SelectMultiple\"\n [name]=\"'setting' + setting.id\">\n @for (item of setting.values | keyNameValue; track item) {\n <mat-option\n [value]=\"item.value\">\n {{ item.name }}\n </mat-option>\n }\n </mat-select>\n @if (setting.instructions) {\n <button\n type=\"button\"\n matSuffix\n mat-icon-button\n matTooltip=\"Setup instructions\"\n (click)=\"openInstructions(setting)\">\n <mat-icon>\n help_outline\n </mat-icon>\n </button>\n }\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Date) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <input\n matInput\n fsDatePicker\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n @if (setting.instructions) {\n <button\n type=\"button\"\n matSuffix\n mat-icon-button\n matTooltip=\"Setup instructions\"\n (click)=\"openInstructions(setting)\">\n <mat-icon>\n help_outline\n </mat-icon>\n </button>\n }\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Color) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <input\n matInput\n fsColorPicker\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n @if (setting.instructions) {\n <button\n type=\"button\"\n matSuffix\n mat-icon-button\n matTooltip=\"Setup instructions\"\n (click)=\"openInstructions(setting)\">\n <mat-icon>\n help_outline\n </mat-icon>\n </button>\n }\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Time) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <input\n matInput\n fsTimePicker\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n @if (setting.instructions) {\n <button\n type=\"button\"\n matSuffix\n mat-icon-button\n matTooltip=\"Setup instructions\"\n (click)=\"openInstructions(setting)\">\n <mat-icon>\n help_outline\n </mat-icon>\n </button>\n }\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.File) {\n <fs-file-picker\n [allowRemove]=\"true\"\n [allowDownload]=\"true\"\n (select)=\"selectFile(setting, $event.file)\"\n (remove)=\"removeFile(setting)\"\n [url]=\"setting.value\">\n <div fsFileLabel>\n {{ setting.label }}\n @if (setting.instructions) {\n <button\n type=\"button\"\n mat-icon-button\n matTooltip=\"Setup instructions\"\n (click)=\"openInstructions(setting)\">\n <mat-icon>\n help_outline\n </mat-icon>\n </button>\n }\n </div>\n <div\n fsFileHint\n [innerHTML]=\"setting.instruction | linkify\">\n </div>\n </fs-file-picker>\n }\n </div>\n }\n <button\n mat-stroked-button\n color=\"primary\"\n type=\"button\"\n (click)=\"saveGroup(group)\">\n Save\n </button>\n </mat-expansion-panel>\n }\n</mat-accordion>", styles: [".mat-expansion-panel-header{font-size:19px}.full-width{width:100%}.title-row{flex-direction:row;box-sizing:border-box;display:flex;place-content:center flex-start;align-items:center;width:100%}.title-row .title{display:flex;flex:1}:host ::ng-deep .mat-expansion-panel-header-title{margin:0}:host ::ng-deep .mat-expansion-panel .actions{display:none}:host ::ng-deep .mat-expansion-panel.mat-expanded .actions{display:block}:host ::ng-deep .setting.has-instruction fs-file-picker fs-label-message{display:block;padding-bottom:1em}\n"], dependencies: [{ kind: "directive", type: MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "component", type: MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "directive", type: MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "component", type: MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: FsDatePickerModule }, { kind: "component", type: i2$4.FsDatePickerComponent, selector: "[fsDatePicker]", inputs: ["minYear", "maxYear", "minDate", "maxDate", "startOfDay", "view", "format", "minutes"], outputs: ["change"] }, { kind: "component", type: i2$4.FsTimePickerComponent, selector: "[fsTimePicker]", inputs: ["view"] }, { kind: "ngmodule", type: FsColorPickerModule }, { kind: "component", type: i3$3.FsColorPickerComponent, selector: "[fsColorPicker]", inputs: ["showClear"] }, { kind: "ngmodule", type: FsFileModule }, { kind: "component", type: i4$1.FsFilePickerComponent, selector: "fs-file-picker", inputs: ["minWidth", "minHeight", "imageWidth", "imageHeight", "imageQuality", "label", "showFilename", "allowDownload", "allowReupload", "multiple", "allowRemove", "url", "file", "accept", "disabled", "previewWidth", "previewHeight"], outputs: ["select", "remove", "download"] }, { kind: "directive", type: i4$1.FsFileLabelDirective, selector: "[fsFileLabel]" }, { kind: "component", type: MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "pipe", type: KeyNameValuePipe, name: "keyNameValue" }, { kind: "pipe", type: LinkifyPipe, name: "linkify" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1761
1890
  }
1762
1891
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: SettingsComponent, decorators: [{
1763
1892
  type: Component,
@@ -1775,6 +1904,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.7", ngImpor
1775
1904
  MatInput,
1776
1905
  FormsModule,
1777
1906
  MatHint,
1907
+ MatSuffix,
1778
1908
  MatSelect,
1779
1909
  MatOption,
1780
1910
  FsDatePickerModule,
@@ -1783,19 +1913,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.7", ngImpor
1783
1913
  MatButton,
1784
1914
  KeyNameValuePipe,
1785
1915
  LinkifyPipe,
1786
- ], template: "<mat-accordion>\n @for (group of groups; track group) {\n <mat-expansion-panel\n hideToggle\n >\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"title-row\">\n <div class=\"title\">\n {{ group || 'General' }}\n </div>\n <div class=\"actions\">\n <button\n type=\"button\"\n [matTooltip]=\"'Export ' + (group || 'General') + ' Settings'\"\n mat-icon-button\n (click)=\"export($event, groupedSettings[group])\">\n <mat-icon>\n cloud_download\n </mat-icon>\n </button>\n <button\n type=\"button\"\n [matTooltip]=\"'Import ' + (group || 'General') + ' Settings'\"\n mat-icon-button\n (click)=\"import($event, group)\">\n <mat-icon>\n cloud_upload\n </mat-icon>\n </button>\n </div>\n </div>\n </mat-panel-title>\n </mat-expansion-panel-header>\n @for (setting of groupedSettings[group]; track setting) {\n <div\n hideToggle\n class=\"setting\"\n [ngClass]=\"{ 'has-instruction': !! setting.instruction }\">\n @if (setting.interfaceType===SettingInterfaceType.InputBox) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <input\n matInput\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Textarea) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <textarea\n matInput\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n </textarea>\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Select || setting.interfaceType===SettingInterfaceType.SelectMultiple) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <mat-select\n [(ngModel)]=\"setting.value\"\n [multiple]=\"setting.interfaceType===SettingInterfaceType.SelectMultiple\"\n [name]=\"'setting' + setting.id\">\n @for (item of setting.values | keyNameValue; track item) {\n <mat-option\n [value]=\"item.value\">\n {{ item.name }}\n </mat-option>\n }\n </mat-select>\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Date) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <input\n matInput\n fsDatePicker\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Color) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <input\n matInput\n fsColorPicker\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Time) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <input\n matInput\n fsTimePicker\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.File) {\n <fs-file-picker\n [allowRemove]=\"true\"\n [allowDownload]=\"true\"\n (select)=\"fileSelect.emit({ setting: setting, file: $event.file })\"\n (remove)=\"fileRemove.emit(setting)\"\n [url]=\"setting.value\">\n <div fsFileLabel>\n {{ setting.label }}\n </div>\n <div\n fsFileHint\n [innerHTML]=\"setting.instruction | linkify\">\n </div>\n </fs-file-picker>\n }\n </div>\n }\n <button\n mat-stroked-button\n color=\"primary\"\n type=\"button\"\n (click)=\"saveGroup(group)\">\n Save\n </button>\n </mat-expansion-panel>\n }\n</mat-accordion>", styles: [".mat-expansion-panel-header{font-size:19px}.full-width{width:100%}.title-row{flex-direction:row;box-sizing:border-box;display:flex;place-content:center flex-start;align-items:center;width:100%}.title-row .title{display:flex;flex:1}:host ::ng-deep .mat-expansion-panel-header-title{margin:0}:host ::ng-deep .mat-expansion-panel .actions{display:none}:host ::ng-deep .mat-expansion-panel.mat-expanded .actions{display:block}:host ::ng-deep .setting.has-instruction fs-file-picker fs-label-message{display:block;padding-bottom:1em}\n"] }]
1916
+ ], template: "<mat-accordion>\n @for (group of groups; track group) {\n <mat-expansion-panel\n hideToggle\n >\n <mat-expansion-panel-header>\n <mat-panel-title>\n <div class=\"title-row\">\n <div class=\"title\">\n {{ group || 'General' }}\n </div>\n <div class=\"actions\">\n <button\n type=\"button\"\n [matTooltip]=\"'Export ' + (group || 'General') + ' Settings'\"\n mat-icon-button\n (click)=\"export($event, groupedSettings[group])\">\n <mat-icon>\n cloud_download\n </mat-icon>\n </button>\n <button\n type=\"button\"\n [matTooltip]=\"'Import ' + (group || 'General') + ' Settings'\"\n mat-icon-button\n (click)=\"import($event, group)\">\n <mat-icon>\n cloud_upload\n </mat-icon>\n </button>\n </div>\n </div>\n </mat-panel-title>\n </mat-expansion-panel-header>\n @for (setting of groupedSettings[group]; track setting) {\n <div\n hideToggle\n class=\"setting\"\n [ngClass]=\"{ 'has-instruction': !! setting.instruction }\">\n @if (setting.interfaceType===SettingInterfaceType.InputBox) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <input\n matInput\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n @if (setting.instructions) {\n <button\n type=\"button\"\n matSuffix\n mat-icon-button\n matTooltip=\"Setup instructions\"\n (click)=\"openInstructions(setting)\">\n <mat-icon>\n help_outline\n </mat-icon>\n </button>\n }\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Textarea) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <textarea\n matInput\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n </textarea>\n @if (setting.instructions) {\n <button\n type=\"button\"\n matSuffix\n mat-icon-button\n matTooltip=\"Setup instructions\"\n (click)=\"openInstructions(setting)\">\n <mat-icon>\n help_outline\n </mat-icon>\n </button>\n }\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Select || setting.interfaceType===SettingInterfaceType.SelectMultiple) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <mat-select\n [(ngModel)]=\"setting.value\"\n [multiple]=\"setting.interfaceType===SettingInterfaceType.SelectMultiple\"\n [name]=\"'setting' + setting.id\">\n @for (item of setting.values | keyNameValue; track item) {\n <mat-option\n [value]=\"item.value\">\n {{ item.name }}\n </mat-option>\n }\n </mat-select>\n @if (setting.instructions) {\n <button\n type=\"button\"\n matSuffix\n mat-icon-button\n matTooltip=\"Setup instructions\"\n (click)=\"openInstructions(setting)\">\n <mat-icon>\n help_outline\n </mat-icon>\n </button>\n }\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Date) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <input\n matInput\n fsDatePicker\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n @if (setting.instructions) {\n <button\n type=\"button\"\n matSuffix\n mat-icon-button\n matTooltip=\"Setup instructions\"\n (click)=\"openInstructions(setting)\">\n <mat-icon>\n help_outline\n </mat-icon>\n </button>\n }\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Color) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <input\n matInput\n fsColorPicker\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n @if (setting.instructions) {\n <button\n type=\"button\"\n matSuffix\n mat-icon-button\n matTooltip=\"Setup instructions\"\n (click)=\"openInstructions(setting)\">\n <mat-icon>\n help_outline\n </mat-icon>\n </button>\n }\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.Time) {\n <mat-form-field\n class=\"full-width form-field-multiline-subscript\"\n >\n <mat-label>\n {{ setting.label }}\n </mat-label>\n <input\n matInput\n fsTimePicker\n [(ngModel)]=\"setting.value\"\n [name]=\"'setting' + setting.id\">\n @if (setting.instructions) {\n <button\n type=\"button\"\n matSuffix\n mat-icon-button\n matTooltip=\"Setup instructions\"\n (click)=\"openInstructions(setting)\">\n <mat-icon>\n help_outline\n </mat-icon>\n </button>\n }\n <mat-hint\n [innerHTML]=\"setting.instruction | linkify\">\n </mat-hint>\n </mat-form-field>\n }\n @if (setting.interfaceType===SettingInterfaceType.File) {\n <fs-file-picker\n [allowRemove]=\"true\"\n [allowDownload]=\"true\"\n (select)=\"selectFile(setting, $event.file)\"\n (remove)=\"removeFile(setting)\"\n [url]=\"setting.value\">\n <div fsFileLabel>\n {{ setting.label }}\n @if (setting.instructions) {\n <button\n type=\"button\"\n mat-icon-button\n matTooltip=\"Setup instructions\"\n (click)=\"openInstructions(setting)\">\n <mat-icon>\n help_outline\n </mat-icon>\n </button>\n }\n </div>\n <div\n fsFileHint\n [innerHTML]=\"setting.instruction | linkify\">\n </div>\n </fs-file-picker>\n }\n </div>\n }\n <button\n mat-stroked-button\n color=\"primary\"\n type=\"button\"\n (click)=\"saveGroup(group)\">\n Save\n </button>\n </mat-expansion-panel>\n }\n</mat-accordion>", styles: [".mat-expansion-panel-header{font-size:19px}.full-width{width:100%}.title-row{flex-direction:row;box-sizing:border-box;display:flex;place-content:center flex-start;align-items:center;width:100%}.title-row .title{display:flex;flex:1}:host ::ng-deep .mat-expansion-panel-header-title{margin:0}:host ::ng-deep .mat-expansion-panel .actions{display:none}:host ::ng-deep .mat-expansion-panel.mat-expanded .actions{display:block}:host ::ng-deep .setting.has-instruction fs-file-picker fs-label-message{display:block;padding-bottom:1em}\n"] }]
1787
1917
  }], propDecorators: { loadSettings: [{
1788
1918
  type: Input
1789
1919
  }], save: [{
1790
1920
  type: Input
1791
- }], fileRemove: [{
1792
- type: Output
1793
- }], fileSelect: [{
1794
- type: Output
1921
+ }], fileUpload: [{
1922
+ type: Input
1923
+ }], fileDelete: [{
1924
+ type: Input
1795
1925
  }] } });
1796
1926
 
1797
1927
  class UpgradeLogsComponent {
1928
+ _api = inject(FsApi);
1798
1929
  list;
1930
+ /**
1931
+ * Optional override. Left unset, the component reads the framework's own
1932
+ * system/logs/upgrade endpoint.
1933
+ */
1799
1934
  loadUpgradeLogs;
1800
1935
  config = null;
1801
1936
  logTypes = [];
@@ -1813,11 +1948,16 @@ class UpgradeLogsComponent {
1813
1948
  },
1814
1949
  ],
1815
1950
  fetch: (query) => {
1816
- return this.loadUpgradeLogs(query)
1817
- .pipe(map((response) => ({ data: response.data, paging: response.paging })));
1951
+ return this._upgradeLogsGet(query);
1818
1952
  },
1819
1953
  };
1820
1954
  }
1955
+ _upgradeLogsGet(query) {
1956
+ return this.loadUpgradeLogs
1957
+ ? this.loadUpgradeLogs(query)
1958
+ : this._api.get('system/logs/upgrade', query, { key: null })
1959
+ .pipe(map((response) => ({ data: response.upgrades, paging: response.paging })));
1960
+ }
1821
1961
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: UpgradeLogsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1822
1962
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.7", type: UpgradeLogsComponent, isStandalone: true, selector: "fs-system-upgrade-logs", inputs: { loadUpgradeLogs: "loadUpgradeLogs" }, viewQueries: [{ propertyName: "list", first: true, predicate: FsListComponent, descendants: true, static: true }], ngImport: i0, template: "<fs-list [config]=\"config\">\r\n <fs-list-column>\r\n <ng-template fs-list-header>Name</ng-template>\r\n <ng-template fs-list-cell let-row=\"row\">\r\n {{row.name}}\r\n </ng-template>\r\n </fs-list-column>\r\n\r\n <fs-list-column>\r\n <ng-template fs-list-header>Function</ng-template>\r\n <ng-template fs-list-cell let-row=\"row\">\r\n {{row.function}}()\r\n </ng-template>\r\n </fs-list-column>\r\n\r\n <fs-list-column>\r\n <ng-template fs-list-header>Date</ng-template>\r\n <ng-template fs-list-cell let-row=\"row\">\r\n <fs-date-ago [date]=\"row.createDate\"></fs-date-ago>\r\n </ng-template>\r\n </fs-list-column>\r\n</fs-list>\r\n", styles: [""], dependencies: [{ kind: "ngmodule", type: FsListModule }, { kind: "component", type: i1$1.FsListComponent, selector: "fs-list", inputs: ["config", "loaderLines"], outputs: ["filtersReady"] }, { kind: "directive", type: i1$1.FsListColumnDirective, selector: "fs-list-column", inputs: ["show", "title", "name", "customizable", "sortable", "sortableDefault", "sortableDirection", "direction", "align", "width", "class"] }, { kind: "directive", type: i1$1.FsListCellDirective, selector: "[fs-list-cell]", inputs: ["colspan", "align", "class"] }, { kind: "directive", type: i1$1.FsListHeaderDirective, selector: "[fs-list-header]", inputs: ["colspan", "align", "class"] }, { kind: "ngmodule", type: FsDateModule }, { kind: "component", type: i4.FsDateAgoComponent, selector: "fs-date-ago", inputs: ["date", "showTime", "format", "showTooltip", "tooltipDateFormat"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1823
1963
  }
@@ -1881,7 +2021,8 @@ class FsSystemModule {
1881
2021
  InfoComponent,
1882
2022
  ProcessComponent,
1883
2023
  CronComponent,
1884
- CronLogComponent], exports: [DashboardComponent,
2024
+ CronLogComponent,
2025
+ SettingInstructionsComponent], exports: [DashboardComponent,
1885
2026
  CronsComponent,
1886
2027
  SettingsComponent,
1887
2028
  ProcessesComponent,
@@ -1925,7 +2066,8 @@ class FsSystemModule {
1925
2066
  ApiLogComponent,
1926
2067
  ProcessComponent,
1927
2068
  CronComponent,
1928
- CronLogComponent] });
2069
+ CronLogComponent,
2070
+ SettingInstructionsComponent] });
1929
2071
  }
1930
2072
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.7", ngImport: i0, type: FsSystemModule, decorators: [{
1931
2073
  type: NgModule,
@@ -1970,6 +2112,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.7", ngImpor
1970
2112
  ProcessComponent,
1971
2113
  CronComponent,
1972
2114
  CronLogComponent,
2115
+ SettingInstructionsComponent,
1973
2116
  ],
1974
2117
  exports: [
1975
2118
  DashboardComponent,
@@ -2152,5 +2295,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.7", ngImpor
2152
2295
  * Generated bundle index. Do not edit.
2153
2296
  */
2154
2297
 
2155
- export { ApiLogComponent, ApiLogState, ApiLogStates, ApiLogsComponent, CronComponent, CronLogComponent, CronState, CronStates, CronsComponent, DashboardComponent, FS_SYSTEM_CONFIG, FsSystemModule, InfoComponent, KeyNameValuePipe, LinkifyPipe, ProcessComponent, ProcessState, ProcessStates, ProcessesComponent, QueryLogsComponent, ServerLogsComponent, SettingsComponent, UpgradeLogsComponent };
2298
+ export { ApiLogComponent, ApiLogState, ApiLogStates, ApiLogsComponent, CronComponent, CronLogComponent, CronState, CronStates, CronsComponent, DashboardComponent, FS_SYSTEM_CONFIG, FsSystemModule, InfoComponent, KeyNameValuePipe, LinkifyPipe, ProcessComponent, ProcessState, ProcessStates, ProcessesComponent, QueryLogsComponent, ServerLogsComponent, SettingInstructionsComponent, SettingsComponent, UpgradeLogsComponent };
2156
2299
  //# sourceMappingURL=firestitch-system.mjs.map