From 18f59f78e331d9c784a9e3727939e5624f41607f Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 3 Jan 2024 23:28:32 -0600 Subject: [PATCH] feat(web): onboarding (#6066) * feat(web): onboarding * feat: openapi * feat: modulization * feat: page advancing * Animation * Add storage templaete settings * sql * more style * Theme * information and styling * hide/show table * Styling * Update user property * fix test * fix test: * fix e2e * test * Update web/src/lib/components/onboarding-page/onboarding-hello.svelte Co-authored-by: bo0tzz * naming * use System Metadata * better return type * onboarding using server metadata * revert previous changes in user entity * sql * test web * fix test server * server/web test * more test * consolidate color theme change logic * consolidate save button to storage template * merge main * fix web --------- Co-authored-by: bo0tzz --- cli/src/api/open-api/api.ts | 71 ++++++ mobile/openapi/README.md | Bin 24161 -> 24290 bytes mobile/openapi/doc/ServerConfigDto.md | Bin 563 -> 596 bytes mobile/openapi/doc/ServerInfoApi.md | Bin 9228 -> 11069 bytes mobile/openapi/lib/api/server_info_api.dart | Bin 11619 -> 12548 bytes .../openapi/lib/model/server_config_dto.dart | Bin 4102 -> 4382 bytes .../openapi/test/server_config_dto_test.dart | Bin 1022 -> 1127 bytes mobile/openapi/test/server_info_api_test.dart | Bin 1473 -> 1583 bytes server/immich-openapi-specs.json | 29 +++ .../src/domain/server-info/server-info.dto.ts | 1 + .../server-info/server-info.service.spec.ts | 23 +- .../domain/server-info/server-info.service.ts | 9 + .../controllers/server-info.controller.ts | 9 +- .../infra/entities/system-metadata.entity.ts | 2 + server/test/api/index.ts | 2 + server/test/api/server-info-api.ts | 10 + server/test/e2e/server-info.e2e-spec.ts | 16 ++ server/test/repositories/index.ts | 1 + .../system-metadata.repository.mock.ts | 8 + web/src/api/open-api/api.ts | 71 ++++++ web/src/lib/assets/settings-outline.svg | 1 + .../admin-page/settings/setting-switch.svelte | 2 +- .../storage-template-settings.svelte | 241 ++++++++++-------- .../components/elements/buttons/button.svelte | 6 +- .../lib/components/forms/login-form.svelte | 12 +- .../onboarding-page/onboarding-card.svelte | 11 + .../onboarding-page/onboarding-hello.svelte | 28 ++ .../onboarding-storage-template.svelte | 37 +++ .../onboarding-page/onboarding-theme.svelte | 60 +++++ web/src/lib/constants.ts | 1 + web/src/lib/stores/server-config.store.ts | 1 + web/src/routes/+layout.svelte | 12 + web/src/routes/auth/login/+page.svelte | 1 + web/src/routes/auth/onboarding/+page.svelte | 25 ++ web/src/routes/auth/onboarding/+page.ts | 13 + 35 files changed, 593 insertions(+), 110 deletions(-) create mode 100644 server/test/api/server-info-api.ts create mode 100644 server/test/repositories/system-metadata.repository.mock.ts create mode 100644 web/src/lib/assets/settings-outline.svg create mode 100644 web/src/lib/components/onboarding-page/onboarding-card.svelte create mode 100644 web/src/lib/components/onboarding-page/onboarding-hello.svelte create mode 100644 web/src/lib/components/onboarding-page/onboarding-storage-template.svelte create mode 100644 web/src/lib/components/onboarding-page/onboarding-theme.svelte create mode 100644 web/src/routes/auth/onboarding/+page.svelte create mode 100644 web/src/routes/auth/onboarding/+page.ts diff --git a/cli/src/api/open-api/api.ts b/cli/src/api/open-api/api.ts index 089b69c474..5d93a32b57 100644 --- a/cli/src/api/open-api/api.ts +++ b/cli/src/api/open-api/api.ts @@ -3019,6 +3019,12 @@ export interface ServerConfigDto { * @memberof ServerConfigDto */ 'isInitialized': boolean; + /** + * + * @type {boolean} + * @memberof ServerConfigDto + */ + 'isOnboarded': boolean; /** * * @type {string} @@ -15142,6 +15148,44 @@ export const ServerInfoApiAxiosParamCreator = function (configuration?: Configur + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + setAdminOnboarding: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/server-info/admin-onboarding`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication cookie required + + // authentication api_key required + await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) + + // authentication bearer required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; @@ -15233,6 +15277,15 @@ export const ServerInfoApiFp = function(configuration?: Configuration) { const localVarAxiosArgs = await localVarAxiosParamCreator.pingServer(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async setAdminOnboarding(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.setAdminOnboarding(options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, } }; @@ -15307,6 +15360,14 @@ export const ServerInfoApiFactory = function (configuration?: Configuration, bas pingServer(options?: AxiosRequestConfig): AxiosPromise { return localVarFp.pingServer(options).then((request) => request(axios, basePath)); }, + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + setAdminOnboarding(options?: AxiosRequestConfig): AxiosPromise { + return localVarFp.setAdminOnboarding(options).then((request) => request(axios, basePath)); + }, }; }; @@ -15396,6 +15457,16 @@ export class ServerInfoApi extends BaseAPI { public pingServer(options?: AxiosRequestConfig) { return ServerInfoApiFp(this.configuration).pingServer(options).then((request) => request(this.axios, this.basePath)); } + + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ServerInfoApi + */ + public setAdminOnboarding(options?: AxiosRequestConfig) { + return ServerInfoApiFp(this.configuration).setAdminOnboarding(options).then((request) => request(this.axios, this.basePath)); + } } diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 068aa8aa2d01e9d701fda6cc5e9f1cc420a47fc3..39cf9a74ad9adcf438734c2203d8d101d3ce96da 100644 GIT binary patch delta 99 zcmaF3hw;%~#tjvQlfUT-3l*o9IHu%g=K1F(8O4x3;5&X!k zkXVozpP5%&l9-pAs)wmiV{(D0lMtr(@FnPR7ap>=Wsi#j#~6%-b- J)N<8w0RXDpIMo0E delta 10 RcmZokdK|SOQJ1Nf3ji7Q1M2_) diff --git a/mobile/openapi/lib/model/server_config_dto.dart b/mobile/openapi/lib/model/server_config_dto.dart index abe5983966a6866664b7f51f0439f1a82cd3a6a8..d93da96a23d826c52ac47d9de7510aab5068d105 100644 GIT binary patch delta 190 zcmZoun5VSi5+i43v437teqvF|%JTa}?l0o5Pu;871J{)D#6;W@%*o z87%k3kks0$z&V?pSrr)N;M~*{^;iX4g_4ZSVm-Lv<{-A2Oh~TTz@f|zb=l@S9M9R1 Jq;~TA0sx4_M9%;K delta 44 zcmV+{0Mq}TB8DKa(gBn40gSVf0u%wW&jShpllBD5v)ct60kb9thyt_l3D^dc+Ym#P CQW2y8 diff --git a/mobile/openapi/test/server_config_dto_test.dart b/mobile/openapi/test/server_config_dto_test.dart index ffd373bf2f679dac752d04dba936ce6fc62bafb0..813ac25656db1f2da84e4a0a73a26a648bb24453 100644 GIT binary patch delta 43 rcmeyz{+wgOdM1IS{QMk+%wqq%r2NF9l+={TR?Lc=aQ@~7raDFda9R)R delta 16 XcmaFP@sEAOdZx*}%oZq~^T%v{0>#i=EZDY=<>{&`9HiA5=ydFhk?GD`?y5njlw&Q_~w%~i_< E01$~64gdfE delta 14 VcmZ3_bC7$(ZdSHhO>3@NE&wM_1aJTV diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json index 240a2d3810..0cf34169be 100644 --- a/server/immich-openapi-specs.json +++ b/server/immich-openapi-specs.json @@ -4725,6 +4725,31 @@ ] } }, + "/server-info/admin-onboarding": { + "post": { + "operationId": "setAdminOnboarding", + "parameters": [], + "responses": { + "204": { + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "tags": [ + "Server Info" + ] + } + }, "/server-info/config": { "get": { "operationId": "getServerConfig", @@ -8599,6 +8624,9 @@ "isInitialized": { "type": "boolean" }, + "isOnboarded": { + "type": "boolean" + }, "loginPageMessage": { "type": "string" }, @@ -8614,6 +8642,7 @@ "oauthButtonText", "loginPageMessage", "isInitialized", + "isOnboarded", "externalDomain" ], "type": "object" diff --git a/server/src/domain/server-info/server-info.dto.ts b/server/src/domain/server-info/server-info.dto.ts index 32ac51aa43..ea0d03b881 100644 --- a/server/src/domain/server-info/server-info.dto.ts +++ b/server/src/domain/server-info/server-info.dto.ts @@ -86,6 +86,7 @@ export class ServerConfigDto { @ApiProperty({ type: 'integer' }) trashDays!: number; isInitialized!: boolean; + isOnboarded!: boolean; externalDomain!: string; } diff --git a/server/src/domain/server-info/server-info.service.spec.ts b/server/src/domain/server-info/server-info.service.spec.ts index d093399c77..a8cd82443a 100644 --- a/server/src/domain/server-info/server-info.service.spec.ts +++ b/server/src/domain/server-info/server-info.service.spec.ts @@ -1,8 +1,10 @@ +import { SystemMetadataKey } from '@app/infra/entities'; import { newCommunicationRepositoryMock, newServerInfoRepositoryMock, newStorageRepositoryMock, newSystemConfigRepositoryMock, + newSystemMetadataRepositoryMock, newUserRepositoryMock, } from '@test'; import { serverVersion } from '../domain.constant'; @@ -11,6 +13,7 @@ import { IServerInfoRepository, IStorageRepository, ISystemConfigRepository, + ISystemMetadataRepository, IUserRepository, } from '../repositories'; import { ServerInfoService } from './server-info.service'; @@ -22,6 +25,7 @@ describe(ServerInfoService.name, () => { let serverInfoMock: jest.Mocked; let storageMock: jest.Mocked; let userMock: jest.Mocked; + let systemMetadataMock: jest.Mocked; beforeEach(() => { configMock = newSystemConfigRepositoryMock(); @@ -29,8 +33,16 @@ describe(ServerInfoService.name, () => { serverInfoMock = newServerInfoRepositoryMock(); storageMock = newStorageRepositoryMock(); userMock = newUserRepositoryMock(); + systemMetadataMock = newSystemMetadataRepositoryMock(); - sut = new ServerInfoService(communicationMock, configMock, userMock, serverInfoMock, storageMock); + sut = new ServerInfoService( + communicationMock, + configMock, + userMock, + serverInfoMock, + storageMock, + systemMetadataMock, + ); }); it('should work', () => { @@ -184,12 +196,21 @@ describe(ServerInfoService.name, () => { loginPageMessage: '', oauthButtonText: 'Login with OAuth', trashDays: 30, + isInitialized: undefined, + isOnboarded: false, externalDomain: '', }); expect(configMock.load).toHaveBeenCalled(); }); }); + describe('setAdminOnboarding', () => { + it('should set admin onboarding to true', async () => { + await sut.setAdminOnboarding(); + expect(systemMetadataMock.set).toHaveBeenCalledWith(SystemMetadataKey.ADMIN_ONBOARDING, { isOnboarded: true }); + }); + }); + describe('getStats', () => { it('should total up usage by user', async () => { userMock.getUserStats.mockResolvedValue([ diff --git a/server/src/domain/server-info/server-info.service.ts b/server/src/domain/server-info/server-info.service.ts index 7c3f707843..697e461de5 100644 --- a/server/src/domain/server-info/server-info.service.ts +++ b/server/src/domain/server-info/server-info.service.ts @@ -1,3 +1,4 @@ +import { SystemMetadataKey } from '@app/infra/entities'; import { ImmichLogger } from '@app/infra/logger'; import { Inject, Injectable } from '@nestjs/common'; import { DateTime } from 'luxon'; @@ -9,6 +10,7 @@ import { IServerInfoRepository, IStorageRepository, ISystemConfigRepository, + ISystemMetadataRepository, IUserRepository, UserStatsQueryResponse, } from '../repositories'; @@ -37,6 +39,7 @@ export class ServerInfoService { @Inject(IUserRepository) private userRepository: IUserRepository, @Inject(IServerInfoRepository) private repository: IServerInfoRepository, @Inject(IStorageRepository) private storageRepository: IStorageRepository, + @Inject(ISystemMetadataRepository) private readonly systemMetadataRepository: ISystemMetadataRepository, ) { this.configCore = SystemConfigCore.create(configRepository); this.communicationRepository.on('connect', (userId) => this.handleConnect(userId)); @@ -79,16 +82,22 @@ export class ServerInfoService { async getConfig(): Promise { const config = await this.configCore.getConfig(); const isInitialized = await this.userRepository.hasAdmin(); + const onboarding = await this.systemMetadataRepository.get(SystemMetadataKey.ADMIN_ONBOARDING); return { loginPageMessage: config.server.loginPageMessage, trashDays: config.trash.days, oauthButtonText: config.oauth.buttonText, isInitialized, + isOnboarded: onboarding?.isOnboarded || false, externalDomain: config.server.externalDomain, }; } + setAdminOnboarding(): Promise { + return this.systemMetadataRepository.set(SystemMetadataKey.ADMIN_ONBOARDING, { isOnboarded: true }); + } + async getStatistics(): Promise { const userStats: UserStatsQueryResponse[] = await this.userRepository.getUserStats(); const serverStats = new ServerStatsResponseDto(); diff --git a/server/src/immich/controllers/server-info.controller.ts b/server/src/immich/controllers/server-info.controller.ts index 52e9ea8d26..66835501cc 100644 --- a/server/src/immich/controllers/server-info.controller.ts +++ b/server/src/immich/controllers/server-info.controller.ts @@ -9,7 +9,7 @@ import { ServerThemeDto, ServerVersionResponseDto, } from '@app/domain'; -import { Controller, Get } from '@nestjs/common'; +import { Controller, Get, HttpCode, HttpStatus, Post } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { AdminRoute, Authenticated, PublicRoute } from '../app.guard'; import { UseValidation } from '../app.utils'; @@ -67,4 +67,11 @@ export class ServerInfoController { getSupportedMediaTypes(): ServerMediaTypesResponseDto { return this.service.getSupportedMediaTypes(); } + + @AdminRoute() + @Post('admin-onboarding') + @HttpCode(HttpStatus.NO_CONTENT) + setAdminOnboarding(): Promise { + return this.service.setAdminOnboarding(); + } } diff --git a/server/src/infra/entities/system-metadata.entity.ts b/server/src/infra/entities/system-metadata.entity.ts index 623806db79..24e9f83c74 100644 --- a/server/src/infra/entities/system-metadata.entity.ts +++ b/server/src/infra/entities/system-metadata.entity.ts @@ -11,8 +11,10 @@ export class SystemMetadataEntity { export enum SystemMetadataKey { REVERSE_GEOCODING_STATE = 'reverse-geocoding-state', + ADMIN_ONBOARDING = 'admin-onboarding', } export interface SystemMetadata extends Record { [SystemMetadataKey.REVERSE_GEOCODING_STATE]: { lastUpdate?: string; lastImportFileName?: string }; + [SystemMetadataKey.ADMIN_ONBOARDING]: { isOnboarded: boolean }; } diff --git a/server/test/api/index.ts b/server/test/api/index.ts index 21987c5004..d13f2425e8 100644 --- a/server/test/api/index.ts +++ b/server/test/api/index.ts @@ -5,6 +5,7 @@ import { assetApi } from './asset-api'; import { authApi } from './auth-api'; import { libraryApi } from './library-api'; import { partnerApi } from './partner-api'; +import { serverInfoApi } from './server-info-api'; import { sharedLinkApi } from './shared-link-api'; import { userApi } from './user-api'; @@ -14,6 +15,7 @@ export const api = { apiKeyApi, assetApi, libraryApi, + serverInfoApi, sharedLinkApi, albumApi, userApi, diff --git a/server/test/api/server-info-api.ts b/server/test/api/server-info-api.ts new file mode 100644 index 0000000000..f885bc856f --- /dev/null +++ b/server/test/api/server-info-api.ts @@ -0,0 +1,10 @@ +import { ServerConfigDto } from '@app/domain'; +import request from 'supertest'; + +export const serverInfoApi = { + getConfig: async (server: any) => { + const res = await request(server).get('/server-info/config'); + expect(res.status).toBe(200); + return res.body as ServerConfigDto; + }, +}; diff --git a/server/test/e2e/server-info.e2e-spec.ts b/server/test/e2e/server-info.e2e-spec.ts index d2d54d0790..fb0ad90c89 100644 --- a/server/test/e2e/server-info.e2e-spec.ts +++ b/server/test/e2e/server-info.e2e-spec.ts @@ -98,6 +98,7 @@ describe(`${ServerInfoController.name} (e2e)`, () => { trashDays: 30, isInitialized: true, externalDomain: '', + isOnboarded: false, }); }); }); @@ -167,4 +168,19 @@ describe(`${ServerInfoController.name} (e2e)`, () => { }); }); }); + + describe('POST /server-info/admin-onboarding', () => { + it('should set admin onboarding', async () => { + const config = await api.serverInfoApi.getConfig(server); + expect(config.isOnboarded).toBe(false); + + const { status } = await request(server) + .post('/server-info/admin-onboarding') + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(204); + + const newConfig = await api.serverInfoApi.getConfig(server); + expect(newConfig.isOnboarded).toBe(true); + }); + }); }); diff --git a/server/test/repositories/index.ts b/server/test/repositories/index.ts index f625dc5213..d7a7f3e0c4 100644 --- a/server/test/repositories/index.ts +++ b/server/test/repositories/index.ts @@ -19,6 +19,7 @@ export * from './smart-info.repository.mock'; export * from './storage.repository.mock'; export * from './system-config.repository.mock'; export * from './system-info.repository.mock'; +export * from './system-metadata.repository.mock'; export * from './tag.repository.mock'; export * from './user-token.repository.mock'; export * from './user.repository.mock'; diff --git a/server/test/repositories/system-metadata.repository.mock.ts b/server/test/repositories/system-metadata.repository.mock.ts new file mode 100644 index 0000000000..fc4207da6f --- /dev/null +++ b/server/test/repositories/system-metadata.repository.mock.ts @@ -0,0 +1,8 @@ +import { ISystemMetadataRepository } from '@app/domain'; + +export const newSystemMetadataRepositoryMock = (): jest.Mocked => { + return { + get: jest.fn(), + set: jest.fn(), + }; +}; diff --git a/web/src/api/open-api/api.ts b/web/src/api/open-api/api.ts index 089b69c474..5d93a32b57 100644 --- a/web/src/api/open-api/api.ts +++ b/web/src/api/open-api/api.ts @@ -3019,6 +3019,12 @@ export interface ServerConfigDto { * @memberof ServerConfigDto */ 'isInitialized': boolean; + /** + * + * @type {boolean} + * @memberof ServerConfigDto + */ + 'isOnboarded': boolean; /** * * @type {string} @@ -15142,6 +15148,44 @@ export const ServerInfoApiAxiosParamCreator = function (configuration?: Configur + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + setAdminOnboarding: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/server-info/admin-onboarding`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication cookie required + + // authentication api_key required + await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration) + + // authentication bearer required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; @@ -15233,6 +15277,15 @@ export const ServerInfoApiFp = function(configuration?: Configuration) { const localVarAxiosArgs = await localVarAxiosParamCreator.pingServer(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async setAdminOnboarding(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.setAdminOnboarding(options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, } }; @@ -15307,6 +15360,14 @@ export const ServerInfoApiFactory = function (configuration?: Configuration, bas pingServer(options?: AxiosRequestConfig): AxiosPromise { return localVarFp.pingServer(options).then((request) => request(axios, basePath)); }, + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + setAdminOnboarding(options?: AxiosRequestConfig): AxiosPromise { + return localVarFp.setAdminOnboarding(options).then((request) => request(axios, basePath)); + }, }; }; @@ -15396,6 +15457,16 @@ export class ServerInfoApi extends BaseAPI { public pingServer(options?: AxiosRequestConfig) { return ServerInfoApiFp(this.configuration).pingServer(options).then((request) => request(this.axios, this.basePath)); } + + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ServerInfoApi + */ + public setAdminOnboarding(options?: AxiosRequestConfig) { + return ServerInfoApiFp(this.configuration).setAdminOnboarding(options).then((request) => request(this.axios, this.basePath)); + } } diff --git a/web/src/lib/assets/settings-outline.svg b/web/src/lib/assets/settings-outline.svg new file mode 100644 index 0000000000..7253589414 --- /dev/null +++ b/web/src/lib/assets/settings-outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/lib/components/admin-page/settings/setting-switch.svelte b/web/src/lib/components/admin-page/settings/setting-switch.svelte index 302e1a88c8..6797423a55 100644 --- a/web/src/lib/components/admin-page/settings/setting-switch.svelte +++ b/web/src/lib/components/admin-page/settings/setting-switch.svelte @@ -16,7 +16,7 @@
-